Compare commits

...

178 Commits

Author SHA1 Message Date
John Gross
814a9def7f Update readme 2019-01-21 16:13:14 -08:00
John Gross
276a4522d6 Disable building Torch client 2019-01-17 10:22:09 -08:00
Brant Martin
8b7a07ffe7 Deprecate ReservedPlayers in Torch cfg
Add a scrollpanel to fix clipped controls
2019-01-10 21:35:48 -05:00
Brant Martin
f990d27851 Enable creation of new worlds in Torch 2019-01-10 19:11:19 -05:00
N1Ran
3d8bf78213 Add Reserved slot and "Auto Detect Dependencies" to config ui plus Torch reserve management oversees all (#269)
* Add server description to config uI and adjusted the mod blocks to fit

* Added checkbox for "Auto detect dependencies" and Reserved players slot.

* Torch reserved slot management is now main and will scan sededi also to match steamid making new config ui addition usable
2019-01-10 09:28:35 -05:00
N1Ran
90479dfea2 Add server description to config uI and adjusted the mod blocks to fit (#268) 2019-01-08 17:30:42 -05:00
Brant Martin
844d4be96a Rearrange client join auth again 2019-01-06 21:21:36 -05:00
John Gross
9f610d5ae8 Merge pull request #266 from sirhamsteralot/master
Add extra functionality to the command system to make dynamic commands remotely plausible
2019-01-01 22:43:18 -08:00
sirhamsteralot
c9adb2a212 add a way to delete nodes from the tree 2018-12-28 02:11:24 +01:00
sirhamsteralot
7fd814d595 fix arrayoutofboundsthing 2018-12-27 23:40:56 +01:00
sirhamsteralot
e7065a7159 forgot to actually fill the parameters 2018-12-27 23:27:47 +01:00
sirhamsteralot
8625db7ae4 fixed nullref crash when invoking 2018-12-27 23:21:29 +01:00
sirhamsteralot
68e6774e26 performed an Exterminatus on code that doesnt work with the new Delegate system. 2018-12-27 23:13:54 +01:00
sirhamsteralot
a7c6ae7382 Invoke the Action directly 2018-12-27 22:57:27 +01:00
sirhamsteralot
9c06049628 Add extra command constructor for easier use of dynamic methods. 2018-12-27 00:57:58 +01:00
Brant Martin
34e5f4df49 Fix #247 in the worst possible way 2018-12-26 13:10:07 -05:00
Brant Martin
b7b58f5870 Add View Distance to torch confix. Resolves #264 2018-12-26 11:52:52 -05:00
Brant Martin
f9d75856d1 Fix some crashes in mod communication 2018-12-26 10:52:54 -05:00
Brant Martin
66b7adf485 Fix mod communication (probably) 2018-12-24 18:02:43 -05:00
Brant Martin
76637b130c Add ban and kick events 2018-12-16 20:53:52 -05:00
Brant Martin
9a1a31c424 Set thread culture to en-US for English exceptions 2018-12-15 20:18:43 -05:00
Brant Martin
faef000245 Fix Family Share banning. 2018-12-15 20:16:44 -05:00
Westin Miller
c2035668cd Async logging 2018-11-04 06:33:14 -08:00
Westin Miller
f1201c6259 Don't add null TryCatchOperations 2018-10-23 14:22:11 -07:00
Brant Martin
bdaa674662 Merge branch 'master' of https://github.com/TorchAPI/Torch 2018-10-23 16:30:15 -04:00
Brant Martin
3b17eb4750 Fix for 188 2018-10-23 16:23:52 -04:00
Westin Miller
9221d412ca Merge branch 'patcher-trycatch' 2018-10-18 18:00:36 -07:00
Brant Martin
65bb71aabf Add reserved slots 2018-10-12 16:12:33 -04:00
Westin Miller
dcd0fa86b9 Add debug symbols to release 2018-10-10 14:25:02 -07:00
Brant Martin
72be1b8dbf Merge branch 'master' of https://github.com/TorchAPI/Torch 2018-10-08 18:00:55 -04:00
Brant Martin
1d7b642c50 Tweak network reflection. Should fix #259 2018-10-08 18:00:40 -04:00
Westin Miller
f7d45ca338 Add more advanced debugging to patcher
Support multiple try-catch operations on a single instruction
Replace long branch with short branch instructions where possible
Dump generated IL at three stages to a file.
2018-10-07 00:44:36 -07:00
John Gross
831722dd84 Add remote API support 2018-09-28 21:48:24 -07:00
Brant Martin
ee3dd0b5bd Fix network injection (Keen broke it) 2018-09-27 10:28:18 -04:00
Brant Martin
7480847677 Add game version to !ver command 2018-09-18 10:42:46 -04:00
Brant Martin
0ff715af1b Remove server password when password field is blank. 2018-08-30 10:07:02 -04:00
a318aa87cf get remote ip fix (#254)
* get remote ip fix

* add GetIp fix to chat command
2018-08-27 22:40:30 -04:00
Brant Martin
74b00d3ab1 Add server password to config UI 2018-08-23 06:38:20 -04:00
Brant Martin
355375e9db Add OnlineMode to session settings. Only allow admins to join an Offline game. 2018-08-20 20:06:32 -04:00
John Gross
016203d2bc Merge pull request #251 from TorchAPI/plugin-asm-resolve
Fix assembly resolving.
2018-08-15 19:10:26 -07:00
Westin Miller
b65efa2968 Fixed error message 2018-08-15 19:07:27 -07:00
Westin Miller
4fc5f10bad Ignore resource assemblies 2018-08-14 16:33:02 -07:00
Westin Miller
f8e9d68ceb Don't unregister plugin assembly resolver. 2018-08-13 19:33:38 -07:00
Brant Martin
65e8d62391 Copy Keen's minidump on crash system. 2018-08-07 08:18:31 -04:00
Westin Miller
93fa82201a Merge pull request #242 from TorchAPI/eq-patcher-locals
Allow prefixes and suffixes to declare shared locals
2018-07-27 07:35:37 -07:00
Westin Miller
71930182dd Allow prefixes and suffixes to declare shared locals 2018-07-27 07:15:55 -07:00
Brant Martin
8764540d3b Set TorchUI window title with InstanceName setting (in torch.cfg) 2018-07-26 23:24:40 -04:00
Brant Martin
6a6676c1cb Revert "Collection editor improvements. Allows reordering of elements, and actually updates the list when content changes."
This reverts commit 14e16a959f.
2018-07-24 21:17:08 -04:00
John Gross
5bf91f1891 Improve UI layout, add flags enum editor to PropertyGrid 2018-07-24 12:51:13 -07:00
Brant Martin
14e16a959f Collection editor improvements. Allows reordering of elements, and actually updates the list when content changes. 2018-07-23 21:50:42 -04:00
John Gross
3b72724966 Update session settings 2018-07-22 10:14:06 -07:00
Brant Martin
9a0e7809cd Fix issue with TorchMod being added to the server config. (also removes TorchMod from any extant config to avoid Problems) 2018-07-20 10:05:08 -04:00
Brant Martin
94c25a70b3 Fix mods not showing up in UI 2018-07-19 21:08:41 -04:00
John Gross
4901120be4 Fix online player list reflection 2018-07-19 14:32:28 -07:00
John Gross
21e45b5e45 Hotfix for assembly loading issues 2018-07-19 11:57:30 -07:00
John Gross
b829e90edb Merge pull request #238 from TorchAPI/experiment
MP Update
2018-07-19 10:18:49 -07:00
Brant Martin
fbf7fa6176 Merge branch 'experiment' of https://github.com/TorchAPI/Torch into experiment 2018-07-18 22:35:23 -04:00
Brant Martin
2b413ef609 Fix mod settings 2018-07-18 22:35:11 -04:00
Brant Martin
0f06ee5688 Disable client IP stuff because it borked 2018-07-18 22:34:59 -04:00
Brant Martin
ec065ec329 Fix network intercept 2018-07-18 22:34:44 -04:00
Brant Martin
c9a5472282 Merge branch 'master' into experiment 2018-07-18 20:24:15 -04:00
Brant Martin
b12199c65b Revert "Let Jenkins download the new branch so the build doesn't blow up"
This reverts commit 4c34a653bd.
2018-07-18 19:38:04 -04:00
Brant Martin
b4ac097910 Revert "SteamCMD is trash"
This reverts commit ebc8b7a7fd.
2018-07-18 19:35:23 -04:00
Brant Martin
aae4ec97a9 Revert "STEAM PLEASE"
This reverts commit 06eca83ff9.
2018-07-18 19:35:22 -04:00
Brant Martin
45d931b351 Revert "I don't even know anymore"
This reverts commit 63f504feb7.
2018-07-18 19:35:20 -04:00
Brant Martin
f53c9660fe Revert "TRY THINGS UNTIL IT WORKS"
This reverts commit b89b61496b.
2018-07-18 19:35:18 -04:00
Brant Martin
c889854818 Minor fixes to client mod 2018-07-18 19:25:08 -04:00
Brant Martin
b8b0a0fcce Make PropertyGrid controls immune from scrollbar wrapping (caused by #218) 2018-07-08 18:25:06 -04:00
Brant Martin
b3f9d7e5c7 Make description box in property grids not clip 2018-07-08 17:46:53 -04:00
Brant Martin
94b457d9c0 Merge pull request #236 from TorchAPI/client-mod
Client mod
2018-07-06 15:30:39 -04:00
Brant Martin
0c58655708 Merge branch 'master' into client-mod 2018-07-06 15:28:15 -04:00
Brant Martin
ba98e0a15a Stop Equinox complaining about session download patch not being static 2018-07-03 18:34:09 -04:00
Brant Martin
5496ad1198 Make get-only collections editable in propertygrid 2018-07-03 14:47:36 -04:00
Brant Martin
7bad6149b5 Refactor ObjectCollectionEditor so it's embeddable in a propertygrid.
Make the listbox actually update when items are added/removed.
2018-07-03 14:47:19 -04:00
Westin Miller
378905268d Update Jenkinsfile
This time remove the correct one.
2018-07-02 08:40:09 -07:00
Westin Miller
f56a700fea Update Jenkinsfile
Remove the client build package.  It was broken anyways.
2018-07-02 08:38:21 -07:00
Brant Martin
736176ce27 Fix custom control type in property grids 2018-07-02 11:27:43 -04:00
John Gross
96d749c512 Merge pull request #234 from SinZ163/fix-231
Disable "Import World Config" button when there are no worlds available
2018-07-01 09:33:14 -07:00
Trent Monahan
17514c89ad Disable ImportWorldConfig when there are no worlds to import 2018-07-01 15:46:07 +10:00
Brant Martin
8b08f2b747 Enable multiline editing of string values in propertygrids 2018-06-23 03:21:45 -04:00
Brant Martin
07bb0fc4cf Merge remote-tracking branch 'origin/master' into client-mod 2018-06-14 18:04:33 -04:00
Brant Martin
dbea9d83f4 Fix crash on exit 2018-06-14 14:15:51 -04:00
Brant Martin
8989ae94a7 Debug output for plugin loading 2018-06-14 11:08:09 -04:00
Brant Martin
4db83e6f65 Add voxel reset message 2018-06-11 07:57:12 -04:00
Westin Miller
9286f2e559 Patch persistent (#232)
Makes it save properly.  Untested push to master :shipit:
2018-06-10 21:46:00 -07:00
Brant Martin
045a572058 Add Torch client mod. Currently supports dialogs and notifications.
Add dialog output to !longhelp
Add !notify command
Silently inserts mod into session when client connects, server admins don't need to do anything to enable it.
2018-06-10 07:31:19 -04:00
Brant Martin
f68be8e4c9 last try 2018-05-16 07:55:51 -04:00
Brant Martin
ec4572c390 VS WHY 2018-05-16 07:49:58 -04:00
Brant Martin
b89b61496b TRY THINGS UNTIL IT WORKS 2018-05-16 07:29:22 -04:00
Brant Martin
63f504feb7 I don't even know anymore 2018-05-16 07:24:33 -04:00
Brant Martin
06eca83ff9 STEAM PLEASE 2018-05-16 07:18:16 -04:00
Brant Martin
ebc8b7a7fd SteamCMD is trash 2018-05-16 07:16:33 -04:00
Brant Martin
4c34a653bd Let Jenkins download the new branch so the build doesn't blow up 2018-05-16 07:14:10 -04:00
Brant Martin
38d2f1b62e Fix for MP 2018-05-16 07:09:05 -04:00
Westin Miller
d8915d1893 Update README.md
Proper download links.
2018-05-13 23:45:07 -07:00
John Gross
873acfcb4f Improve initializer logging 2018-04-29 18:17:17 -07:00
John Gross
030df5029b Merge pull request #229 from TorchAPI/propertygrid
Propertygrid
2018-04-28 10:33:25 -07:00
Brant Martin
7404b6bd2d Fix crash when trying to filter an empty PropertyGrid 2018-04-27 10:26:17 -04:00
Brant Martin
b9e9be227a Tweak DisplayAttribute, don't disable text labels, PropertyGrid should be feature complete now. 2018-04-27 10:25:57 -04:00
Brant Martin
f2537706e7 Allow properties in PropertyGrid to set Enabled, Visible, ReadOnly, override sort order. 2018-04-26 08:20:37 -04:00
John Gross
a8251d9385 Update TorchCommands.cs 2018-04-13 12:09:14 -07:00
John Gross
b1edd62c0b Disallow restart if one is already pending 2018-04-13 12:07:21 -07:00
John Gross
c1e315fa40 Merge pull request #227 from sirhamsteralot/master
Fixed [Theme Bug] #226 / Fixed Crash when plugin has no UI
2018-04-13 12:02:26 -07:00
sirhamsteralot
bfcf96f1ad Disable typing in Log 2018-04-13 20:53:56 +02:00
sirhamsteralot
d257e9e1e8 Fixed crash when plugin doesnt use UI 2018-04-13 20:45:41 +02:00
John Gross
85e307f8db Fix theming for plugins 2018-04-12 18:04:15 -07:00
sirhamsteralot
03fa0a73b6 Changed back to referencing the proper package whoops? 2018-04-12 09:54:34 -07:00
sirhamsteralot
2f157a6438 XML comments 2018-04-12 09:54:34 -07:00
sirhamsteralot
cdde72cbe0 Comments 2018-04-12 09:54:33 -07:00
sirhamsteralot
b18420ad55 Themes apply everywhere now 2018-04-12 09:54:33 -07:00
sirhamsteralot
d92daccdbf I fixed the issue 2018-04-12 09:54:33 -07:00
sirhamsteralot
6b3cc6c421 Save on select 2018-04-12 09:54:32 -07:00
sirhamsteralot
18af85c4d7 Most of these arnt even changes 2018-04-12 09:54:32 -07:00
sirhamsteralot
6c6ff18ec3 Added more themes and serialization of the latest theme 2018-04-12 09:54:32 -07:00
sirhamsteralot
67e663f023 Added Dark themes 2018-04-12 09:54:31 -07:00
sirhamsteralot
3f717b304a Switching Themes is working, just have to create/find a proper theme now 2018-04-12 09:54:31 -07:00
SirHamsterAlot
300af03012 ColoringAttempts 2018-04-12 09:54:30 -07:00
SirHamsterAlot
3d8d333f10 Added Nlog reference 2018-04-12 09:54:30 -07:00
John Gross
d87cc7f1e7 Truncate log in UI, add ability to cancel restart 2018-04-12 09:51:52 -07:00
John Gross
d59ef20f72 Fix config not saving session settings 2018-04-03 11:17:19 -07:00
John Gross
b2bf0229ed Revert FirstOrDefault change 2018-04-03 00:05:32 -07:00
John Gross
f03bfd2d7a Fix suppressed fatal error, make world config importing manual 2018-04-02 21:00:36 -07:00
John Gross
3dd646d6e9 Merge pull request #219 from TorchAPI/GenericUI
Add entity id to grid views
2018-03-21 06:49:59 +01:00
Brant Martin
58ad553b39 Fix data binding direction 2018-03-20 22:47:33 -07:00
Brant Martin
83056bacf4 Add entity id to grid views 2018-03-20 22:47:33 -07:00
John Gross
2751eaf399 Merge pull request #218 from Phoenix84/ScrollViewer
Wrap the plugin controls around a ScrollViewer
2018-03-12 04:39:40 +01:00
Daniel Osborne
869ba0d33c Wrap the plugin controls around a ScrollViewer
So plugin authors don't have to worry about controls drawing out of the
window, wrap the plugin in a ScrollViewer, which will automatically add
a scroll bar if needed.
2018-03-11 20:29:25 -07:00
Westin Miller
aeb29d9a69 Also load symbols from zip files 2018-03-11 07:45:03 -07:00
Westin Miller
979d5914a9 Always try and include debug symbols during plugin load. 2018-03-11 07:39:36 -07:00
John Gross
e72f5b7c37 Fix chat logging and !longhelp permission 2018-02-27 19:19:50 -08:00
John Gross
42d3324fc1 Disable tests on build server 2018-02-21 17:34:06 -08:00
John Gross
e242ed6f1f Disable new world button, fix crash opening plugin folder 2018-02-21 17:19:33 -08:00
Westin Miller
a2acb9c11c Merge pull request #208 from TorchAPI/network-improvement
Tweak client join validation
2018-02-18 22:10:49 -08:00
Brant Martin
444da941c9 Tweak client join validation 2018-02-15 11:14:57 -05:00
Westin Miller
f19fd84f1d Fix empty group letting people in over cap 2018-02-12 14:41:50 -08:00
John Gross
b5793d36a8 Merge pull request #207 from TorchAPI/GenericUI
UI improvements
2018-02-11 20:51:57 -08:00
Brant Martin
a71c03124b Make ObjectCollectionEditor resizable 2018-02-11 23:47:37 -05:00
Brant Martin
66c484796d how about you check in your new files you idiot 2018-02-11 23:24:17 -05:00
Brant Martin
a4927030d7 Let's not require people to press a button to edit primitives 2018-02-11 23:22:30 -05:00
Brant Martin
c32badb750 Add support for Category, Name, and Description to PropertyGrid using DisplayAttribute 2018-02-11 22:43:22 -05:00
John Gross
356eb849f2 Merge pull request #205 from TorchAPI/GenericUI
Add ObjectCollectionEditor
2018-02-09 12:55:26 -08:00
Brant Martin
04e949ed0c Merge branch 'GenericUI' of https://github.com/TorchAPI/Torch into GenericUI 2018-02-09 15:52:27 -05:00
Brant Martin
13dc8622c9 Add ObjectCollectionEditor which allows PropertyGrids to edit any collection of arbitrary objects using yet another PropertyGrid 2018-02-09 15:52:13 -05:00
John Gross
a2b9c4724d Remove text template broken by VRage.Game arch change 2018-02-09 09:18:09 -08:00
Brant Martin
f326e569a1 Add ObjectCollectionEditor which allows PropertyGrids to edit any collection of arbitrary objects using yet another PropertyGrid 2018-02-08 23:34:44 -05:00
John Gross
c1961dee5f Update checkpoint view model 2018-02-02 10:59:21 -08:00
John Gross
e42a231553 Add sane whitelist, fix bad server init logging 2018-01-30 23:40:38 -08:00
John Gross
b3d9a64632 Add validation for world load settings 2018-01-27 09:19:03 -08:00
John Gross
47c7c37fa9 Merge pull request #203 from TorchAPI/character-vm
Character view model and fixed character display
2018-01-26 21:52:45 -08:00
Westin Miller
17413f81ff Character view model and fixed character display 2018-01-26 21:48:46 -08:00
John Gross
725e555733 Merge pull request #201 from TorchAPI/ui-improvements
UI Improvements
2018-01-25 19:10:16 -08:00
John Gross
6e7456605d Allow custom handling of chat command response 2018-01-25 18:34:21 -08:00
John Gross
b652181dda Remove timeouts when waiting for game state changes 2018-01-25 18:34:21 -08:00
John Gross
6764d80534 Enable text wrapping in chat log 2018-01-25 18:34:21 -08:00
John Gross
6fbc06081e Add block limit editor, various UI tweaks 2018-01-25 18:34:21 -08:00
John Gross
0328876d50 More async init, add proper NLog target for WPF and free console in UI mode 2018-01-25 18:34:16 -08:00
John Gross
c5e1dd7c3a Oops, don't init the server twice 2018-01-25 18:33:23 -08:00
John Gross
714824df97 Fix start/stop buttons, improve DS init order, add console tab 2018-01-25 18:33:08 -08:00
John Gross
2cb921087f Remove lazy loading of plugin WPF control 2018-01-25 18:30:23 -08:00
John Gross
1ed3144428 Fix/disable broken tests 2018-01-25 18:30:23 -08:00
John Gross
ba8fa01ce5 Auto-generate configuration dialog, fix logger names, prepare for async initialization 2018-01-25 18:30:23 -08:00
John Gross
3f6f077833 Fix reference path 2018-01-25 18:30:23 -08:00
John Gross
74d9999202 Add informational version type 2018-01-25 18:30:23 -08:00
John Gross
1be1c938cc View model improvements, load world checkpoints for more config options 2018-01-25 18:30:23 -08:00
John Gross
930f1d43e0 Merge pull request #200 from TorchAPI/load-order
Load order
2018-01-25 18:24:06 -08:00
Westin Miller
1e04053026 Let you invoke async on the game thread, just not async blocking.
Fixed event list
2018-01-25 18:19:07 -08:00
Westin Miller
1e6b3faff8 Changed plugins so they get Init-d before the sandbox game is created.
Changed NLog to dump Keen errors to Torch log too
Added some debug info
2018-01-24 17:24:08 -08:00
Westin Miller
e6928b6ab1 Throw error when given the wrong type name 2018-01-15 15:25:53 -08:00
Westin Miller
eb97d0d479 Fixed junction link prompt 2018-01-07 21:57:32 -08:00
Westin Miller
0a75d57cf9 Make async saving as singleton
Don't need an extra Task for save task with no timeout.
2018-01-05 22:08:08 -08:00
Westin Miller
383c9b9a33 Client should work again
™️
2017-12-10 02:46:21 -08:00
Westin Miller
d2adbecc44 Typos 2017-12-10 02:32:59 -08:00
Westin Miller
834395bdc3 Admins can join full servers
Sessions get cleaned up properly
2017-12-05 00:42:03 -08:00
Westin Miller
18dad5bedf Add block count to groups 2017-12-05 00:26:31 -08:00
Westin Miller
c188367749 Observable collection base code for those without a true backing collection.
Observable sorted dictionary
Grid view now displays blocks grouped by subtype.
Null propagation in entity view models because WPF.
2017-12-04 23:52:03 -08:00
Westin Miller
5b098c68aa Session reload stuff
Better async invoke calls for the game thread
2017-12-03 22:27:30 -08:00
Westin Miller
22bd56652d Always force a full reset, to reduce the number of undesired resets. 2017-12-03 22:05:05 -08:00
Westin Miller
d07caea0f6 New saving system with proper waiting for file flush
Command context for servers now supports Respond()
Chat manager now treats the steam ID Sync.MyId as a local destination, and processes the event accordingly.
Save makes better use of Task<>
Restart actually waits for save
PlayerCollectionExtension uses a dictionary lookup for TryGetBySteamId
Shutting the UI window properly closes Torch
Torch Dispose renamed to Destroy, VRage Dispose marked as obsolete (do not use)
2017-12-02 21:19:09 -08:00
167 changed files with 8291 additions and 2485 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,9 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
#Rider directory
.idea/
# User-specific files
*.suo
*.user

7
Jenkinsfile vendored
View File

@@ -9,9 +9,6 @@ def packageAndArchive(buildMode, packageName, exclude) {
if (exclude.length() > 0) {
bat "del ${packageDir}${exclude}"
}
if (buildMode == "Release") {
bat "del ${packageDir}*.pdb"
}
powershell "Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\"\$PWD\\${packageDir}\", \"\$PWD\\${zipFile}\")"
archiveArtifacts artifacts: zipFile, caseSensitive: false, onlyIfSuccessful: true
}
@@ -50,9 +47,10 @@ node {
packageAndArchive(buildMode, "torch-server", "Torch.Client*")
packageAndArchive(buildMode, "torch-client", "Torch.Server*")
/*packageAndArchive(buildMode, "torch-client", "Torch.Server*")*/
}
/* Disabled because they fail builds more often than they detect actual problems
stage('Test') {
bat 'IF NOT EXIST reports MKDIR reports'
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/${buildMode}/Torch.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Server.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
@@ -71,4 +69,5 @@ node {
]]
])
}
*/
}

View File

@@ -4,21 +4,23 @@
<variable name="logStamp" value="${time} ${pad:padding=-8:inner=[${level:uppercase=true}]}" />
<variable name="logContent" value="${message:withException=true}"/>
<targets>
<target xsi:type="Null" name="null" formatMessage="false" />
<targets async="true">
<target xsi:type="Null" name="null" formatMessage="false" />
<target xsi:type="File" name="keen" layout="${var:logStamp} ${logger}: ${var:logContent}" fileName="Logs\Keen-${shortdate}.log" />
<target xsi:type="File" name="main" layout="${var:logStamp} ${logger}: ${var:logContent}" fileName="Logs\Torch-${shortdate}.log" />
<target xsi:type="File" name="chat" layout="${longdate} ${message}" fileName="Logs\Chat.log" />
<target xsi:type="ColoredConsole" name="console" layout="${var:logStamp} ${logger}: ${var:logContent}" />
<target xsi:type="ColoredConsole" name="console" layout="${var:logStamp} ${logger:shortName=true}: ${var:logContent}" />
<target xsi:type="File" name="patch" layout="${var:logContent}" fileName="Logs\patch.log"/>
<target xsi:type="FlowDocument" name="wpf" layout="${var:logStamp} ${logger:shortName=true}: ${var:logContent}" />
</targets>
<rules>
<logger name="Keen" minlevel="Info" writeTo="console"/>
<logger name="Keen" minlevel="Warn" writeTo="main"/>
<logger name="Keen" minlevel="Info" writeTo="console, wpf"/>
<logger name="Keen" minlevel="Debug" writeTo="keen" final="true" />
<logger name="Keen" writeTo="null" final="true" />
<logger name="*" minlevel="Info" writeTo="main, console" />
<logger name="*" minlevel="Info" writeTo="main, console, wpf" />
<logger name="Chat" minlevel="Info" writeTo="chat" />
<!--<logger name="Torch.Managers.PatchManager.*" minlevel="Trace" writeTo="patch"/>-->
</rules>

View File

@@ -1,4 +1,4 @@
[![Discord](https://discordapp.com/api/guilds/230191591640268800/widget.png)](https://discord.gg/8uHZykr) [![Build Status](http://server.torchapi.net:8080/job/Torch/job/Torch/job/master/badge/icon)](http://server.torchapi.net:8080/job/Torch/job/Torch/job/master/)
[![Discord](https://discordapp.com/api/guilds/230191591640268800/widget.png)](https://discord.gg/8uHZykr) [![Build Status](http://build.torchapi.net/job/Torch/job/Torch/job/master/badge/icon)](http://build.torchapi.net/job/Torch/job/Torch/job/master/)
# What is Torch?
Torch is the successor to SE Server Extender and gives server admins the tools they need to keep their Space Engineers servers running smoothly. It features a user interface with live management tools and a plugin system so you can run your server exactly how you'd like. Torch is still in early development so there may be bugs and incomplete features.
@@ -14,22 +14,14 @@ Torch is the successor to SE Server Extender and gives server admins the tools t
### Installation
* Get the latest Torch release here: https://github.com/TorchAPI/Torch/releases
* Get the latest Torch release here: https://torchapi.net/download
* Unzip the Torch release into its own directory and run the executable. It will automatically download the SE DS and generate the other necessary files.
- If you already have a DS installed you can unzip the Torch files into the folder that contains the DedicatedServer64 folder.
## Torch.Client
* An optional client-side version of Torch. More documentation to come.
# Building
To build Torch you must first have a complete SE Dedicated installation somewhere. Before you open the solution, run the Setup batch file and enter the path of that installation's DedicatedServer64 folder. The script will make a symlink to that folder so the Torch solution can find the DLL references it needs.
In both cases you will need to set the InstancePath in TorchConfig.xml to an existing dedicated server instance as Torch can't fully generate it on its own yet.
# Official Plugins
Install plugins by unzipping them into the 'Plugins' folder which should be in the same location as the Torch files. If it doesn't exist you can simply create it.
* [Essentials](https://github.com/TorchAPI/Essentials): Adds a slew of chat commands and other tools to help manage your server.
* [Concealment](https://github.com/TorchAPI/Concealment): Adds game logic and physics optimizations that significantly improve sim speed.
If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon.
[![Patreon](http://i.imgur.com/VzzIMgn.png)](https://www.patreon.com/bePatron?u=847269)!

View File

@@ -18,21 +18,25 @@ namespace Torch.API
/// <summary>
/// Fired when the session begins loading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionLoading;
/// <summary>
/// Fired when the session finishes loading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionLoaded;
/// <summary>
/// Fires when the session begins unloading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionUnloading;
/// <summary>
/// Fired when the session finishes unloading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionUnloaded;
/// <summary>
@@ -61,7 +65,7 @@ namespace Torch.API
/// <summary>
/// The binary version of the current instance.
/// </summary>
Version TorchVersion { get; }
InformationalVersion TorchVersion { get; }
/// <summary>
/// Invoke an action on the game thread.
@@ -70,15 +74,23 @@ namespace Torch.API
/// <summary>
/// Invoke an action on the game thread and block until it has completed.
/// If this is called on the game thread the action will execute immediately.
/// </summary>
void InvokeBlocking(Action action, [CallerMemberName] string caller = "");
/// <param name="action">Action to execute</param>
/// <param name="caller">Caller of the invoke function</param>
/// <param name="timeoutMs">Timeout before <see cref="TimeoutException"/> is thrown, or -1 to never timeout</param>
/// <exception cref="TimeoutException">If the action times out</exception>
void InvokeBlocking(Action action, int timeoutMs = -1, [CallerMemberName] string caller = "");
/// <summary>
/// Invoke an action on the game thread asynchronously.
/// </summary>
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
/// <summary>
/// Invoke a function on the game thread asynchronously.
/// </summary>
Task<T> InvokeAsync<T>(Func<T> func, [CallerMemberName] string caller = "");
/// <summary>
/// Signals the torch instance to start, then blocks until it's started.
/// </summary>
@@ -90,15 +102,17 @@ namespace Torch.API
void Stop();
/// <summary>
/// Restart the Torch instance.
/// Restart the Torch instance, blocking until the restart has been performed.
/// </summary>
void Restart();
/// <summary>
/// Initializes a save of the game.
/// </summary>
/// <param name="callerId">Id of the player who initiated the save.</param>
Task Save(long callerId);
/// <param name="timeoutMs">timeout before the save is treated as failed, or -1 for no timeout</param>
/// <param name="exclusive">Only start saving if we aren't already saving</param>
/// <returns>Future result of the save, or null if one is in progress and in exclusive mode</returns>
Task<GameSaveResult> Save(int timeoutMs = -1, bool exclusive = false);
/// <summary>
/// Initialize the Torch instance. Before this <see cref="Start"/> is invalid.
@@ -108,7 +122,7 @@ namespace Torch.API
/// <summary>
/// Disposes the Torch instance. After this <see cref="Start"/> is invalid.
/// </summary>
void Dispose();
void Destroy();
/// <summary>
/// The current state of the game this instance of torch is controlling.
@@ -135,6 +149,11 @@ namespace Torch.API
/// Path of the dedicated instance folder.
/// </summary>
string InstancePath { get; }
/// <summary>
/// Raised when the server's Init() method has completed.
/// </summary>
event Action<ITorchServer> Initialized;
}
/// <summary>

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API
{
/// <summary>
/// Version in the form v#.#.#.#-info
/// </summary>
public class InformationalVersion
{
public Version Version { get; set; }
public string[] Information { get; set; }
public static bool TryParse(string input, out InformationalVersion version)
{
version = default(InformationalVersion);
var trim = input.TrimStart('v');
var info = trim.Split('-');
if (!Version.TryParse(info[0], out Version result))
return false;
version = new InformationalVersion { Version = result };
if (info.Length > 1)
version.Information = info.Skip(1).ToArray();
return true;
}
/// <inheritdoc />
public override string ToString()
{
if (Information == null || Information.Length == 0)
return $"v{Version}";
return $"v{Version}-{string.Join("-", Information)}";
}
public static explicit operator InformationalVersion(Version v)
{
return new InformationalVersion { Version = v };
}
public static implicit operator Version(InformationalVersion v)
{
return v.Version;
}
}
}

View File

@@ -32,5 +32,15 @@ namespace Torch.API.Managers
/// <param name="steamId">The SteamID of the player.</param>
/// <returns>True if the player is banned; otherwise false.</returns>
bool IsBanned(ulong steamId);
/// <summary>
/// Raised when a player is kicked. Passes with SteamID of kicked player.
/// </summary>
event Action<ulong> PlayerKicked;
/// <summary>
/// Raised when a player is banned or unbanned. Passes SteamID of player, and true if banned, false if unbanned.
/// </summary>
event Action<ulong, bool> PlayerBanned;
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Session
{
/// <summary>
/// The result of a save operation
/// </summary>
public enum GameSaveResult
{
/// <summary>
/// Successfully saved
/// </summary>
Success = 0,
/// <summary>
/// The game wasn't ready to be saved
/// </summary>
GameNotReady = -1,
/// <summary>
/// Failed to take the snapshot of the current world state
/// </summary>
FailedToTakeSnapshot = -2,
/// <summary>
/// Failed to save the snapshot to disk
/// </summary>
FailedToSaveToDisk = -3,
/// <summary>
/// An unknown error occurred
/// </summary>
UnknownError = -4,
/// <summary>
/// The save operation timed out
/// </summary>
TimedOut = -5
}
}

View File

@@ -74,11 +74,6 @@
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SteamSDK, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\SteamSDK.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
@@ -160,6 +155,7 @@
<Link>Properties\AssemblyVersion.cs</Link>
</Compile>
<Compile Include="ConnectionState.cs" />
<Compile Include="InformationalVersion.cs" />
<Compile Include="ITorchConfig.cs" />
<Compile Include="Managers\DependencyManagerExtensions.cs" />
<Compile Include="Managers\DependencyProviderExtensions.cs" />
@@ -187,6 +183,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerState.cs" />
<Compile Include="ModAPI\TorchAPI.cs" />
<Compile Include="Session\GameSaveResult.cs" />
<Compile Include="Session\ITorchSession.cs" />
<Compile Include="Session\ITorchSessionManager.cs" />
<Compile Include="Session\TorchSessionState.cs" />
@@ -198,6 +195,7 @@
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
</Project>

View File

@@ -90,6 +90,7 @@
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />

View File

@@ -27,6 +27,20 @@ namespace Torch.Client.Manager
/// <inheritdoc />
public bool IsBanned(ulong steamId) => false;
/// <inheritdoc />
public event Action<ulong> PlayerKicked
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
/// <inheritdoc />
public event Action<ulong, bool> PlayerBanned
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
/// <inheritdoc/>
public override void Attach()
{

View File

@@ -18,6 +18,7 @@ namespace Torch.Client
{
public const string SpaceEngineersBinaries = "Bin64";
private static string _spaceEngInstallAlias = null;
public static string SpaceEngineersInstallAlias
{
get
@@ -26,7 +27,8 @@ namespace Torch.Client
if (_spaceEngInstallAlias == null)
{
// ReSharper disable once AssignNullToNotNullAttribute
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "SpaceEngineersAlias");
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location),
"SpaceEngineersAlias");
}
return _spaceEngInstallAlias;
}
@@ -52,16 +54,20 @@ namespace Torch.Client
{
AllocConsole();
#endif
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// Early config: Resolve SE install directory.
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
SetupSpaceEngInstallAlias();
using (new TorchAssemblyResolver(Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries)))
if (!TorchLauncher.IsTorchWrapped())
{
RunClient();
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// Early config: Resolve SE install directory.
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
SetupSpaceEngInstallAlias();
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName, args,
Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries));
return;
}
RunClient();
#if DEBUG
}
finally
@@ -77,7 +83,8 @@ namespace Torch.Client
// TODO look at Steam/config/Config.VDF? Has alternate directories.
var steamDir =
Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Valve\\Steam", "SteamPath", null) as string;
Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Valve\\Steam", "SteamPath",
null) as string;
if (steamDir != null)
{
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
@@ -85,7 +92,10 @@ namespace Torch.Client
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
else
{
_log.Debug("Couldn't find Space Engineers in {0}", spaceEngineersDirectory);
spaceEngineersDirectory = null;
}
}
if (spaceEngineersDirectory == null)
{
@@ -97,7 +107,8 @@ namespace Torch.Client
{
if (dialog.ShowDialog() != DialogResult.OK)
{
var ex = new FileNotFoundException("Unable to find the Space Engineers install directory, aborting");
var ex = new FileNotFoundException(
"Unable to find the Space Engineers install directory, aborting");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
@@ -109,11 +120,12 @@ namespace Torch.Client
$"Unable to find {0} in {1}. Are you sure it's the Space Engineers install directory?",
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
break;
} while (true); // Repeat until they confirm.
} while (true); // Repeat until they confirm.
}
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
{
var ex = new IOException($"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
var ex = new IOException(
$"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
@@ -121,7 +133,8 @@ namespace Torch.Client
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
if (!File.Exists(junctionVerify))
{
var ex = new FileNotFoundException($"Junction link is not working. File {junctionVerify} does not exist");
var ex = new FileNotFoundException(
$"Junction link is not working. File {junctionVerify} does not exist");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
@@ -153,7 +166,7 @@ namespace Torch.Client
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
var ex = (Exception) e.ExceptionObject;
_log.Error(ex);
LogManager.Flush();
MessageBox.Show(ex.StackTrace, ex.Message);

View File

@@ -26,22 +26,19 @@ namespace Torch.Client
{
public class TorchClient : TorchBase, ITorchClient
{
private MyCommonProgramStartup _startup;
private IMyRender _renderer;
protected override uint SteamAppId => 244850;
protected override string SteamAppName => "Space Engineers";
protected override string SteamAppName => "SpaceEngineers";
public TorchClient()
{
Config = new TorchClientConfig();
var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerLobby
? new MultiplayerManagerLobby(this)
: null);
? new MultiplayerManagerLobby(this)
: null);
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerClientBase
? new MultiplayerManagerClient(this)
: null);
? new MultiplayerManagerClient(this)
: null);
}
public override void Init()
@@ -49,38 +46,22 @@ namespace Torch.Client
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
Log.Info("Initializing Torch Client");
_startup = new MyCommonProgramStartup(RunArgs);
SpaceEngineersGame.SetupBasicGameInfo();
SpaceEngineersGame.SetupPerGameSettings();
if (_startup.PerformReporting())
throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
_startup.PerformAutoconnect();
if (!_startup.CheckSingleInstance())
throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
var appDataPath = _startup.GetAppDataPath();
Config.InstancePath = appDataPath;
Config.InstancePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
SteamAppName);
base.Init();
OverrideMenus();
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
}
public override void Dispose()
{
base.Dispose();
_startup.DetectSharpDxLeaksAfterRun();
}
private void OverrideMenus()
{
var credits = new MyCreditsDepartment("Torch Developed By")
{
Persons = new List<MyCreditsPerson>
{
new MyCreditsPerson("THE TORCH TEAM"),
new MyCreditsPerson("http://github.com/TorchSE"),
}
{
new MyCreditsPerson("THE TORCH TEAM"),
new MyCreditsPerson("http://github.com/TorchSE"),
}
};
MyPerGameSettings.Credits.Departments.Insert(0, credits);
@@ -96,12 +77,11 @@ namespace Torch.Client
BindingFlags.Instance | BindingFlags.NonPublic);
if (renderWindowField == null)
return;
var window = renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as System.Windows.Forms.Form;
var window =
renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as
System.Windows.Forms.Form;
if (window != null)
renderThread.Invoke(() =>
{
window.Text = title;
});
renderThread.Invoke(() => { window.Text = title; });
}
public override void Restart()

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
using Sandbox.ModAPI;
namespace Torch.Mod.Messages
{
/// Dialogs are structured as follows
///
/// _____________________________________
/// | Title |
/// --------------------------------------
/// | Prefix Subtitle |
/// --------------------------------------
/// | ________________________________ |
/// | | Content | |
/// | --------------------------------- |
/// | ____________ |
/// | | ButtonText | |
/// | -------------- |
/// --------------------------------------
///
/// Button has a callback on click option,
/// but can't serialize that, so ¯\_(ツ)_/¯
[ProtoContract]
public class DialogMessage : MessageBase
{
[ProtoMember(201)]
public string Title;
[ProtoMember(202)]
public string Subtitle;
[ProtoMember(203)]
public string Prefix;
[ProtoMember(204)]
public string Content;
[ProtoMember(205)]
public string ButtonText;
public DialogMessage()
{ }
public DialogMessage(string title, string subtitle, string content)
{
Title = title;
Subtitle = subtitle;
Content = content;
Prefix = String.Empty;
}
public DialogMessage(string title = null, string prefix = null, string subtitle = null, string content = null, string buttonText = null)
{
Title = title;
Subtitle = subtitle;
Prefix = prefix ?? String.Empty;
Content = content;
ButtonText = buttonText;
}
public override void ProcessClient()
{
MyAPIGateway.Utilities.ShowMissionScreen(Title, Prefix, Subtitle, Content, null, ButtonText);
}
public override void ProcessServer()
{
throw new Exception();
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Torch.Mod.Messages
{
/// <summary>
/// shim to store incoming message data
/// </summary>
internal class IncomingMessage : MessageBase
{
public IncomingMessage()
{
}
public override void ProcessClient()
{
throw new Exception();
}
public override void ProcessServer()
{
throw new Exception();
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ProtoBuf;
namespace Torch.Mod.Messages
{
#region Includes
[ProtoInclude(1, typeof(DialogMessage))]
[ProtoInclude(2, typeof(NotificationMessage))]
[ProtoInclude(3, typeof(VoxelResetMessage))]
#endregion
[ProtoContract]
public abstract class MessageBase
{
[ProtoMember(101)]
public ulong SenderId;
public abstract void ProcessClient();
public abstract void ProcessServer();
//members below not serialized, they're just metadata about the intended target(s) of this message
internal MessageTarget TargetType;
internal ulong Target;
internal ulong[] Ignore;
internal byte[] CompressedData;
}
public enum MessageTarget
{
/// <summary>
/// Send to Target
/// </summary>
Single,
/// <summary>
/// Send to Server
/// </summary>
Server,
/// <summary>
/// Send to all Clients (only valid from server)
/// </summary>
AllClients,
/// <summary>
/// Send to all except those steam ID listed in Ignore
/// </summary>
AllExcept,
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using ProtoBuf;
using Sandbox.ModAPI;
namespace Torch.Mod.Messages
{
[ProtoContract]
public class NotificationMessage : MessageBase
{
[ProtoMember(201)]
public string Message;
[ProtoMember(202)]
public string Font;
[ProtoMember(203)]
public int DisappearTimeMs;
public NotificationMessage()
{ }
public NotificationMessage(string message, int disappearTimeMs, string font)
{
Message = message;
DisappearTimeMs = disappearTimeMs;
Font = font;
}
public override void ProcessClient()
{
MyAPIGateway.Utilities.ShowNotification(Message, DisappearTimeMs, Font);
}
public override void ProcessServer()
{
throw new Exception();
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Text;
using ProtoBuf;
using Sandbox.ModAPI;
using VRage.ModAPI;
using VRage.Voxels;
namespace Torch.Mod.Messages
{
[ProtoContract]
public class VoxelResetMessage : MessageBase
{
[ProtoMember(201)]
public long[] EntityId;
public VoxelResetMessage()
{ }
public VoxelResetMessage(long[] entityId)
{
EntityId = entityId;
}
public override void ProcessClient()
{
//MyAPIGateway.Parallel.ForEach(EntityId, id =>
foreach (var id in EntityId)
{
IMyEntity e;
if (!MyAPIGateway.Entities.TryGetEntityById(id, out e))
continue;
var v = e as IMyVoxelBase;
v?.Storage.Reset(MyStorageDataTypeFlags.All);
}
}
public override void ProcessServer()
{
throw new Exception();
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Sandbox.ModAPI;
using Torch.Mod.Messages;
using VRage;
using VRage.Collections;
using VRage.Game.ModAPI;
using VRage.Utils;
using Task = ParallelTasks.Task;
namespace Torch.Mod
{
public static class ModCommunication
{
public const ushort NET_ID = 4352;
private static bool _closing = false;
private static BlockingCollection<MessageBase> _processing;
private static MyConcurrentPool<IncomingMessage> _messagePool;
private static List<IMyPlayer> _playerCache;
public static void Register()
{
MyLog.Default.WriteLineAndConsole("TORCH MOD: Registering mod communication.");
_processing = new BlockingCollection<MessageBase>(new ConcurrentQueue<MessageBase>());
_playerCache = new List<IMyPlayer>();
_messagePool = new MyConcurrentPool<IncomingMessage>(8);
MyAPIGateway.Multiplayer.RegisterMessageHandler(NET_ID, MessageHandler);
//background thread to handle de/compression and processing
_closing = false;
MyAPIGateway.Parallel.StartBackground(DoProcessing);
MyLog.Default.WriteLineAndConsole("TORCH MOD: Mod communication registered successfully.");
}
public static void Unregister()
{
MyLog.Default.WriteLineAndConsole("TORCH MOD: Unregistering mod communication.");
MyAPIGateway.Multiplayer?.UnregisterMessageHandler(NET_ID, MessageHandler);
_processing?.CompleteAdding();
_closing = true;
//_task.Wait();
}
private static void MessageHandler(byte[] bytes)
{
var m = _messagePool.Get();
m.CompressedData = bytes;
_processing.Add(m);
}
public static void DoProcessing()
{
while (!_closing)
{
try
{
var m = _processing.Take();
MyLog.Default.WriteLineAndConsole($"Processing message: {m.GetType().Name}");
if (m is IncomingMessage)
{
MessageBase i;
try
{
var o = MyCompression.Decompress(m.CompressedData);
m.CompressedData = null;
_messagePool.Return((IncomingMessage)m);
i = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(o);
}
catch (Exception ex)
{
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Failed to deserialize message! {ex}");
continue;
}
if (MyAPIGateway.Multiplayer.IsServer)
i.ProcessServer();
else
i.ProcessClient();
}
else
{
var b = MyAPIGateway.Utilities.SerializeToBinary(m);
m.CompressedData = MyCompression.Compress(b);
MyAPIGateway.Utilities.InvokeOnGameThread(() =>
{
switch (m.TargetType)
{
case MessageTarget.Single:
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, m.Target);
break;
case MessageTarget.Server:
MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, m.CompressedData);
break;
case MessageTarget.AllClients:
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId)
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
}
break;
case MessageTarget.AllExcept:
MyAPIGateway.Players.GetPlayers(_playerCache);
foreach (var p in _playerCache)
{
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || m.Ignore.Contains(p.SteamUserId))
continue;
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
}
break;
default:
throw new Exception();
}
_playerCache.Clear();
});
}
}
catch (Exception ex)
{
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Exception occurred in communication thread! {ex}");
}
}
MyLog.Default.WriteLineAndConsole("TORCH MOD: COMMUNICATION THREAD: EXIT SIGNAL RECEIVED!");
//exit signal received. Clean everything and GTFO
_processing?.Dispose();
_processing = null;
_messagePool?.Clean();
_messagePool = null;
_playerCache = null;
}
public static void SendMessageTo(MessageBase message, ulong target)
{
if (!MyAPIGateway.Multiplayer.IsServer)
throw new Exception("Only server can send targeted messages");
if (_closing)
return;
message.Target = target;
message.TargetType = MessageTarget.Single;
_processing.Add(message);
}
public static void SendMessageToClients(MessageBase message)
{
if (!MyAPIGateway.Multiplayer.IsServer)
throw new Exception("Only server can send targeted messages");
if (_closing)
return;
message.TargetType = MessageTarget.AllClients;
_processing.Add(message);
}
public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers)
{
if (!MyAPIGateway.Multiplayer.IsServer)
throw new Exception("Only server can send targeted messages");
if (_closing)
return;
message.TargetType = MessageTarget.AllExcept;
message.Ignore = ignoredUsers;
_processing.Add(message);
}
public static void SendMessageToServer(MessageBase message)
{
if (_closing)
return;
message.TargetType = MessageTarget.Server;
_processing.Add(message);
}
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>3ce4d2e9-b461-4f19-8233-f87e0dfddd74</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Torch.Mod</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Messages\IncomingMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Messages\VoxelResetMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ModCommunication.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TorchModCore.cs" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>3ce4d2e9-b461-4f19-8233-f87e0dfddd74</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<Import Project="Torch.Mod.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

37
Torch.Mod/TorchModCore.cs Normal file
View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VRage.Game.Components;
namespace Torch.Mod
{
[MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
public class TorchModCore : MySessionComponentBase
{
public const ulong MOD_ID = 1406994352;
private static bool _init;
public override void UpdateAfterSimulation()
{
if (_init)
return;
_init = true;
ModCommunication.Register();
}
protected override void UnloadData()
{
try
{
ModCommunication.Unregister();
}
catch
{
//session unloading, don't care
}
}
}
}

View File

@@ -39,6 +39,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -46,6 +47,10 @@
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
</Reference>
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
</Reference>
@@ -65,6 +70,7 @@
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TorchServerReflectionTest.cs" />
<Compile Include="TorchServerSessionSettingsTest.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
@@ -88,6 +94,9 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
</Project>

View File

@@ -6,6 +6,7 @@ using Xunit;
namespace Torch.Server.Tests
{
#warning Disabled reflection tests because of seemingly random failures
public class TorchServerReflectionTest
{
static TorchServerReflectionTest()
@@ -34,7 +35,7 @@ namespace Torch.Server.Tests
public static IEnumerable<object[]> Events => Manager().Events;
#region Binding
[Theory]
//[Theory]
[MemberData(nameof(Getters))]
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
{
@@ -45,7 +46,7 @@ namespace Torch.Server.Tests
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
//[Theory]
[MemberData(nameof(Setters))]
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
{
@@ -56,7 +57,7 @@ namespace Torch.Server.Tests
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
//[Theory]
[MemberData(nameof(Invokers))]
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
{
@@ -67,7 +68,7 @@ namespace Torch.Server.Tests
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
//[Theory]
[MemberData(nameof(Events))]
public void TestBindingEvents(ReflectionTestManager.FieldRef field)
{

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Torch.Server.ViewModels;
using VRage.Game;
using Xunit;
using System.ComponentModel.DataAnnotations;
namespace Torch.Server.Tests
{
public class TorchServerSessionSettingsTest
{
public static PropertyInfo[] ViewModelProperties = typeof(SessionSettingsViewModel).GetProperties(BindingFlags.Public | BindingFlags.Instance);
public static IEnumerable<object[]> ModelFields = typeof(MyObjectBuilder_SessionSettings).GetFields(BindingFlags.Public | BindingFlags.Instance).Select(x => new object[] { x });
[Theory]
[MemberData(nameof(ModelFields))]
public void MissingPropertyTest(FieldInfo modelField)
{
// Ignore fields that aren't applicable to SE
if (modelField.GetCustomAttribute<GameRelationAttribute>()?.RelatedTo == Game.MedievalEngineers)
return;
if (string.IsNullOrEmpty(modelField.GetCustomAttribute<DisplayAttribute>()?.Name))
return;
var match = ViewModelProperties.FirstOrDefault(p => p.Name.Equals(modelField.Name, StringComparison.InvariantCultureIgnoreCase));
Assert.NotNull(match);
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Torch.Commands;
namespace Torch.Server.Commands
{
[Category("whitelist")]
public class WhitelistCommands : CommandModule
{
private TorchConfig Config => (TorchConfig)Context.Torch.Config;
[Command("on", "Enables the whitelist.")]
public void On()
{
if (!Config.EnableWhitelist)
{
Config.EnableWhitelist = true;
Context.Respond("Whitelist enabled.");
Config.Save();
}
else
Context.Respond("Whitelist is already enabled.");
}
[Command("off", "Disables the whitelist")]
public void Off()
{
if (Config.EnableWhitelist)
{
Config.EnableWhitelist = false;
Context.Respond("Whitelist disabled.");
Config.Save();
}
else
Context.Respond("Whitelist is already disabled.");
}
[Command("add", "Add a Steam ID to the whitelist.")]
public void Add(ulong steamId)
{
if (Config.Whitelist.Add(steamId))
{
Context.Respond($"Added {steamId} to the whitelist.");
Config.Save();
}
else
Context.Respond($"{steamId} is already whitelisted.");
}
[Command("remove", "Remove a Steam ID from the whitelist.")]
public void Remove(ulong steamId)
{
if (Config.Whitelist.Remove(steamId))
{
Context.Respond($"Removed {steamId} from the whitelist.");
Config.Save();
}
else
Context.Respond($"{steamId} is not whitelisted.");
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Documents;
using System.Windows.Media;
using NLog;
using NLog.Targets;
namespace Torch.Server
{
/// <summary>
/// NLog target that writes to a <see cref="FlowDocument"/>.
/// </summary>
[Target("flowDocument")]
public sealed class FlowDocumentTarget : TargetWithLayout
{
private FlowDocument _document = new FlowDocument { Background = new SolidColorBrush(Colors.Black) };
private readonly Paragraph _paragraph = new Paragraph();
private readonly int _maxLines = 500;
public FlowDocument Document => _document;
public FlowDocumentTarget()
{
_document.Blocks.Add(_paragraph);
}
/// <inheritdoc />
protected override void Write(LogEventInfo logEvent)
{
_document.Dispatcher.BeginInvoke(() =>
{
var message = $"{Layout.Render(logEvent)}\n";
_paragraph.Inlines.Add(new Run(message) {Foreground = LogLevelColors[logEvent.Level]});
// A massive paragraph slows the UI down
if (_paragraph.Inlines.Count > _maxLines)
_paragraph.Inlines.Remove(_paragraph.Inlines.FirstInline);
});
}
private static readonly Dictionary<LogLevel, SolidColorBrush> LogLevelColors = new Dictionary<LogLevel, SolidColorBrush>
{
[LogLevel.Trace] = new SolidColorBrush(Colors.DimGray),
[LogLevel.Debug] = new SolidColorBrush(Colors.DarkGray),
[LogLevel.Info] = new SolidColorBrush(Colors.White),
[LogLevel.Warn] = new SolidColorBrush(Colors.Magenta),
[LogLevel.Error] = new SolidColorBrush(Colors.Yellow),
[LogLevel.Fatal] = new SolidColorBrush(Colors.Red),
};
}
}

View File

@@ -9,9 +9,14 @@ using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using NLog;
using NLog.Targets;
using Sandbox.Engine.Utils;
using Torch.Utils;
using VRage.FileSystem;
using VRage.Library.Exceptions;
namespace Torch.Server
{
@@ -23,6 +28,7 @@ namespace Torch.Server
private const string STEAMCMD_ZIP = "temp.zip";
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
private const string RUNSCRIPT = @"force_install_dir ../
login anonymous
app_update 298740
@@ -49,9 +55,17 @@ quit";
AppDomain.CurrentDomain.UnhandledException += HandleException;
#endif
if (!args.Contains("-noupdate"))
// This is what happens when Keen is bad and puts extensions into the System namespace.
if (!Enumerable.Contains(args, "-noupdate"))
RunSteamCmd();
var basePath = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
var apiSource = Path.Combine(basePath, "DedicatedServer64", "steam_api64.dll");
var apiTarget = Path.Combine(basePath, "steam_api64.dll");
if (!File.Exists(apiTarget))
File.Copy(apiSource, apiTarget);
_config = InitConfig();
if (!_config.Parse(args))
return false;
@@ -69,7 +83,6 @@ quit";
Console.Write(".");
Thread.Sleep(1000);
}
}
catch
{
@@ -84,17 +97,29 @@ quit";
public void Run()
{
_server = new TorchServer(_config);
_server.Init();
var init = Task.Run(() => _server.Init()).ContinueWith(x =>
{
if (!x.IsFaulted)
return;
Log.Error("Error initializing server.");
LogException(x.Exception);
});
if (!_config.NoGui)
{
var ui = new TorchUI(_server);
if (_config.Autostart)
_server.Start();
ui.ShowDialog();
init.ContinueWith(x => _server.Start());
Log.Info("Showing UI");
Console.SetOut(TextWriter.Null);
NativeMethods.FreeConsole();
new TorchUI(_server).ShowDialog();
}
else
{
init.Wait();
_server.Start();
}
}
private TorchConfig InitConfig()
@@ -109,13 +134,13 @@ quit";
else
{
Log.Info($"Generating default config at {configPath}");
var config = new TorchConfig { InstancePath = Path.GetFullPath("Instance") };
var config = new TorchConfig {InstancePath = Path.GetFullPath("Instance")};
config.Save(configPath);
return config;
}
}
private static void RunSteamCmd()
public static void RunSteamCmd()
{
var log = LogManager.GetLogger("SteamCMD");
@@ -170,26 +195,43 @@ quit";
{
LogException(ex.InnerException);
}
Log.Fatal(ex);
if (ex is ReflectionTypeLoadException exti)
foreach (Exception exl in exti.LoaderExceptions)
LogException(exl);
if (ex is AggregateException ag)
foreach (Exception e in ag.InnerExceptions)
LogException(e);
}
private void HandleException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
LogException(ex);
Console.WriteLine("Exiting in 5 seconds.");
Thread.Sleep(5000);
if (MyFakes.ENABLE_MINIDUMP_SENDING)
{
string path = Path.Combine(MyFileSystem.UserDataPath, "Minidump.dmp");
Log.Info($"Generating minidump at {path}");
MyMiniDump.Options options = MyMiniDump.Options.WithProcessThreadData | MyMiniDump.Options.WithThreadInfo;
MyMiniDump.Write(path, options, MyMiniDump.ExceptionInfo.Present);
}
LogManager.Flush();
if (_config.RestartOnCrash)
{
Console.WriteLine("Restarting in 5 seconds.");
Thread.Sleep(5000);
var exe = typeof(Program).Assembly.Location;
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
Process.Start(exe, _config.ToString());
}
else
{
MessageBox.Show("Torch encountered a fatal error and needs to close. Please check the logs for details.");
}
Process.GetCurrentProcess().Kill();
}
}

View File

@@ -10,19 +10,27 @@ using Havok;
using NLog;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.Gui;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using Torch.Mod;
using Torch.Server.ViewModels;
using VRage;
using VRage.FileSystem;
using VRage.Game;
using VRage.Game.ObjectBuilder;
using VRage.ObjectBuilders;
using VRage.Plugins;
namespace Torch.Server.Managers
{
public class InstanceManager : Manager
{
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
public event Action<ConfigDedicatedViewModel> InstanceLoaded;
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
[Dependency]
@@ -35,6 +43,8 @@ namespace Torch.Server.Managers
public void LoadInstance(string path, bool validate = true)
{
Log.Info($"Loading instance {path}");
if (validate)
ValidateInstance(path);
@@ -54,33 +64,73 @@ namespace Torch.Server.Managers
config.Load(configPath);
DedicatedConfig = new ConfigDedicatedViewModel(config);
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
foreach (var f in worldFolders)
DedicatedConfig.WorldPaths.Add(f);
{
if (!string.IsNullOrEmpty(f) && File.Exists(Path.Combine(f, "Sandbox.sbc")))
DedicatedConfig.Worlds.Add(new WorldViewModel(f));
}
if (DedicatedConfig.WorldPaths.Count == 0)
if (DedicatedConfig.Worlds.Count == 0)
{
Log.Warn($"No worlds found in the current instance {path}.");
return;
}
ImportWorldConfig();
SelectWorld(DedicatedConfig.LoadWorld ?? DedicatedConfig.Worlds.First().WorldPath, false);
/*
if (string.IsNullOrEmpty(DedicatedConfig.LoadWorld))
{
Log.Warn("No world specified, importing first available world.");
SelectWorld(DedicatedConfig.WorldPaths[0], false);
}*/
InstanceLoaded?.Invoke(DedicatedConfig);
}
public void SelectWorld(string worldPath, bool modsOnly = true)
{
DedicatedConfig.LoadWorld = worldPath;
ImportWorldConfig(modsOnly);
DedicatedConfig.SelectedWorld = DedicatedConfig.Worlds.FirstOrDefault(x => x.WorldPath == worldPath);
if (DedicatedConfig.SelectedWorld?.Checkpoint != null)
{
DedicatedConfig.Mods.Clear();
//remove the Torch mod to avoid running multiple copies of it
DedicatedConfig.SelectedWorld.Checkpoint.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID);
foreach (var m in DedicatedConfig.SelectedWorld.Checkpoint.Mods)
DedicatedConfig.Mods.Add(m.PublishedFileId);
}
}
public void SelectWorld(WorldViewModel world, bool modsOnly = true)
{
DedicatedConfig.LoadWorld = world.WorldPath;
DedicatedConfig.SelectedWorld = world;
if (DedicatedConfig.SelectedWorld?.Checkpoint != null)
{
DedicatedConfig.Mods.Clear();
//remove the Torch mod to avoid running multiple copies of it
DedicatedConfig.SelectedWorld.Checkpoint.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID);
foreach (var m in DedicatedConfig.SelectedWorld.Checkpoint.Mods)
DedicatedConfig.Mods.Add(m.PublishedFileId);
}
}
public void ImportSelectedWorldConfig()
{
ImportWorldConfig(DedicatedConfig.SelectedWorld, false);
}
private void ImportWorldConfig(WorldViewModel world, bool modsOnly = true)
{
var sb = new StringBuilder();
foreach (var mod in world.Checkpoint.Mods)
sb.AppendLine(mod.PublishedFileId.ToString());
DedicatedConfig.Mods = world.Checkpoint.Mods.Select(x => x.PublishedFileId).ToList();
Log.Debug("Loaded mod list from world");
if (!modsOnly)
DedicatedConfig.SessionSettings = world.Checkpoint.Settings;
}
private void ImportWorldConfig(bool modsOnly = true)
{
@@ -97,15 +147,11 @@ namespace Torch.Server.Managers
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
if (checkpoint == null)
{
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})");
return;
}
var sb = new StringBuilder();
foreach (var mod in checkpoint.Mods)
sb.AppendLine(mod.PublishedFileId.ToString());
DedicatedConfig.Mods = sb.ToString();
DedicatedConfig.Mods = checkpoint.Mods.Select(x => x.PublishedFileId).ToList();
Log.Debug("Loaded mod list from world");
@@ -121,23 +167,39 @@ namespace Torch.Server.Managers
public void SaveConfig()
{
var cf = Torch.Config as TorchConfig;
if (cf?.ReservedPlayers?.Count > 0)
{
foreach (var res in cf.ReservedPlayers)
{
if (!DedicatedConfig.Reserved.Contains(res))
DedicatedConfig.Reserved.Add(res);
}
}
DedicatedConfig.Save(Path.Combine(Torch.Config.InstancePath, CONFIG_NAME));
Log.Info("Saved dedicated config.");
try
{
MyObjectBuilderSerializer.DeserializeXML(Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
var sandboxPath = Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc");
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
if (checkpoint == null)
{
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})");
return;
}
checkpoint.SessionName = DedicatedConfig.WorldName;
checkpoint.Settings = DedicatedConfig.SessionSettings;
checkpoint.Mods.Clear();
foreach (var modId in DedicatedConfig.Model.Mods)
foreach (var modId in DedicatedConfig.Mods)
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld);
MyObjectBuilderSerializer.SerializeXML(sandboxPath, false, checkpoint);
//MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld);
Log.Info("Saved world config.");
}
catch (Exception e)
@@ -162,4 +224,44 @@ namespace Torch.Server.Managers
config.Save(configPath);
}
}
public class WorldViewModel : ViewModel
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public string FolderName { get; set; }
public string WorldPath { get; }
public long WorldSizeKB { get; }
private string _checkpointPath;
public CheckpointViewModel Checkpoint { get; private set; }
public WorldViewModel(string worldPath)
{
WorldPath = worldPath;
WorldSizeKB = new DirectoryInfo(worldPath).GetFiles().Sum(x => x.Length) / 1024;
_checkpointPath = Path.Combine(WorldPath, "Sandbox.sbc");
FolderName = Path.GetFileName(worldPath);
BeginLoadCheckpoint();
}
public async Task SaveCheckpointAsync()
{
await Task.Run(() =>
{
using (var f = File.Open(_checkpointPath, FileMode.Create))
MyObjectBuilderSerializer.SerializeXML(f, Checkpoint);
});
}
private void BeginLoadCheckpoint()
{
//Task.Run(() =>
{
Log.Info($"Preloading checkpoint {_checkpointPath}");
MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint);
Checkpoint = new CheckpointViewModel(checkpoint);
OnPropertyChanged(nameof(Checkpoint));
}//);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
@@ -9,11 +10,15 @@ using NLog.Fluent;
using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Game.World;
using Steamworks;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using Torch.Utils;
using Torch.ViewModels;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.GameServices;
using VRage.Network;
using VRage.Steam;
@@ -25,7 +30,9 @@ namespace Torch.Server.Managers
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
#pragma warning disable 649
[ReflectedGetter(Name = "m_members")] private static Func<MyDedicatedServerBase, List<ulong>> _members;
[ReflectedGetter(Name = "m_members")]
private static Func<MyDedicatedServerBase, List<ulong>> _members;
[ReflectedGetter(Name = "m_waitingForGroup")]
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
#pragma warning restore 649
@@ -35,6 +42,9 @@ namespace Torch.Server.Managers
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
[Dependency]
private InstanceManager _instanceManager;
/// <inheritdoc />
public MultiplayerManagerDedicated(ITorchBase torch) : base(torch)
{
@@ -49,15 +59,34 @@ namespace Torch.Server.Managers
Torch.Invoke(() =>
{
MyMultiplayer.Static.BanClient(steamId, banned);
if (_gameOwnerIds.ContainsKey(steamId))
MyMultiplayer.Static.BanClient(_gameOwnerIds[steamId], banned);
});
}
internal void RaiseClientBanned(ulong user, bool banned)
{
PlayerBanned?.Invoke(user, banned);
Torch.Invoke(() =>
{
if(_gameOwnerIds.TryGetValue(user, out ulong owner))
MyMultiplayer.Static.BanClient(owner, banned);
});
}
internal void RaiseClientKicked(ulong user)
{
PlayerKicked?.Invoke(user);
}
/// <inheritdoc />
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) ||
MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
/// <inheritdoc />
public event Action<ulong> PlayerKicked;
/// <inheritdoc />
public event Action<ulong, bool> PlayerBanned;
/// <inheritdoc/>
public override void Attach()
{
@@ -105,13 +134,17 @@ namespace Torch.Server.Managers
[ReflectedStaticMethod(Type = typeof(MyGameService), Name = "GetServerAccountType")]
private static Func<ulong, MyGameServiceAccountType> _getServerAccountType;
[ReflectedMethod(Name = "UserAccepted")] private static Action<MyDedicatedServerBase, ulong> _userAcceptedImpl;
[ReflectedMethod(Name = "UserAccepted")]
private static Action<MyDedicatedServerBase, ulong> _userAcceptedImpl;
[ReflectedMethod(Name = "UserRejected")]
private static Action<MyDedicatedServerBase, ulong, JoinResult> _userRejected;
[ReflectedMethod(Name = "IsClientBanned")] private static Func<MyMultiplayerBase, ulong, bool> _isClientBanned;
[ReflectedMethod(Name = "IsClientKicked")] private static Func<MyMultiplayerBase, ulong, bool> _isClientKicked;
[ReflectedMethod(Name = "IsClientBanned")]
private static Func<MyMultiplayerBase, ulong, bool> _isClientBanned;
[ReflectedMethod(Name = "IsClientKicked")]
private static Func<MyMultiplayerBase, ulong, bool> _isClientKicked;
[ReflectedMethod(Name = "RaiseClientKicked")]
private static Action<MyMultiplayerBase, ulong> _raiseClientKicked;
@@ -135,50 +168,83 @@ namespace Torch.Server.Managers
}
//Largely copied from SE
private void ValidateAuthTicketResponse(ulong steamID, JoinResult response, ulong steamOwner)
private void ValidateAuthTicketResponse(ulong steamId, JoinResult response, ulong steamOwner)
{
_log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
if (MySandboxGame.ConfigDedicated.GroupID == 0uL)
RunEvent(new ValidateAuthTicketEvent(steamID, steamOwner, response, 0, true, false));
var state = new MyP2PSessionState();
MySteamService.Static.Peer2Peer.GetSessionState(steamId, ref state);
var ip = new IPAddress(BitConverter.GetBytes(state.RemoteIP).Reverse().ToArray());
Torch.CurrentSession.KeenSession.PromotedUsers.TryGetValue(steamId, out MyPromoteLevel promoteLevel);
_log.Debug($"ValidateAuthTicketResponse(user={steamId}, response={response}, owner={steamOwner}, permissions={promoteLevel})");
_log.Info($"Connection attempt by {steamId} from {ip}");
if (Torch.CurrentSession.KeenSession.OnlineMode == MyOnlineModeEnum.OFFLINE &&
promoteLevel < MyPromoteLevel.Admin)
{
_log.Warn($"Rejecting user {steamId}, world is set to offline and user is not admin.");
UserRejected(steamId, JoinResult.TicketCanceled);
}
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL)
RunEvent(new ValidateAuthTicketEvent(steamId, steamOwner, response, 0, true, false));
else if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
UserRejected(steamID, JoinResult.GroupIdInvalid);
else if (MyGameService.GameServer.RequestGroupStatus(steamID, MySandboxGame.ConfigDedicated.GroupID))
UserRejected(steamId, JoinResult.GroupIdInvalid);
else if (MyGameService.GameServer.RequestGroupStatus(steamId, MySandboxGame.ConfigDedicated.GroupID))
lock (_waitingForGroupLocal)
{
if (_waitingForGroupLocal.Count >= _waitListSize)
_waitingForGroupLocal.RemoveAt(0);
_waitingForGroupLocal.Add(new WaitingForGroup(steamID, response, steamOwner));
_waitingForGroupLocal.Add(new WaitingForGroup(steamId, response, steamOwner));
}
else
UserRejected(steamID, JoinResult.SteamServersOffline);
UserRejected(steamId, JoinResult.SteamServersOffline);
}
private void RunEvent(ValidateAuthTicketEvent info)
{
MultiplayerManagerDedicatedEventShim.RaiseValidateAuthTicket(ref info);
JoinResult internalAuth;
if (info.FutureVerdict == null)
if (IsBanned(info.SteamOwner) || IsBanned(info.SteamID))
internalAuth = JoinResult.BannedByAdmins;
else if (_isClientKicked(MyMultiplayer.Static, info.SteamID) ||
_isClientKicked(MyMultiplayer.Static, info.SteamOwner))
internalAuth = JoinResult.KickedRecently;
else if (info.SteamResponse == JoinResult.OK)
{
if (IsBanned(info.SteamOwner) || IsBanned(info.SteamID))
CommitVerdict(info.SteamID, JoinResult.BannedByAdmins);
else if (_isClientKicked(MyMultiplayer.Static, info.SteamID) ||
_isClientKicked(MyMultiplayer.Static, info.SteamOwner))
CommitVerdict(info.SteamID, JoinResult.KickedRecently);
else if (info.SteamResponse != JoinResult.OK)
CommitVerdict(info.SteamID, info.SteamResponse);
else if (MyMultiplayer.Static.MemberLimit > 0 &&
MyMultiplayer.Static.MemberCount + 1 > MyMultiplayer.Static.MemberLimit)
CommitVerdict(info.SteamID, JoinResult.ServerFull);
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString()) ||
var config = (TorchConfig) Torch.Config;
if (config.EnableWhitelist && !config.Whitelist.Contains(info.SteamID))
{
_log.Warn($"Rejecting user {info.SteamID} because they are not whitelisted in Torch.cfg.");
internalAuth = JoinResult.NotInGroup;
}
else if (MySandboxGame.ConfigDedicated.Reserved.Contains(info.SteamID))
internalAuth = JoinResult.OK;
//Admins can bypass member limit
else if (MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString()) ||
MySandboxGame.ConfigDedicated.Administrators.Contains(_convertSteamIDFrom64(info.SteamID)))
CommitVerdict(info.SteamID, JoinResult.OK);
else if (MySandboxGame.ConfigDedicated.GroupID == info.Group && (info.Member || info.Officer))
CommitVerdict(info.SteamID, JoinResult.OK);
internalAuth = JoinResult.OK;
//Server counts as a client, so subtract 1 from MemberCount
else if (MyMultiplayer.Static.MemberLimit > 0 &&
MyMultiplayer.Static.MemberCount - 1 >= MyMultiplayer.Static.MemberLimit)
internalAuth = JoinResult.ServerFull;
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL)
internalAuth = JoinResult.OK;
else
CommitVerdict(info.SteamID, JoinResult.NotInGroup);
return;
{
if (MySandboxGame.ConfigDedicated.GroupID == info.Group && (info.Member || info.Officer))
internalAuth = JoinResult.OK;
else
internalAuth = JoinResult.NotInGroup;
}
}
else
internalAuth = info.SteamResponse;
info.FutureVerdict = Task.FromResult(internalAuth);
MultiplayerManagerDedicatedEventShim.RaiseValidateAuthTicket(ref info);
info.FutureVerdict.ContinueWith((task) =>
{
@@ -190,6 +256,7 @@ namespace Torch.Server.Managers
}
else
verdict = task.Result;
Torch.Invoke(() => { CommitVerdict(info.SteamID, verdict); });
});
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;
using Sandbox.Engine.Multiplayer;
using Torch.Managers.PatchManager;
using Torch.API.Managers;
namespace Torch.Server.Managers
{
[PatchShim]
internal static class MultiplayerManagerDedicatedPatchShim
{
public static void Patch(PatchContext ctx)
{
ctx.GetPattern(typeof(MyDedicatedServerBase).GetMethod(nameof(MyDedicatedServerBase.BanClient))).Prefixes.Add(typeof(MultiplayerManagerDedicatedPatchShim).GetMethod(nameof(BanPrefix)));
ctx.GetPattern(typeof(MyDedicatedServerBase).GetMethod(nameof(MyDedicatedServerBase.KickClient))).Prefixes.Add(typeof(MultiplayerManagerDedicatedPatchShim).GetMethod(nameof(KickPrefix)));
}
public static void BanPrefix(ulong userId, bool banned)
{
TorchBase.Instance.CurrentSession.Managers.GetManager<MultiplayerManagerDedicated>().RaiseClientBanned(userId, banned);
}
public static void KickPrefix(ulong userId)
{
TorchBase.Instance.CurrentSession.Managers.GetManager<MultiplayerManagerDedicated>().RaiseClientKicked(userId);
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Torch.Server
{
public class MultiTextWriter : TextWriter
{
private IEnumerable<TextWriter> writers;
public MultiTextWriter(IEnumerable<TextWriter> writers)
{
this.writers = writers.ToList();
}
public MultiTextWriter(params TextWriter[] writers)
{
this.writers = writers;
}
public override void Write(char value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Write(string value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Flush()
{
foreach (var writer in writers)
writer.Flush();
}
public override void Close()
{
foreach (var writer in writers)
writer.Close();
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
}

View File

@@ -1,33 +1,15 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.IO.Compression;
using System.Net;
using System.Reflection;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using NLog;
using Sandbox.Game.World;
using Sandbox.ModAPI;
using Torch;
using Torch.API;
using Torch.Server.Views;
using VRage.Game.ModAPI;
using System.IO.Compression;
using System.Net;
using System.Security.Policy;
using Torch.Server.Managers;
using NLog.Targets;
using Torch.Utils;
using VRage.FileSystem;
using VRageRender;
namespace Torch.Server
{
@@ -39,6 +21,7 @@ namespace Torch.Server
[STAThread]
public static void Main(string[] args)
{
Target.Register<FlowDocumentTarget>("FlowDocument");
//Ensures that all the files are downloaded in the Torch directory.
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
var binDir = Path.Combine(workingDir, "DedicatedServer64");
@@ -46,7 +29,7 @@ namespace Torch.Server
if (!TorchLauncher.IsTorchWrapped())
{
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName,args, binDir);
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName, args, binDir);
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace Torch.Server
{
public class RichTextBoxWriter : TextWriter
{
private RichTextBox textbox;
private StringBuilder _cache = new StringBuilder();
public RichTextBoxWriter(RichTextBox textbox)
{
this.textbox = textbox;
textbox.Document.Background = new SolidColorBrush(UnpackColor(Console.BackgroundColor));
textbox.Document.Blocks.Clear();
textbox.Document.Blocks.Add(new Paragraph {LineHeight = 12});
}
public override void Write(char value)
{
if (value == '\r')
return;
_cache.Append(value);
if (value == '\n')
{
var str = _cache.ToString();
_cache.Clear();
var brush = _brushes[Console.ForegroundColor];
textbox.Dispatcher.BeginInvoke(() =>
{
var p = (Paragraph)textbox.Document.Blocks.FirstBlock;
p.Inlines.Add(new Run(str) { Foreground = brush });
textbox.ScrollToEnd();
});
}
}
public override void Write(string value)
{
var brush = _brushes[Console.ForegroundColor];
textbox.Dispatcher.BeginInvoke(() =>
{
var p = (Paragraph)textbox.Document.Blocks.FirstBlock;
p.Inlines.Add(new Run(value) { Foreground = brush });
textbox.ScrollToEnd();
});
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
static RichTextBoxWriter()
{
foreach (var value in (ConsoleColor[])Enum.GetValues(typeof(ConsoleColor)))
{
_brushes.Add(value, new SolidColorBrush(UnpackColor(value)));
}
}
private static Dictionary<ConsoleColor, SolidColorBrush> _brushes = new Dictionary<ConsoleColor, SolidColorBrush>();
private static Color UnpackColor(ConsoleColor color)
{
var colorByte = (byte)color;
var isBright = (colorByte & 0b1000) >> 3 > 0;
var brightness = isBright ? (byte)255 : (byte)128;
var red = (colorByte & 0b0100) >> 2;
var green = (colorByte & 0b0010) >> 1;
var blue = (colorByte & 0b0001);
return new Color
{
R = (byte)(brightness * red),
G = (byte)(brightness * green),
B = (byte)(brightness * blue),
A = 255
};
}
}
}

View File

@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Torch.Collections;
namespace Torch.Server
{
public class ServerStatistics
{
public RollingAverage SimSpeed { get; } = new RollingAverage(30);
}
}

View File

@@ -0,0 +1,17 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedSingleRowTabControl.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,85 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TabControl">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" Orientation="Horizontal"/>
<Border x:Name="ContentPanel" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
<TabPanel x:Name="HeaderPanel2"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,0,2,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,2,0,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="0,2,2,2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,19 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedSingleRowTabControl.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,85 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TabControl">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" Orientation="Horizontal"/>
<Border x:Name="ContentPanel" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
<TabPanel x:Name="HeaderPanel2"
Panel.ZIndex ="1"
KeyboardNavigation.TabIndex="1"
Grid.Column="0"
Grid.Row="0"
Margin="2,2,2,0"
IsItemsHost="true"/>
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,0,2,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,2,0,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="0,2,2,2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
</Style>
</ResourceDictionary>

View File

@@ -15,6 +15,21 @@
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
@@ -44,11 +59,17 @@
<ApplicationIcon>torchicon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="ControlzEx, Version=3.0.2.4, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ControlzEx.3.0.2.4\lib\net45\ControlzEx.dll</HintPath>
</Reference>
<Reference Include="HavokWrapper, Version=1.0.6051.28726, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MahApps.Metro, Version=1.6.1.4, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.1.6.1\lib\net45\MahApps.Metro.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.dll</HintPath>
@@ -96,18 +117,20 @@
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SteamSDK, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\SteamSDK.dll</HintPath>
<Private>False</Private>
<Reference Include="Steamworks.NET">
<HintPath>..\GameBinaries\Steamworks.NET.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
@@ -194,15 +217,18 @@
<Compile Include="..\Versioning\AssemblyVersion.cs">
<Link>Properties\AssemblyVersion.cs</Link>
</Compile>
<Compile Include="Commands\WhitelistCommands.cs" />
<Compile Include="FlowDocumentTarget.cs" />
<Compile Include="ListBoxExtensions.cs" />
<Compile Include="Managers\EntityControlManager.cs" />
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
<Compile Include="Managers\InstanceManager.cs" />
<Compile Include="Managers\MultiplayerManagerDedicatedEventShim.cs" />
<Compile Include="Managers\MultiplayerManagerDedicatedPatchShim.cs" />
<Compile Include="NativeMethods.cs" />
<Compile Include="Initializer.cs" />
<Compile Include="Properties\Annotations.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerStatistics.cs" />
<Compile Include="TorchConfig.cs" />
<Compile Include="TorchService.cs">
<SubType>Component</SubType>
@@ -211,12 +237,26 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
<Compile Include="ViewModels\CheckpointViewModel.cs" />
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
<Compile Include="ViewModels\Entities\CharacterViewModel.cs" />
<Compile Include="ViewModels\ConfigDedicatedViewModel.cs" />
<Compile Include="ViewModels\Entities\EntityControlViewModel.cs" />
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
<Compile Include="Views\Converters\DefinitionToIdConverter.cs" />
<Compile Include="Views\Converters\BooleanAndConverter.cs" />
<Compile Include="Views\Converters\ListConverter.cs" />
<Compile Include="MultiTextWriter.cs" />
<Compile Include="RichTextBoxWriter.cs" />
<Compile Include="Views\Entities\CharacterView.xaml.cs">
<DependentUpon>CharacterView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ThemeControl.xaml.cs">
<DependentUpon>ThemeControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ValidationRules\ListConverterValidationRule.cs" />
<Compile Include="Views\Entities\EntityControlHost.xaml.cs">
<DependentUpon>EntityControlHost.xaml</DependentUpon>
</Compile>
@@ -230,7 +270,6 @@
<Compile Include="ViewModels\ILazyLoad.cs" />
<Compile Include="ViewModels\PluginManagerViewModel.cs" />
<Compile Include="ViewModels\PluginViewModel.cs" />
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
<Compile Include="ViewModels\SteamUserViewModel.cs" />
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
@@ -261,15 +300,15 @@
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
<DependentUpon>VoxelMapView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\FirstTimeSetup.xaml.cs">
<DependentUpon>FirstTimeSetup.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ModsControl.xaml.cs">
<DependentUpon>ModsControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\PluginsControl.xaml.cs">
<DependentUpon>PluginsControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SessionSettingsView.xaml.cs">
<DependentUpon>SessionSettingsView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TorchUI.xaml.cs">
<DependentUpon>TorchUI.xaml</DependentUpon>
</Compile>
@@ -278,6 +317,10 @@
</Compile>
<Compile Include="Program.cs" />
<Compile Include="TorchServer.cs" />
<Compile Include="Views\ValidationRules\NumberValidationRule.cs" />
<Compile Include="Views\WorldGeneratorDialog.xaml.cs">
<DependentUpon>WorldGeneratorDialog.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\Resources.Designer.cs">
@@ -314,6 +357,22 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Page Include="Themes\Dark Theme Animated.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Themes\Dark Theme.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Themes\Light Theme Animated.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Themes\Light Theme.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\Entities\EntityControlHost.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -346,6 +405,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Entities\CharacterView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\Entities\GridView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -358,14 +421,22 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\FirstTimeSetup.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ModsControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\Resources.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SessionSettingsView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ThemeControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\TorchUI.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -374,6 +445,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\WorldGeneratorDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Resource Include="torchicon.ico" />
@@ -381,7 +456,18 @@
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
<PropertyGroup>

View File

@@ -60,8 +60,22 @@ namespace Torch.Server
/// <inheritdoc />
public List<string> Plugins { get; set; } = new List<string>();
public bool EnableWhitelist { get; set; } = false;
public HashSet<ulong> Whitelist { get; set; } = new HashSet<ulong>();
internal Point WindowSize { get; set; } = new Point(800, 600);
internal Point WindowPosition { get; set; } = new Point();
public string LastUsedTheme { get; set; } = "Torch Theme";
//TODO: REMOVE ME BY JULY 2019
[Obsolete("Use vanilla reserved slot config")]
public HashSet<ulong> ReservedPlayers { get; set; } = new HashSet<ulong>();
//Prevent reserved players being written to disk, but allow it to bre read
//remove this when ReservedPlayers is removed
private bool ShouldSerializeReservedPlayers() => false;
[XmlIgnore]
private string _path;

View File

@@ -1,40 +1,35 @@
using Sandbox;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.World;
#region
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xml.Serialization.GeneratedAssembly;
using System.Windows.Forms;
using NLog;
using Sandbox.Engine.Analytics;
using Sandbox;
using Sandbox.Engine.Networking;
using Sandbox.Game.Multiplayer;
using Sandbox.ModAPI;
using SteamSDK;
using Sandbox.Game.World;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Session;
using Torch.Managers;
using Torch.Commands;
using Torch.Mod;
using Torch.Server.Commands;
using Torch.Server.Managers;
using Torch.Utils;
using VRage;
using VRage.Dedicated;
using VRage.FileSystem;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.Game.ObjectBuilder;
using VRage.Game.SessionComponents;
using VRage.Library;
using VRage.ObjectBuilders;
using VRage.Plugins;
using VRage.Utils;
using VRage.Dedicated.RemoteAPI;
using VRage.GameServices;
using VRage.Steam;
using Timer = System.Threading.Timer;
#endregion
#pragma warning disable 618
@@ -42,53 +37,40 @@ namespace Torch.Server
{
public class TorchServer : TorchBase, ITorchServer
{
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
private bool _canRun;
private TimeSpan _elapsedPlayTime;
private bool _hasRun;
private bool _isRunning;
private float _simRatio;
private ServerState _state;
private Stopwatch _uptime;
private Timer _watchdog;
/// <inheritdoc />
public float SimulationRatio
public TorchServer(TorchConfig config = null)
{
get => _simRatio;
set
{
_simRatio = value;
OnPropertyChanged();
}
DedicatedInstance = new InstanceManager(this);
AddManager(DedicatedInstance);
AddManager(new EntityControlManager(this));
Config = config ?? new TorchConfig();
var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory(x => new MultiplayerManagerDedicated(this));
}
/// <inheritdoc />
public TimeSpan ElapsedPlayTime
{
get => _elapsedPlayTime;
set
{
_elapsedPlayTime = value;
OnPropertyChanged();
}
}
public float SimulationRatio { get => _simRatio; set => SetValue(ref _simRatio, value); }
/// <inheritdoc />
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set => SetValue(ref _elapsedPlayTime, value); }
/// <inheritdoc />
public Thread GameThread { get; private set; }
/// <inheritdoc />
public ServerState State
{
get => _state;
private set
{
_state = value;
OnPropertyChanged();
}
}
public bool IsRunning { get => _isRunning; set => SetValue(ref _isRunning, value); }
/// <inheritdoc />
public bool IsRunning
{
get => _isRunning;
set
{
_isRunning = value;
OnPropertyChanged();
}
}
public bool CanRun { get => _canRun; set => SetValue(ref _canRun, value); }
/// <inheritdoc />
public InstanceManager DedicatedInstance { get; }
@@ -97,52 +79,30 @@ namespace Torch.Server
public string InstanceName => Config?.InstanceName;
/// <inheritdoc />
public string InstancePath => Config?.InstancePath;
private bool _isRunning;
private ServerState _state;
private TimeSpan _elapsedPlayTime;
private float _simRatio;
private Timer _watchdog;
private Stopwatch _uptime;
/// <inheritdoc />
public TorchServer(TorchConfig config = null)
{
DedicatedInstance = new InstanceManager(this);
AddManager(DedicatedInstance);
AddManager(new EntityControlManager(this));
Config = config ?? new TorchConfig();
var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory((x) => new MultiplayerManagerDedicated(this));
}
/// <inheritdoc/>
protected override uint SteamAppId => 244850;
/// <inheritdoc/>
/// <inheritdoc />
protected override string SteamAppName => "SpaceEngineersDedicated";
/// <inheritdoc />
public ServerState State { get => _state; private set => SetValue(ref _state, value); }
public event Action<ITorchServer> Initialized;
/// <inheritdoc />
public string InstancePath => Config?.InstancePath;
/// <inheritdoc />
public override void Init()
{
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
Sandbox.Engine.Platform.Game.IsDedicated = true;
Log.Info("Initializing server");
MySandboxGame.IsDedicated = true;
base.Init();
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
}
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
{
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded)
{
_watchdog?.Dispose();
_watchdog = null;
}
CanRun = true;
Initialized?.Invoke(this);
Log.Info($"Initialized server '{Config.InstanceName}' at '{Config.InstancePath}'");
}
/// <inheritdoc />
@@ -150,12 +110,25 @@ namespace Torch.Server
{
if (State != ServerState.Stopped)
return;
if (_hasRun)
{
Restart();
return;
}
State = ServerState.Starting;
IsRunning = true;
CanRun = false;
_hasRun = true;
Log.Info("Starting server.");
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
if (MySandboxGame.ConfigDedicated.RemoteApiEnabled && !string.IsNullOrEmpty(MySandboxGame.ConfigDedicated.RemoteSecurityKey))
{
var myRemoteServer = new MyRemoteServer(MySandboxGame.ConfigDedicated.RemoteApiPort, MySandboxGame.ConfigDedicated.RemoteSecurityKey);
Log.Info($"Remote API started on port {myRemoteServer.Port}");
}
DedicatedInstance.SaveConfig();
_uptime = Stopwatch.StartNew();
base.Start();
}
@@ -171,23 +144,51 @@ namespace Torch.Server
State = ServerState.Stopped;
IsRunning = false;
CanRun = true;
}
/// <summary>
/// Restart the program.
/// Restart the program.
/// </summary>
public override void Restart()
{
Save(0).Wait();
Stop();
LogManager.Flush();
if (IsRunning)
Save().ContinueWith(DoRestart, this, TaskContinuationOptions.RunContinuationsAsynchronously);
else
DoRestart(null, this);
var exe = Assembly.GetExecutingAssembly().Location;
((TorchConfig)Config).WaitForPID = Process.GetCurrentProcess().Id.ToString();
Config.Autostart = true;
Process.Start(exe, Config.ToString());
void DoRestart(Task<GameSaveResult> task, object torch0)
{
var torch = (TorchServer)torch0;
torch.Stop();
// TODO clone this
var config = (TorchConfig)torch.Config;
LogManager.Flush();
Process.GetCurrentProcess().Kill();
string exe = Assembly.GetExecutingAssembly().Location;
Debug.Assert(exe != null);
config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
config.Autostart = true;
Process.Start(exe, config.ToString());
Process.GetCurrentProcess().Kill();
}
}
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
{
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded)
{
_watchdog?.Dispose();
_watchdog = null;
ModCommunication.Unregister();
}
if (newState == TorchSessionState.Loaded)
{
CurrentSession.Managers.GetManager<CommandManager>().RegisterCommandModule(typeof(WhitelistCommands));
ModCommunication.Register();
}
}
/// <inheritdoc />
@@ -196,21 +197,17 @@ namespace Torch.Server
base.Init(gameInstance);
var game = gameInstance as MySandboxGame;
if (game != null && MySession.Static != null)
{
State = ServerState.Running;
// SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
}
else
{
State = ServerState.Stopped;
}
}
/// <inheritdoc />
public override void Update()
{
base.Update();
SimulationRatio = Sync.ServerSimulationRatio;
// Stops 1.00-1.02 flicker.
SimulationRatio = Math.Min(Sync.ServerSimulationRatio, 1);
var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds));
ElapsedPlayTime = elapsed;
@@ -227,7 +224,7 @@ namespace Torch.Server
private static void CheckServerResponding(object state)
{
var mre = new ManualResetEvent(false);
((TorchServer) state).Invoke(() => mre.Set());
((TorchServer)state).Invoke(() => mre.Set());
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
{
#if DEBUG
@@ -239,10 +236,8 @@ namespace Torch.Server
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.");
#endif
}
else
{
Log.Debug("Server watchdog responded");
}
Log.Debug("Server watchdog responded");
}
private static string DumpFrozenThread(Thread thread, int traces = 3, int pause = 5000)
@@ -256,6 +251,7 @@ namespace Torch.Server
stacks.Add(dump);
Thread.Sleep(pause);
}
string commonPrefix = StringUtils.CommonSuffix(stacks);
// Advance prefix to include the line terminator.
commonPrefix = commonPrefix.Substring(commonPrefix.IndexOf('\n') + 1);
@@ -269,6 +265,7 @@ namespace Torch.Server
result.AppendLine($"Suffix {i}");
result.AppendLine(stacks[i].Substring(0, stacks[i].Length - commonPrefix.Length));
}
return result.ToString();
}
@@ -282,6 +279,7 @@ namespace Torch.Server
{
// ignored
}
var stack = new StackTrace(thread, true);
try
{
@@ -291,51 +289,10 @@ namespace Torch.Server
{
// ignored
}
return stack;
}
#endregion
/// <inheritdoc/>
public override Task Save(long callerId)
{
return SaveGameAsync(statusCode => SaveCompleted(statusCode, callerId));
}
/// <summary>
/// Callback for when save has finished.
/// </summary>
/// <param name="statusCode">Return code of the save operation</param>
/// <param name="callerId">Caller of the save operation</param>
private void SaveCompleted(SaveGameStatus statusCode, long callerId = 0)
{
string response = null;
switch (statusCode)
{
case SaveGameStatus.Success:
Log.Info("Save completed.");
response = "Saved game.";
break;
case SaveGameStatus.SaveInProgress:
Log.Error("Save failed, a save is already in progress.");
response = "Save failed, a save is already in progress.";
break;
case SaveGameStatus.GameNotReady:
Log.Error("Save failed, game was not ready.");
response = "Save failed, game was not ready.";
break;
case SaveGameStatus.TimedOut:
Log.Error("Save failed, save timed out.");
response = "Save failed, save timed out.";
break;
default:
break;
}
if (MySession.Static.Players.TryGetPlayerId(callerId, out MyPlayer.PlayerId result))
{
Managers.GetManager<IChatManagerServer>()?.SendMessageAsOther("Server", response,
statusCode == SaveGameStatus.Success ? MyFontEnum.Green : MyFontEnum.Red, result.SteamId);
}
}
}
}

View File

@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Torch.Collections;
using VRage;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.ObjectBuilders;
using VRage.Serialization;
using VRageMath;
namespace Torch.Server.ViewModels
{
public class CheckpointViewModel : ViewModel
{
private MyObjectBuilder_Checkpoint _checkpoint;
private SessionSettingsViewModel _sessionSettings;
public CheckpointViewModel(MyObjectBuilder_Checkpoint checkpoint)
{
_checkpoint = checkpoint;
_sessionSettings = new SessionSettingsViewModel(_checkpoint.Settings);
}
public static implicit operator MyObjectBuilder_Checkpoint(CheckpointViewModel model)
{
return model._checkpoint;
}
public Vector3I CurrentSector { get => _checkpoint.CurrentSector; set => SetValue(ref _checkpoint.CurrentSector, value); }
public long ElapsedGameTime { get => _checkpoint.ElapsedGameTime; set => SetValue(ref _checkpoint.ElapsedGameTime, value); }
public string SessionName { get => _checkpoint.SessionName; set => SetValue(ref _checkpoint.SessionName, value); }
public MyPositionAndOrientation SpectatorPosition { get => _checkpoint.SpectatorPosition; set => SetValue(ref _checkpoint.SpectatorPosition, value); }
public bool SpectatorIsLightOn { get => _checkpoint.SpectatorIsLightOn; set => SetValue(ref _checkpoint.SpectatorIsLightOn, value); }
public MyCameraControllerEnum CameraController { get => _checkpoint.CameraController; set => SetValue(ref _checkpoint.CameraController, value); }
public long CameraEntity { get => _checkpoint.CameraEntity; set => SetValue(ref _checkpoint.CameraEntity, value); }
public long ControlledObject { get => _checkpoint.ControlledObject; set => SetValue(ref _checkpoint.ControlledObject, value); }
public string Password { get => _checkpoint.Password; set => SetValue(ref _checkpoint.Password, value); }
public string Description { get => _checkpoint.Description; set => SetValue(ref _checkpoint.Description, value); }
public DateTime LastSaveTime { get => _checkpoint.LastSaveTime; set => SetValue(ref _checkpoint.LastSaveTime, value); }
public float SpectatorDistance { get => _checkpoint.SpectatorDistance; set => SetValue(ref _checkpoint.SpectatorDistance, value); }
public ulong? WorkshopId { get => _checkpoint.WorkshopId; set => SetValue(ref _checkpoint.WorkshopId, value); }
public MyObjectBuilder_Toolbar CharacterToolbar { get => _checkpoint.CharacterToolbar; set => SetValue(ref _checkpoint.CharacterToolbar, value); }
public SerializableDictionary<long, MyObjectBuilder_Checkpoint.PlayerId> ControlledEntities { get => _checkpoint.ControlledEntities; set => SetValue(ref _checkpoint.ControlledEntities, value); }
public SessionSettingsViewModel Settings
{
get => _sessionSettings;
set
{
SetValue(ref _sessionSettings, value);
_checkpoint.Settings = _sessionSettings;
}
}
public MyObjectBuilder_ScriptManager ScriptManagerData => throw new NotImplementedException();
public int AppVersion { get => _checkpoint.AppVersion; set => SetValue(ref _checkpoint.AppVersion, value); }
public MyObjectBuilder_FactionCollection Factions => throw new NotImplementedException();
public List<MyObjectBuilder_Checkpoint.ModItem> Mods { get => _checkpoint.Mods; set => SetValue(ref _checkpoint.Mods, value); }
public SerializableDictionary<ulong, MyPromoteLevel> PromotedUsers { get => _checkpoint.PromotedUsers; set => SetValue(ref _checkpoint.PromotedUsers, value); }
public HashSet<ulong> CreativeTools { get => _checkpoint.CreativeTools; set => SetValue(ref _checkpoint.CreativeTools, value); }
public SerializableDefinitionId Scenario { get => _checkpoint.Scenario; set => SetValue(ref _checkpoint.Scenario, value); }
public List<MyObjectBuilder_Checkpoint.RespawnCooldownItem> RespawnCooldowns { get => _checkpoint.RespawnCooldowns; set => SetValue(ref _checkpoint.RespawnCooldowns, value); }
public List<MyObjectBuilder_Identity> Identities { get => _checkpoint.Identities; set => SetValue(ref _checkpoint.Identities, value); }
public List<MyObjectBuilder_Client> Clients { get => _checkpoint.Clients; set => SetValue(ref _checkpoint.Clients, value); }
public MyEnvironmentHostilityEnum? PreviousEnvironmentHostility { get => _checkpoint.PreviousEnvironmentHostility; set => SetValue(ref _checkpoint.PreviousEnvironmentHostility, value); }
public SerializableDictionary<MyObjectBuilder_Checkpoint.PlayerId, MyObjectBuilder_Player> AllPlayersData { get => _checkpoint.AllPlayersData; set => SetValue(ref _checkpoint.AllPlayersData, value); }
public SerializableDictionary<MyObjectBuilder_Checkpoint.PlayerId, List<Vector3>> AllPlayersColors { get => _checkpoint.AllPlayersColors; set => SetValue(ref _checkpoint.AllPlayersColors, value); }
public List<MyObjectBuilder_ChatHistory> ChatHistory { get => _checkpoint.ChatHistory; set => SetValue(ref _checkpoint.ChatHistory, value); }
public List<MyObjectBuilder_FactionChatHistory> FactionChatHistory { get => _checkpoint.FactionChatHistory; set => SetValue(ref _checkpoint.FactionChatHistory, value); }
public List<long> NonPlayerIdentities { get => _checkpoint.NonPlayerIdentities; set => SetValue(ref _checkpoint.NonPlayerIdentities, value); }
public SerializableDictionary<long, MyObjectBuilder_Gps> Gps { get => _checkpoint.Gps; set => SetValue(ref _checkpoint.Gps, value); }
public SerializableBoundingBoxD? WorldBoundaries { get => _checkpoint.WorldBoundaries; set => SetValue(ref _checkpoint.WorldBoundaries, value); }
public List<MyObjectBuilder_SessionComponent> SessionComponents { get => _checkpoint.SessionComponents; set => SetValue(ref _checkpoint.SessionComponents, value); }
public SerializableDefinitionId GameDefinition { get => _checkpoint.GameDefinition; set => SetValue(ref _checkpoint.GameDefinition, value); }
public HashSet<string> SessionComponentEnabled { get => _checkpoint.SessionComponentEnabled; set => SetValue(ref _checkpoint.SessionComponentEnabled, value); }
public HashSet<string> SessionComponentDisabled { get => _checkpoint.SessionComponentDisabled; set => SetValue(ref _checkpoint.SessionComponentDisabled, value); }
public DateTime InGameTime { get => _checkpoint.InGameTime; set => SetValue(ref _checkpoint.InGameTime, value); }
public MyObjectBuilder_SessionComponentMission MissionTriggers { get => _checkpoint.MissionTriggers; set => SetValue(ref _checkpoint.MissionTriggers, value); }
public string Briefing { get => _checkpoint.Briefing; set => SetValue(ref _checkpoint.Briefing, value); }
public string BriefingVideo { get => _checkpoint.BriefingVideo; set => SetValue(ref _checkpoint.BriefingVideo, value); }
public string CustomLoadingScreenImage { get => _checkpoint.CustomLoadingScreenImage; set => SetValue(ref _checkpoint.BriefingVideo, value); }
public string CustomLoadingScreenText { get => _checkpoint.CustomLoadingScreenText; set => SetValue(ref _checkpoint.CustomLoadingScreenText, value); }
public string CustomSkybox { get => _checkpoint.CustomSkybox; set => SetValue(ref _checkpoint.CustomSkybox, value); }
public int RequiresDX { get => _checkpoint.RequiresDX; set => SetValue(ref _checkpoint.RequiresDX, value); }
}
}

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using NLog;
using Sandbox.Engine.Utils;
using Torch.Collections;
using Torch.Server.Managers;
using VRage.Game;
using VRage.Game.ModAPI;
@@ -14,7 +15,7 @@ namespace Torch.Server.ViewModels
{
public class ConfigDedicatedViewModel : ViewModel
{
private static readonly Logger Log = LogManager.GetLogger("Config");
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Model => _config;
@@ -28,106 +29,108 @@ namespace Torch.Server.ViewModels
_config = configDedicated;
_config.IgnoreLastSession = true;
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
Administrators = string.Join(Environment.NewLine, _config.Administrators);
Banned = string.Join(Environment.NewLine, _config.Banned);
Mods = string.Join(Environment.NewLine, _config.Mods);
}
public void Save(string path = null)
{
var newline = new [] {Environment.NewLine};
_config.Administrators.Clear();
foreach (var admin in Administrators.Split(newline, StringSplitOptions.RemoveEmptyEntries))
_config.Administrators.Add(admin);
_config.Banned.Clear();
foreach (var banned in Banned.Split(newline, StringSplitOptions.RemoveEmptyEntries))
_config.Banned.Add(ulong.Parse(banned));
_config.Mods.Clear();
foreach (var mod in Mods.Split(newline, StringSplitOptions.RemoveEmptyEntries))
{
if (ulong.TryParse(mod, out ulong modId))
_config.Mods.Add(modId);
else
Log.Warn($"'{mod}' is not a valid mod ID.");
}
Validate();
_config.SessionSettings = _sessionSettings;
// Never ever
_config.IgnoreLastSession = true;
_config.Save(path);
}
public bool Validate()
{
if (SelectedWorld == null)
{
Log.Warn($"{nameof(SelectedWorld)} == null");
return false;
}
if (LoadWorld == null)
{
Log.Warn($"{nameof(LoadWorld)} == null");
return false;
}
return true;
}
private SessionSettingsViewModel _sessionSettings;
public SessionSettingsViewModel SessionSettings { get => _sessionSettings; set { _sessionSettings = value; OnPropertyChanged(); } }
public MtObservableList<string> WorldPaths { get; } = new MtObservableList<string>();
private string _administrators;
public string Administrators { get => _administrators; set { _administrators = value; OnPropertyChanged(); } }
private string _banned;
public string Banned { get => _banned; set { _banned = value; OnPropertyChanged(); } }
private string _mods;
public string Mods { get => _mods; set { _mods = value; OnPropertyChanged(); } }
public int AsteroidAmount
public MtObservableList<WorldViewModel> Worlds { get; } = new MtObservableList<WorldViewModel>();
private WorldViewModel _selectedWorld;
public WorldViewModel SelectedWorld
{
get { return _config.AsteroidAmount; }
set { _config.AsteroidAmount = value; OnPropertyChanged(); }
get => _selectedWorld;
set
{
SetValue(ref _selectedWorld, value);
LoadWorld = _selectedWorld?.WorldPath;
}
}
public ulong GroupId
{
get { return _config.GroupID; }
set { _config.GroupID = value; OnPropertyChanged(); }
}
public List<string> Administrators { get => _config.Administrators; set => SetValue(x => _config.Administrators = x, value); }
public string IP
{
get { return _config.IP; }
set { _config.IP = value; OnPropertyChanged(); }
}
public List<ulong> Banned { get => _config.Banned; set => SetValue(x => _config.Banned = x, value); }
public int Port
{
get { return _config.ServerPort; }
set { _config.ServerPort = value; OnPropertyChanged(); }
}
public List<ulong> Reserved { get => _config.Reserved; set => SetValue(x => _config.Reserved = x, value); }
public string ServerName
{
get { return _config.ServerName; }
set { _config.ServerName = value; OnPropertyChanged(); }
}
private List<ulong> _mods = new List<ulong>();
public List<ulong> Mods { get => _mods; set => SetValue(x => _mods = x, value); }
public bool PauseGameWhenEmpty
{
get { return _config.PauseGameWhenEmpty; }
set { _config.PauseGameWhenEmpty = value; OnPropertyChanged(); }
}
public int AsteroidAmount { get => _config.AsteroidAmount; set => SetValue(x => _config.AsteroidAmount = x, value); }
public string PremadeCheckpointPath
{
get { return _config.PremadeCheckpointPath; }
set { _config.PremadeCheckpointPath = value; OnPropertyChanged(); }
}
public ulong GroupId { get => _config.GroupID; set => SetValue(x => _config.GroupID = x, value); }
public string LoadWorld
{
get { return _config.LoadWorld; }
set { _config.LoadWorld = value; OnPropertyChanged(); }
}
public string IP { get => _config.IP; set => SetValue(x => _config.IP = x, value); }
public int SteamPort
{
get { return _config.SteamPort; }
set { _config.SteamPort = value; OnPropertyChanged(); }
}
public int Port { get => _config.ServerPort; set => SetValue(x => _config.ServerPort = x, value); }
public string WorldName
public string ServerName { get => _config.ServerName; set => SetValue(x => _config.ServerName = x, value); }
public string ServerDescription { get => _config.ServerDescription; set => SetValue(x => _config.ServerDescription = x, value); }
public bool PauseGameWhenEmpty { get => _config.PauseGameWhenEmpty; set => SetValue(x => _config.PauseGameWhenEmpty = x, value); }
public bool AutodetectDependencies { get => _config.AutodetectDependencies; set => SetValue(x => _config.AutodetectDependencies = x, value); }
public string PremadeCheckpointPath { get => _config.PremadeCheckpointPath; set => SetValue(x => _config.PremadeCheckpointPath = x, value); }
public string LoadWorld { get => _config.LoadWorld; set => SetValue(x => _config.LoadWorld = x, value); }
public int SteamPort { get => _config.SteamPort; set => SetValue(x => _config.SteamPort = x, value); }
public string WorldName { get => _config.WorldName; set => SetValue(x => _config.WorldName = x, value); }
//this is a damn server password. I don't care if this is insecure. Bite me.
private string _password;
public string Password
{
get { return _config.WorldName; }
set { _config.WorldName = value; OnPropertyChanged(); }
get
{
if (string.IsNullOrEmpty(_password))
{
if (string.IsNullOrEmpty(_config.ServerPasswordHash))
return string.Empty;
return "**********";
}
return _password;
}
set
{
_password = value;
if(!string.IsNullOrEmpty(value))
_config.SetPassword(value);
else
{
_config.ServerPasswordHash = null;
_config.ServerPasswordSalt = null;
}
}
}
}
}

View File

@@ -15,10 +15,10 @@ namespace Torch.Server.ViewModels.Blocks
{
public class BlockViewModel : EntityViewModel
{
public IMyTerminalBlock Block { get; }
public IMyTerminalBlock Block => (IMyTerminalBlock) Entity;
public MtObservableList<PropertyViewModel> Properties { get; } = new MtObservableList<PropertyViewModel>();
public string FullName => $"{Block.CubeGrid.CustomName} - {Block.CustomName}";
public string FullName => $"{Block?.CubeGrid.CustomName} - {Block?.CustomName}";
public override string Name
{
@@ -38,7 +38,7 @@ namespace Torch.Server.ViewModels.Blocks
public long BuiltBy
{
get => ((MySlimBlock)Block.SlimBlock).BuiltBy;
get => ((MySlimBlock)Block?.SlimBlock)?.BuiltBy ?? 0;
set
{
TorchBase.Instance.Invoke(() =>
@@ -59,7 +59,6 @@ namespace Torch.Server.ViewModels.Blocks
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
{
Block = block;
if (Block == null)
return;

View File

@@ -6,7 +6,12 @@ namespace Torch.Server.ViewModels.Entities
{
public CharacterViewModel(MyCharacter character, EntityTreeViewModel tree) : base(character, tree)
{
character.ControllerInfo.ControlAcquired += (x) => { OnPropertyChanged(nameof(Name)); };
character.ControllerInfo.ControlReleased += (x) => { OnPropertyChanged(nameof(Name)); };
}
public CharacterViewModel()
{
}
}
}

View File

@@ -1,7 +1,9 @@
using System.Windows.Controls;
using Sandbox.Game.World;
using Torch.API.Managers;
using Torch.Collections;
using Torch.Server.Managers;
using VRage.Game.Entity;
using VRage.Game.ModAPI;
using VRage.ModAPI;
using VRageMath;
@@ -32,23 +34,25 @@ namespace Torch.Server.ViewModels.Entities
public virtual string Name
{
get => Entity.DisplayName;
get => Entity?.DisplayName ?? (Entity != null ? $"eid:{Entity.EntityId}" : "nil");
set
{
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
if (Entity!=null)
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
OnPropertyChanged();
}
}
public virtual string Position
{
get => Entity.GetPosition().ToString();
get => Entity?.GetPosition().ToString();
set
{
if (!Vector3D.TryParse(value, out Vector3D v))
return;
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
if (Entity != null)
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
OnPropertyChanged();
}
}

View File

@@ -1,68 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Sandbox.Definitions;
using Sandbox.Game.Entities;
using Sandbox.Game.Entities.Cube;
using Sandbox.ModAPI;
using Torch.API.Managers;
using Torch.Collections;
using Torch.Server.ViewModels.Blocks;
using VRage.Game;
namespace Torch.Server.ViewModels.Entities
{
public class GridViewModel : EntityViewModel, ILazyLoad
{
private MyCubeGrid Grid => (MyCubeGrid)Entity;
public MtObservableList<BlockViewModel> Blocks { get; } = new MtObservableList<BlockViewModel>();
private static readonly MyCubeBlockDefinition _fillerDefinition = new MyCubeBlockDefinition()
{
Id = new MyDefinitionId(typeof(MyObjectBuilder_DefinitionBase), "")
};
private class CubeBlockDefinitionComparer : IComparer<MyCubeBlockDefinition>
{
public static readonly CubeBlockDefinitionComparer Default = new CubeBlockDefinitionComparer();
public int Compare(MyCubeBlockDefinition x, MyCubeBlockDefinition y)
{
if (x == null && y == null)
return 0;
if (x == null)
return -1;
if (y == null)
return 1;
if (ReferenceEquals(x, y))
return 0;
MyDefinitionId xi = x.Id;
MyDefinitionId yi = y.Id;
if (xi == yi)
return 0;
if (xi.TypeId != yi.TypeId)
return string.CompareOrdinal(((Type) xi.TypeId).Name, ((Type) yi.TypeId).Name);
return xi.SubtypeId == yi.SubtypeId ? 0 : string.CompareOrdinal(xi.SubtypeName, yi.SubtypeName);
}
}
private MyCubeGrid Grid => (MyCubeGrid) Entity;
public MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>
Blocks { get; } =
new MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>(
CubeBlockDefinitionComparer.Default);
/// <inheritdoc />
public string DescriptiveName { get; }
public GridViewModel() { }
public GridViewModel()
{
}
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
{
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
Blocks.Add(new BlockViewModel(null, Tree));
Blocks.Add(_fillerDefinition, new MtObservableSortedDictionary<long, BlockViewModel>());
}
private void Grid_OnBlockRemoved(Sandbox.Game.Entities.Cube.MySlimBlock obj)
{
if (obj.FatBlock != null)
Blocks.RemoveWhere(b => b.Block.EntityId == obj.FatBlock?.EntityId);
RemoveBlock(obj.FatBlock);
OnPropertyChanged(nameof(Name));
}
private void RemoveBlock(MyCubeBlock block)
{
if (!Blocks.TryGetValue(block.BlockDefinition, out var group))
return;
if (group.Remove(block.EntityId) && group.Count == 0 && Blocks.Count > 1)
Blocks.Remove(block.BlockDefinition);
}
private void AddBlock(MyTerminalBlock block)
{
if (!Blocks.TryGetValue(block.BlockDefinition, out var group))
group = Blocks[block.BlockDefinition] = new MtObservableSortedDictionary<long, BlockViewModel>();
group.Add(block.EntityId, new BlockViewModel(block, Tree));
}
private void Grid_OnBlockAdded(Sandbox.Game.Entities.Cube.MySlimBlock obj)
{
var block = obj.FatBlock as IMyTerminalBlock;
var block = obj.FatBlock as MyTerminalBlock;
if (block != null)
Blocks.Add(new BlockViewModel(block, Tree));
AddBlock(block);
OnPropertyChanged(nameof(Name));
}
private bool _load;
public void Load()
{
if (_load)
return;
_load = true;
Blocks.Clear();
TorchBase.Instance.Invoke(() =>
{
foreach (var block in Grid.GetFatBlocks().Where(b => b is IMyTerminalBlock))
{
Blocks.Add(new BlockViewModel((IMyTerminalBlock)block, Tree));
}
Blocks.Clear();
foreach (var block in Grid.GetFatBlocks().OfType<MyTerminalBlock>())
AddBlock(block);
Grid.OnBlockAdded += Grid_OnBlockAdded;
Grid.OnBlockRemoved += Grid_OnBlockRemoved;
Tree.ControlDispatcher.BeginInvoke(() =>
{
Blocks.Sort(b => b.Block.CustomName);
});
});
}
}

View File

@@ -29,11 +29,10 @@ namespace Torch.Server.ViewModels.Entities
await TorchBase.Instance.InvokeAsync(() => MyEntities.GetTopMostEntitiesInBox(ref box, entities)).ConfigureAwait(false);
foreach (var entity in entities.Where(e => e is IMyCubeGrid))
{
var gridModel = Tree.Grids.FirstOrDefault(g => g.Entity.EntityId == entity.EntityId);
if (gridModel == null)
if (Tree.Grids.TryGetValue(entity.EntityId, out var gridModel))
{
gridModel = new GridViewModel((MyCubeGrid)entity, Tree);
Tree.Grids.Add(gridModel);
Tree.Grids.Add(entity.EntityId, gridModel);
}
AttachedGrids.Add(gridModel);

View File

@@ -20,10 +20,10 @@ namespace Torch.Server.ViewModels
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
//TODO: these should be sorted sets for speed
public MtObservableList<GridViewModel> Grids { get; set; } = new MtObservableList<GridViewModel>();
public MtObservableList<CharacterViewModel> Characters { get; set; } = new MtObservableList<CharacterViewModel>();
public MtObservableList<EntityViewModel> FloatingObjects { get; set; } = new MtObservableList<EntityViewModel>();
public MtObservableList<VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableList<VoxelMapViewModel>();
public MtObservableSortedDictionary<long, GridViewModel> Grids { get; set; } = new MtObservableSortedDictionary<long, GridViewModel>();
public MtObservableSortedDictionary<long, CharacterViewModel> Characters { get; set; } = new MtObservableSortedDictionary<long, CharacterViewModel>();
public MtObservableSortedDictionary<long, EntityViewModel> FloatingObjects { get; set; } = new MtObservableSortedDictionary<long, EntityViewModel>();
public MtObservableSortedDictionary<long, VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableSortedDictionary<long, VoxelMapViewModel>();
public Dispatcher ControlDispatcher => _control.Dispatcher;
private EntityViewModel _currentEntity;
@@ -35,6 +35,11 @@ namespace Torch.Server.ViewModels
set { _currentEntity = value; OnPropertyChanged(nameof(CurrentEntity)); }
}
// I hate you today WPF
public EntityTreeViewModel() : this(null)
{
}
public EntityTreeViewModel(UserControl control)
{
_control = control;
@@ -53,16 +58,16 @@ namespace Torch.Server.ViewModels
switch (obj)
{
case MyCubeGrid grid:
Grids.RemoveWhere(m => m.Id == grid.EntityId);
Grids.Remove(grid.EntityId);
break;
case MyCharacter character:
Characters.RemoveWhere(m => m.Id == character.EntityId);
Characters.Remove(character.EntityId);
break;
case MyFloatingObject floating:
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
FloatingObjects.Remove(floating.EntityId);
break;
case MyVoxelBase voxel:
VoxelMaps.RemoveWhere(m => m.Id == voxel.EntityId);
VoxelMaps.Remove(voxel.EntityId);
break;
}
}
@@ -80,16 +85,16 @@ namespace Torch.Server.ViewModels
switch (obj)
{
case MyCubeGrid grid:
Grids.Add(new GridViewModel(grid, this));
Grids.Add(obj.EntityId, new GridViewModel(grid, this));
break;
case MyCharacter character:
Characters.Add(new CharacterViewModel(character, this));
Characters.Add(obj.EntityId, new CharacterViewModel(character, this));
break;
case MyFloatingObject floating:
FloatingObjects.Add(new FloatingObjectViewModel(floating, this));
FloatingObjects.Add(obj.EntityId, new FloatingObjectViewModel(floating, this));
break;
case MyVoxelBase voxel:
VoxelMaps.Add(new VoxelMapViewModel(voxel, this));
VoxelMaps.Add(obj.EntityId, new VoxelMapViewModel(voxel, this));
break;
}
}

View File

@@ -3,32 +3,40 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Torch.API;
using Torch.API.Plugins;
using Torch.Server.Views;
namespace Torch.Server.ViewModels
{
public class PluginViewModel
{
public UserControl Control
{
get
{
if (Plugin is IWpfPlugin p)
return p.GetControl();
return null;
}
}
public UserControl Control { get; }
public string Name { get; }
public ITorchPlugin Plugin { get; }
public PluginViewModel(ITorchPlugin plugin)
{
Plugin = plugin;
if (Plugin is IWpfPlugin p)
Control = p.GetControl();
Name = $"{plugin.Name} ({plugin.Version})";
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
if (this.Control == null)
return;
this.Control.Resources.MergedDictionaries.Clear();
this.Control.Resources.MergedDictionaries.Add(dictionary);
}
}
}

View File

@@ -1,387 +1,267 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX.Toolkit.Collections;
using Torch;
using Torch.Collections;
using Torch.Views;
using VRage.Game;
using VRage.Library.Utils;
using VRage.Serialization;
namespace Torch.Server.ViewModels
{
/// <summary>
/// View model for <see cref="MyObjectBuilder_SessionSettings"/>
/// </summary>
public class SessionSettingsViewModel : ViewModel
{
private MyObjectBuilder_SessionSettings _settings;
/// <summary>
/// Creates a new view model with a new <see cref="MyObjectBuilder_SessionSettings"/> object.
/// </summary>
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
{
[Torch.Views.Display(Description = "The type of the game mode.", Name = "Game Mode", GroupName = "Others")]
public MyGameModeEnum GameMode { get => _settings.GameMode; set => SetValue(ref _settings.GameMode, value); }
[Torch.Views.Display(Description = "The type of the game online mode.", Name = "Online Mode", GroupName = "Others")]
public MyOnlineModeEnum OnlineMode { get => _settings.OnlineMode; set => SetValue(ref _settings.OnlineMode, value); }
}
[Torch.Views.Display(Description = "The multiplier for inventory size.", Name = "Inventory Size", GroupName = "Multipliers")]
public float InventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for assembler speed.", Name = "Assembler Speed", GroupName = "Multipliers")]
public float AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for assembler efficiency.", Name = "Assembler Efficiency", GroupName = "Multipliers")]
public float AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for refinery speed.", Name = "Refinery Speed", GroupName = "Multipliers")]
public float RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); }
[Torch.Views.Display(Description = "The maximum number of connected players.", Name = "Max Players", GroupName = "Players")]
public short MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); }
[Torch.Views.Display(Description = "The maximum number of existing floating objects.", Name = "Max Floating Objects", GroupName = "Environment")]
public short MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); }
[Torch.Views.Display(Description = "The maximum number of backup saves.", Name = "Max Backup Saves", GroupName = "Others")]
public short MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); }
[Torch.Views.Display(Description = "The maximum number of blocks in one grid.", Name = "Max Grid Blocks", GroupName = "Block Limits")]
public int MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); }
[Torch.Views.Display(Description = "The maximum number of blocks per player.", Name = "Max Blocks per Player", GroupName = "Block Limits")]
public int MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); }
[Torch.Views.Display(Description = "The total number of Performance Cost Units in the world.", Name = "World PCU", GroupName = "Block Limits")]
public int TotalPCU { get => _settings.TotalPCU; set => SetValue(ref _settings.TotalPCU, value); }
[Torch.Views.Display(Description = "The maximum number of existing factions in the world.", Name = "Max Factions Count", GroupName = "Block Limits")]
public int MaxFactionsCount { get => _settings.MaxFactionsCount; set => SetValue(ref _settings.MaxFactionsCount, value); }
[Torch.Views.Display(Description = "Defines block limits mode.", Name = "Block Limits Mode", GroupName = "Block Limits")]
public MyBlockLimitsEnabledEnum BlockLimitsEnabled { get => _settings.BlockLimitsEnabled; set => SetValue(ref _settings.BlockLimitsEnabled, value); }
[Torch.Views.Display(Description = "Enables possibility to remove grid remotely from the world by an author.", Name = "Enable Remote Grid Removal", GroupName = "Others")]
public bool EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); }
[Torch.Views.Display(Description = "Defines hostility of the environment.", Name = "Environment Hostility", GroupName = "Environment")]
public MyEnvironmentHostilityEnum EnvironmentHostility { get => _settings.EnvironmentHostility; set => SetValue(ref _settings.EnvironmentHostility, value); }
[Torch.Views.Display(Description = "Enables auto healing of the character.", Name = "Auto Healing", GroupName = "Players")]
public bool AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); }
[Torch.Views.Display(Description = "Enables copy and paste feature.", Name = "Enable Copy & Paste", GroupName = "Players")]
public bool EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); }
[Torch.Views.Display(Description = "Enables weapons.", Name = "Enable Weapons", GroupName = "Others")]
public bool WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); }
[Torch.Views.Display(Description = "", Name = "Show Player Names on HUD", GroupName = "Players")]
public bool ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); }
[Torch.Views.Display(Description = "Enables thruster damage.", Name = "Enable Thruster Damage", GroupName = "Others")]
public bool ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); }
[Torch.Views.Display(Description = "Enables spawning of cargo ships.", Name = "Enable Cargo Ships", GroupName = "NPCs")]
public bool CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); }
[Torch.Views.Display(Description = "Enables spectator camera.", Name = "Enable Spectator Camera", GroupName = "Others")]
public bool EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); }
/// <summary>
/// Creates a view model using an existing <see cref="MyObjectBuilder_SessionSettings"/> object.
/// Size of the edge of the world area cube.
/// Don't use directly, as it is error-prone (it's km instead of m and edge size instead of half-extent)
/// Rather use MyEntities.WorldHalfExtent()
/// </summary>
[Torch.Views.Display(Description = "Defines the size of the world.", Name = "World Size [km]", GroupName = "Environment")]
public int WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); }
[Torch.Views.Display(Description = "When enabled respawn ship is removed after player logout.", Name = "Remove Respawn Ships on Logoff", GroupName = "Others")]
public bool RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); }
[Torch.Views.Display(Description = "", Name = "Reset Ownership", GroupName = "Players")]
public bool ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); }
[Torch.Views.Display(Description = "The multiplier for welder speed.", Name = "Welder Speed", GroupName = "Multipliers")]
public float WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); }
[Torch.Views.Display(Description = "The multiplier for grinder speed.", Name = "Grinder Speed", GroupName = "Multipliers")]
public float GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); }
[Torch.Views.Display(Description = "Enables realistic sounds.", Name = "Enable Realistic Sound", GroupName = "Environment")]
public bool RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); }
[Torch.Views.Display(Description = "The multiplier for hacking speed.", Name = "Hacking Speed", GroupName = "Multipliers")]
public float HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); }
[Torch.Views.Display(Description = "Enables permanent death.", Name = "Permanent Death", GroupName = "Players")]
public bool? PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); }
[Torch.Views.Display(Description = "Defines autosave interval.", Name = "Autosave Interval [mins]", GroupName = "Others")]
public uint AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); }
[Torch.Views.Display(Description = "Enables saving from the menu.", Name = "Enable Saving from Menu", GroupName = "Others")]
public bool EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); }
[Torch.Views.Display(Description = "Enables respawn screen.", Name = "Enable Respawn Screen in the Game", GroupName = "Players")]
public bool EnableRespawnScreen { get => _settings.EnableRespawnScreen; set => SetValue(ref _settings.EnableRespawnScreen, value); }
[Torch.Views.Display(Description = "Enables infinite ammunition in survival game mode.", Name = "Enable Infinite Ammunition in Survival", GroupName = "Others")]
public bool InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); }
[Torch.Views.Display(Description = "Enables drop containers (unknown signals).", Name = "Enable Drop Containers", GroupName = "Others")]
public bool EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); }
[Torch.Views.Display(Description = "The multiplier for respawn ship timer.", Name = "Respawn Ship Time Multiplier", GroupName = "Players")]
public float SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); }
[Torch.Views.Display(Description = "Defines density of the procedurally generated content.", Name = "Procedural Density", GroupName = "Environment")]
public float ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); }
[Torch.Views.Display(Description = "Defines unique starting seed for the procedurally generated content.", Name = "Procedural Seed", GroupName = "Environment")]
public int ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); }
[Torch.Views.Display(Description = "Enables destruction feature for the blocks.", Name = "Enable Destructible Blocks", GroupName = "Environment")]
public bool DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); }
[Torch.Views.Display(Description = "Enables in game scripts.", Name = "Enable Ingame Scripts", GroupName = "Others")]
public bool EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); }
[Torch.Views.Display(Description = "", Name = "Flora Density Multiplier", GroupName = "Environment")]
public float FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); }
[Torch.Views.Display(Description = "Enables tool shake feature.", Name = "Enable Tool Shake", GroupName = "Players")]
[DefaultValue(false)]
public bool EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); }
[Torch.Views.Display(Description = "", Name = "Voxel Generator Version", GroupName = "Environment")]
public int VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); }
[Torch.Views.Display(Description = "Enables oxygen in the world.", Name = "Enable Oxygen", GroupName = "Environment")]
public bool EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); }
[Torch.Views.Display(Description = "Enables airtightness in the world.", Name = "Enable Airtightness", GroupName = "Environment")]
public bool EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); }
[Torch.Views.Display(Description = "Enables 3rd person camera.", Name = "Enable 3rd Person Camera", GroupName = "Players")]
public bool Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); }
[Torch.Views.Display(Description = "Enables random encounters in the world.", Name = "Enable Encounters", GroupName = "NPCs")]
public bool EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); }
[Torch.Views.Display(Description = "Enables possibility of converting grid to station.", Name = "Enable Convert to Station", GroupName = "Others")]
public bool EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); }
[Torch.Views.Display(Description = "Enables possibility of station grid inside voxel.", Name = "Enable Station Grid with Voxel", GroupName = "Environment")]
public bool StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); }
[Torch.Views.Display(Description = "Enables sun rotation.", Name = "Enable Sun Rotation", GroupName = "Environment")]
public bool EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); }
[Torch.Views.Display(Description = "Enables respawn ships.", Name = "Enable Respawn Ships", GroupName = "Others")]
public bool EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); }
[Torch.Views.Display(Description = "", Name = "Physics Iterations", GroupName = "Environment")]
public int PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); }
[Torch.Views.Display(Description = "Defines interval of one rotation of the sun.", Name = "Sun Rotation Interval", GroupName = "Environment")]
public float SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); }
[Torch.Views.Display(Description = "Enables jetpack.", Name = "Enable Jetpack", GroupName = "Players")]
public bool EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); }
[Torch.Views.Display(Description = "Enables spawning with tools in the inventory.", Name = "Spawn with Tools", GroupName = "Players")]
public bool SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); }
[Torch.Views.Display(Description = "Enables voxel destructions.", Name = "Enable Voxel Destruction", GroupName = "Environment")]
public bool EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); }
[Torch.Views.Display(Description = "Enables spawning of drones in the world.", Name = "Enable Drones", GroupName = "NPCs")]
public bool EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); }
[Torch.Views.Display(Description = "Enables spawning of wolves in the world.", Name = "Enable Wolves", GroupName = "NPCs")]
public bool EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); }
[Torch.Views.Display(Description = "Enables spawning of spiders in the world.", Name = "Enable Spiders", GroupName = "NPCs")]
public bool EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); }
[Torch.Views.Display(Name = "Block Type World Limits", GroupName = "Block Limits")]
public Dictionary<string, short> BlockTypeLimits { get => _settings.BlockTypeLimits.Dictionary; set => SetValue(x => _settings.BlockTypeLimits.Dictionary = x, value); }
[Torch.Views.Display(Description = "Enables scripter role for administration.", Name = "Enable Scripter Role", GroupName = "Others")]
public bool EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); }
[Torch.Views.Display(Description = "Defines minimum respawn time for drop containers.", Name = "Min Drop Container Respawn Time", GroupName = "Others")]
public int MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); }
[Torch.Views.Display(Description = "Defines maximum respawn time for drop containers.", Name = "Max Drop Container Respawn Time", GroupName = "Others")]
public int MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); }
[Torch.Views.Display(Description = "Enables friendly fire for turrets.", Name = "Enable Turrets Friendly Fire", GroupName = "Environment")]
public bool EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); }
[Torch.Views.Display(Description = "Enables sub-grid damage.", Name = "Enable Sub-Grid Damage", GroupName = "Environment")]
public bool EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); }
[Torch.Views.Display(Description = "Defines synchronization distance in multiplayer. High distance can slow down server drastically. Use with caution.", Name = "Sync Distance", GroupName = "Environment")]
public int SyncDistance { get => _settings.SyncDistance; set => SetValue(ref _settings.SyncDistance, value); }
[Torch.Views.Display(Description = "Defines render distance for clients in multiplayer. High distance can slow down client FPS. Values larger than SyncDistance may not work as expected.", Name = "View Distance", GroupName = "Environment")]
public int ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value);}
[Torch.Views.Display(Description = "Enables experimental mode.", Name = "Experimental Mode", GroupName = "Others")]
public bool ExperimentalMode { get => _settings.ExperimentalMode; set => SetValue(ref _settings.ExperimentalMode, value); }
[Torch.Views.Display(Description = "Enables adaptive simulation quality system. This system is useful if you have a lot of voxel deformations in the world and low simulation speed.", Name = "Adaptive Simulation Quality", GroupName = "Others")]
public bool AdaptiveSimulationQuality { get => _settings.AdaptiveSimulationQuality; set => SetValue(ref _settings.AdaptiveSimulationQuality, value); }
[Torch.Views.Display(Description = "Enables voxel hand.", Name = "Enable voxel hand", GroupName = "Others")]
public bool EnableVoxelHand { get => _settings.EnableVoxelHand; set => SetValue(ref _settings.EnableVoxelHand, value); }
[Torch.Views.Display(Description = "Enables trash removal system.", Name = "Trash Removal Enabled", GroupName = "Trash Removal")]
public bool TrashRemovalEnabled { get => _settings.TrashRemovalEnabled; set => SetValue(ref _settings.TrashRemovalEnabled, value); }
[Torch.Views.Display(Description = "Defines flags for trash removal system.", Name = "Trash Removal Flags", GroupName = "Trash Removal")]
public MyTrashRemovalFlags TrashFlagsValue { get => (MyTrashRemovalFlags)_settings.TrashFlagsValue; set => SetValue(ref _settings.TrashFlagsValue, (int)value); }
[Torch.Views.Display(Description = "Defines block count threshold for trash removal system.", Name = "Block Count Threshold", GroupName = "Trash Removal")]
public int BlockCountThreshold { get => _settings.BlockCountThreshold; set => SetValue(ref _settings.BlockCountThreshold, value); }
[Torch.Views.Display(Description = "Defines player distance threshold for trash removal system.", Name = "Player Distance Threshold [m]", GroupName = "Trash Removal")]
public float PlayerDistanceThreshold { get => _settings.PlayerDistanceThreshold; set => SetValue(ref _settings.PlayerDistanceThreshold, value); }
[Torch.Views.Display(Description = "By setting this, server will keep number of grids around this value. \n !WARNING! It ignores Powered and Fixed flags, Block Count and lowers Distance from player.\n Set to 0 to disable.", Name = "Optimal Grid Count", GroupName = "Trash Removal")]
public int OptimalGridCount { get => _settings.OptimalGridCount; set => SetValue(ref _settings.OptimalGridCount, value); }
[Torch.Views.Display(Description = "Defines player inactivity threshold for trash removal system. \n !WARNING! This will remove all grids of the player.\n Set to 0 to disable.", Name = "Player Inactivity Threshold [hours]", GroupName = "Trash Removal")]
public float PlayerInactivityThreshold { get => _settings.PlayerInactivityThreshold; set => SetValue(ref _settings.PlayerInactivityThreshold, value); }
[Torch.Views.Display(Description = "Defines character removal threshold for trash removal system. If player disconnects it will remove his character after this time.\n Set to 0 to disable.", Name = "Character Removal Threshold [mins]", GroupName = "Trash Removal")]
public int PlayerCharacterRemovalThreshold { get => _settings.PlayerCharacterRemovalThreshold; set => SetValue(ref _settings.PlayerCharacterRemovalThreshold, value); }
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
{
_settings = settings;
foreach (var limit in settings.BlockTypeLimits.Dictionary)
BlockLimits.Add(new BlockLimitViewModel(this, limit.Key, limit.Value));
}
public MtObservableList<BlockLimitViewModel> BlockLimits { get; } = new MtObservableList<BlockLimitViewModel>();
#region Multipliers
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.InventorySizeMultiplier"/>
public float InventorySizeMultiplier
{
get => _settings.InventorySizeMultiplier; set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.RefinerySpeedMultiplier"/>
public float RefinerySpeedMultiplier
{
get => _settings.RefinerySpeedMultiplier; set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AssemblerEfficiencyMultiplier"/>
public float AssemblerEfficiencyMultiplier
{
get => _settings.AssemblerEfficiencyMultiplier; set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AssemblerSpeedMultiplier"/>
public float AssemblerSpeedMultiplier
{
get => _settings.AssemblerSpeedMultiplier; set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.GrinderSpeedMultiplier"/>
public float GrinderSpeedMultiplier
{
get => _settings.GrinderSpeedMultiplier; set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.HackSpeedMultiplier"/>
public float HackSpeedMultiplier
{
get => _settings.HackSpeedMultiplier; set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.WelderSpeedMultiplier"/>
public float WelderSpeedMultiplier
{
get => _settings.WelderSpeedMultiplier; set { _settings.WelderSpeedMultiplier = value; OnPropertyChanged(); }
}
#endregion
#region NPCs
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableDrones"/>
public bool EnableDrones
{
get => _settings.EnableDrones; set { _settings.EnableDrones = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableEncounters"/>
public bool EnableEncounters
{
get => _settings.EnableEncounters; set { _settings.EnableEncounters = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableSpiders"/>
public bool EnableSpiders
{
get => _settings.EnableSpiders; set { _settings.EnableSpiders = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableWolfs"/>
public bool EnableWolves
{
get => _settings.EnableWolfs; set { _settings.EnableWolfs = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.CargoShipsEnabled"/>
public bool EnableCargoShips
{
get => _settings.CargoShipsEnabled; set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
}
#endregion
#region Environment
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableSunRotation"/>
public bool EnableSunRotation
{
get => _settings.EnableSunRotation; set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableOxygenPressurization"/>
public bool EnableAirtightness
{
get => _settings.EnableOxygenPressurization; set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableOxygen"/>
public bool EnableOxygen
{
get => _settings.EnableOxygen; set { _settings.EnableOxygen = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.DestructibleBlocks"/>
public bool EnableDestructibleBlocks
{
get => _settings.DestructibleBlocks; set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableToolShake"/>
public bool EnableToolShake
{
get => _settings.EnableToolShake; set { _settings.EnableToolShake = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableVoxelDestruction"/>
public bool EnableVoxelDestruction
{
get => _settings.EnableVoxelDestruction; set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
}
/// <summary>
/// List used to populate the environment hostility combo box.
/// </summary>
public List<string> EnvironmentHostilityValues { get; } = Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList();
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnvironmentHostility"/>
public string EnvironmentHostility
{
get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableFlora"/>
public bool EnableFlora
{
get => _settings.EnableFlora; set { _settings.EnableFlora = value; OnPropertyChanged(); }
}
#endregion
/// <summary>
/// List used to populate the game mode combobox.
/// </summary>
public List<string> GameModeValues { get; } = Enum.GetNames(typeof(MyGameModeEnum)).ToList();
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.GameMode"/>
public string GameMode
{
get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AutoHealing"/>
public bool EnableAutoHealing
{
get => _settings.AutoHealing; set { _settings.AutoHealing = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableCopyPaste"/>
public bool EnableCopyPaste
{
get => _settings.EnableCopyPaste; set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ShowPlayerNamesOnHud"/>
public bool ShowPlayerNamesOnHud
{
get => _settings.ShowPlayerNamesOnHud; set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.Enable3rdPersonView"/>
public bool EnableThirdPerson
{
get => _settings.Enable3rdPersonView; set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableSpectator"/>
public bool EnableSpectator
{
get => _settings.EnableSpectator; set { _settings.EnableSpectator = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.SpawnWithTools"/>
public bool SpawnWithTools
{
get => _settings.SpawnWithTools; set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableConvertToStation"/>
public bool EnableConvertToStation
{
get => _settings.EnableConvertToStation; set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableJetpack"/>
public bool EnableJetpack
{
get => _settings.EnableJetpack; set { _settings.EnableJetpack = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableRemoteBlockRemoval"/>
public bool EnableRemoteOwnerRemoval
{
get => _settings.EnableRemoteBlockRemoval; set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableRespawnShips"/>
public bool EnableRespawnShips
{
get => _settings.EnableRespawnShips; set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableScripterRole"/>
public bool EnableScripterRole
{
get => _settings.EnableScripterRole; set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.RealisticSound"/>
public bool EnableRealisticSound
{
get => _settings.RealisticSound; set { _settings.RealisticSound = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ResetOwnership"/>
public bool ResetOwnership
{
get => _settings.ResetOwnership; set { _settings.ResetOwnership = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.RespawnShipDelete"/>
public bool DeleteRespawnShips
{
get => _settings.RespawnShipDelete; set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ThrusterDamage"/>
public bool EnableThrusterDamage
{
get => _settings.ThrusterDamage; set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.WeaponsEnabled"/>
public bool EnableWeapons
{
get => _settings.WeaponsEnabled; set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableIngameScripts"/>
public bool EnableIngameScripts
{
get => _settings.EnableIngameScripts; set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AutoSaveInMinutes"/>
public uint AutosaveInterval
{
get => _settings.AutoSaveInMinutes; set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.FloraDensity"/>
public int FloraDensity
{
get => _settings.FloraDensity; set { _settings.FloraDensity = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.FloraDensityMultiplier"/>
public float FloraDensityMultiplier
{
get => _settings.FloraDensityMultiplier; set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxBackupSaves"/>
public short MaxBackupSaves
{
get => _settings.MaxBackupSaves; set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxBlocksPerPlayer"/>
public int MaxBlocksPerPlayer
{
get => _settings.MaxBlocksPerPlayer; set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxFloatingObjects"/>
public short MaxFloatingObjects
{
get => _settings.MaxFloatingObjects; set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxGridSize"/>
public int MaxGridSize
{
get => _settings.MaxGridSize; set { _settings.MaxGridSize = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxPlayers"/>
public short MaxPlayers
{
get => _settings.MaxPlayers; set { _settings.MaxPlayers = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.PhysicsIterations"/>
public int PhysicsIterations
{
get => _settings.PhysicsIterations; set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.SpawnShipTimeMultiplier"/>
public float SpawnTimeMultiplier
{
get => _settings.SpawnShipTimeMultiplier; set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.SunRotationIntervalMinutes"/>
public float SunRotationInterval
{
get => _settings.SunRotationIntervalMinutes; set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ViewDistance"/>
public int ViewDistance
{
get => _settings.ViewDistance; set { _settings.ViewDistance = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.WorldSizeKm"/>
public int WorldSize
{
get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ProceduralDensity"/>
public float ProceduralDensity
{
get => _settings.ProceduralDensity; set { _settings.ProceduralDensity = value; OnPropertyChanged(); }
}
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ProceduralSeed"/>
public int ProceduralSeed
{
get => _settings.ProceduralSeed;
set { _settings.ProceduralSeed = value; OnPropertyChanged(); }
}
/// <summary />
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
{
viewModel._settings.BlockTypeLimits.Dictionary.Clear();
foreach (var limit in viewModel.BlockLimits)
viewModel._settings.BlockTypeLimits.Dictionary.Add(limit.BlockType, limit.Limit);
return viewModel._settings;
}
}

View File

@@ -0,0 +1,253 @@
// This file is generated automatically! Any changes will be overwritten.
using System;
using System.Collections.Generic;
using System.Linq;
using Torch;
using Torch.Collections;
using VRage.Game;
using VRage.Library.Utils;
using VRage.Serialization;
namespace Torch.Server.ViewModels
{
public class SessionSettingsViewModel : ViewModel
{
private MyObjectBuilder_SessionSettings _settings;
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.GameMode" />
public string GameMode { get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out VRage.Library.Utils.MyGameModeEnum parsedVal); SetValue(ref _settings.GameMode, parsedVal); } }
public List<string> GameModeValues { get; } = new List<string> {"Creative", "Survival"};
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.InventorySizeMultiplier" />
public System.Single InventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AssemblerSpeedMultiplier" />
public System.Single AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AssemblerEfficiencyMultiplier" />
public System.Single AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.RefinerySpeedMultiplier" />
public System.Single RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.OnlineMode" />
public string OnlineMode { get => _settings.OnlineMode.ToString(); set { Enum.TryParse(value, true, out VRage.Game.MyOnlineModeEnum parsedVal); SetValue(ref _settings.OnlineMode, parsedVal); } }
public List<string> OnlineModeValues { get; } = new List<string> {"OFFLINE", "PUBLIC", "FRIENDS", "PRIVATE"};
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxPlayers" />
public System.Int16 MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxFloatingObjects" />
public System.Int16 MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxBackupSaves" />
public System.Int16 MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxGridSize" />
public System.Int32 MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxBlocksPerPlayer" />
public System.Int32 MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableBlockLimits" />
public System.Boolean EnableBlockLimits { get => _settings.EnableBlockLimits; set => SetValue(ref _settings.EnableBlockLimits, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableRemoteBlockRemoval" />
public System.Boolean EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnvironmentHostility" />
public string EnvironmentHostility { get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out VRage.Game.MyEnvironmentHostilityEnum parsedVal); SetValue(ref _settings.EnvironmentHostility, parsedVal); } }
public List<string> EnvironmentHostilityValues { get; } = new List<string> {"SAFE", "NORMAL", "CATACLYSM", "CATACLYSM_UNREAL"};
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AutoHealing" />
public System.Boolean AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableCopyPaste" />
public System.Boolean EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.WeaponsEnabled" />
public System.Boolean WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ShowPlayerNamesOnHud" />
public System.Boolean ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ThrusterDamage" />
public System.Boolean ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.CargoShipsEnabled" />
public System.Boolean CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSpectator" />
public System.Boolean EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.WorldSizeKm" />
public System.Int32 WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.RespawnShipDelete" />
public System.Boolean RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ResetOwnership" />
public System.Boolean ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.WelderSpeedMultiplier" />
public System.Single WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.GrinderSpeedMultiplier" />
public System.Single GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.RealisticSound" />
public System.Boolean RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.HackSpeedMultiplier" />
public System.Single HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.PermanentDeath" />
public System.Nullable<System.Boolean> PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AutoSaveInMinutes" />
public System.UInt32 AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSaving" />
public System.Boolean EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableRespawnScreen" />
public System.Boolean EnableRespawnScreen { get => _settings.EnableRespawnScreen; set => SetValue(ref _settings.EnableRespawnScreen, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.InfiniteAmmo" />
public System.Boolean InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableContainerDrops" />
public System.Boolean EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.SpawnShipTimeMultiplier" />
public System.Single SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ProceduralDensity" />
public System.Single ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ProceduralSeed" />
public System.Int32 ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.DestructibleBlocks" />
public System.Boolean DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableIngameScripts" />
public System.Boolean EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ViewDistance" />
public System.Int32 ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.FloraDensity" />
public System.Int32 FloraDensity { get => _settings.FloraDensity; set => SetValue(ref _settings.FloraDensity, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableToolShake" />
public System.Boolean EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.VoxelGeneratorVersion" />
public System.Int32 VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableOxygen" />
public System.Boolean EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableOxygenPressurization" />
public System.Boolean EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.Enable3rdPersonView" />
public System.Boolean Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableEncounters" />
public System.Boolean EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableFlora" />
public System.Boolean EnableFlora { get => _settings.EnableFlora; set => SetValue(ref _settings.EnableFlora, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableConvertToStation" />
public System.Boolean EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.StationVoxelSupport" />
public System.Boolean StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSunRotation" />
public System.Boolean EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableRespawnShips" />
public System.Boolean EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ScenarioEditMode" />
public System.Boolean ScenarioEditMode { get => _settings.ScenarioEditMode; set => SetValue(ref _settings.ScenarioEditMode, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.Scenario" />
public System.Boolean Scenario { get => _settings.Scenario; set => SetValue(ref _settings.Scenario, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.CanJoinRunning" />
public System.Boolean CanJoinRunning { get => _settings.CanJoinRunning; set => SetValue(ref _settings.CanJoinRunning, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.PhysicsIterations" />
public System.Int32 PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.SunRotationIntervalMinutes" />
public System.Single SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableJetpack" />
public System.Boolean EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.SpawnWithTools" />
public System.Boolean SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.StartInRespawnScreen" />
public System.Boolean StartInRespawnScreen { get => _settings.StartInRespawnScreen; set => SetValue(ref _settings.StartInRespawnScreen, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableVoxelDestruction" />
public System.Boolean EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxDrones" />
public System.Int32 MaxDrones { get => _settings.MaxDrones; set => SetValue(ref _settings.MaxDrones, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableDrones" />
public System.Boolean EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableWolfs" />
public System.Boolean EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSpiders" />
public System.Boolean EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.FloraDensityMultiplier" />
public System.Single FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableStructuralSimulation" />
public System.Boolean EnableStructuralSimulation { get => _settings.EnableStructuralSimulation; set => SetValue(ref _settings.EnableStructuralSimulation, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxActiveFracturePieces" />
public System.Int32 MaxActiveFracturePieces { get => _settings.MaxActiveFracturePieces; set => SetValue(ref _settings.MaxActiveFracturePieces, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.BlockTypeLimits" />
public VRage.Serialization.SerializableDictionary<System.String, System.Int16> BlockTypeLimits { get => _settings.BlockTypeLimits; set => SetValue(ref _settings.BlockTypeLimits, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableScripterRole" />
public System.Boolean EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MinDropContainerRespawnTime" />
public System.Int32 MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxDropContainerRespawnTime" />
public System.Int32 MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableTurretsFriendlyFire" />
public System.Boolean EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); }
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSubgridDamage" />
public System.Boolean EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); }
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
{
_settings = settings;
}
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
{
return viewModel._settings;
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SteamSDK;
using Steamworks;
namespace Torch.Server.ViewModels
{
@@ -15,7 +15,7 @@ namespace Torch.Server.ViewModels
public SteamUserViewModel(ulong id)
{
SteamId = id;
Name = SteamAPI.Instance.Friends.GetPersonaName(id);
Name = SteamFriends.GetFriendPersonaName(new CSteamID(id));
}
public SteamUserViewModel() : this(0) { }

View File

@@ -3,12 +3,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Torch.Server"
mc:Ignorable="d"
Title="Add Workshop Item" Height="200" Width="400">
<DockPanel Background="LightGray">
<DockPanel>
<Label DockPanel.Dock="Top" Content="Add each workshop URL or ID on its own line." HorizontalAlignment="Center"/>
<Button DockPanel.Dock="Bottom" Content="Done" Margin="5,0,5,5" Click="Done_Clicked"/>
<TextBox x:Name="urlBlock" Margin="5,0,5,5" Background="White" AcceptsReturn="True"/>
<TextBox x:Name="urlBlock" Margin="5,0,5,5" AcceptsReturn="True"/>
</DockPanel>
</Window>

View File

@@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
@@ -11,7 +10,7 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer x:Name="ChatScroller" Grid.Row="0" Margin="5,5,5,5" HorizontalScrollBarVisibility="Disabled">
<TextBlock x:Name="ChatItems" />
<TextBlock x:Name="ChatItems" TextWrapping="Wrap" />
</ScrollViewer>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>

View File

@@ -4,6 +4,7 @@ using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -15,11 +16,11 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using NLog;
using Torch;
using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.World;
using SteamSDK;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Session;
@@ -34,22 +35,48 @@ namespace Torch.Server
/// </summary>
public partial class ChatControl : UserControl
{
private TorchBase _server;
private static Logger _log = LogManager.GetCurrentClassLogger();
private ITorchServer _server;
public ChatControl()
{
InitializeComponent();
this.IsVisibleChanged += OnIsVisibleChanged;
}
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
_log.Info($"VisibleChanged: {IsVisible}");
if (IsVisible)
{
Task.Run(() =>
{
Thread.Sleep(100);
Dispatcher.Invoke(() =>
{
Message.Focus();
Keyboard.Focus(Message);
});
});
}
}
public void BindServer(ITorchServer server)
{
_server = (TorchBase)server;
_server = server;
server.Initialized += Server_Initialized ;
}
private void Server_Initialized(ITorchServer obj)
{
Dispatcher.InvokeAsync(() =>
{
ChatItems.Inlines.Clear();
});
var sessionManager = server.Managers.GetManager<ITorchSessionManager>();
var sessionManager = _server.Managers.GetManager<ITorchSessionManager>();
if (sessionManager != null)
sessionManager.SessionStateChanged += SessionStateChanged;
}
@@ -139,9 +166,7 @@ namespace Torch.Server
InsertMessage(new TorchChatMessage("Server", text, MyFontEnum.DarkBlue));
_server.Invoke(() =>
{
string response = commands.HandleCommandFromServer(text);
if (!string.IsNullOrWhiteSpace(response))
InsertMessage(new TorchChatMessage("Server", response, MyFontEnum.Blue));
commands.HandleCommandFromServer(text);
});
}
else

View File

@@ -3,244 +3,149 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server.Views"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
mc:Ignorable="d"
Background="White">
xmlns:managers="clr-namespace:Torch.Server.Managers"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:validationRules="clr-namespace:Torch.Server.Views.ValidationRules"
xmlns:views="clr-namespace:Torch.Views;assembly=Torch"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<viewModels:ConfigDedicatedViewModel />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Label Content="World:" DockPanel.Dock="Left" />
<Button Content="New World" Margin="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" SelectionChanged="Selector_OnSelectionChanged"/>
<Button Content="Import World Config" Margin="3" Padding="3" DockPanel.Dock="Right" Click="ImportConfig_OnClick" ToolTip="Override the DS config with the one from the selected world." IsEnabled="{Binding ElementName=WorldList, Path=Items.Count, Mode=OneWay}"/>
<Button Content="New World" Margin="3" Padding="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
<ComboBox x:Name="WorldList" ItemsSource="{Binding Worlds}" SelectedItem="{Binding SelectedWorld}" Margin="3"
SelectionChanged="Selector_OnSelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate DataType="managers:WorldViewModel">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Checkpoint.SessionName}" FontWeight="Bold" Padding="0" />
<Label Content="{Binding WorldPath}" Padding="5,0,0,0" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Size (KB): " Padding="0" />
<Label Content="{Binding WorldSizeKB}" Padding="0" />
<Label Content="Last saved: " Padding="5,0,0,0" />
<Label Content="{Binding Checkpoint.LastSaveTime}" Padding="0" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="7*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Margin="3">
<StackPanel Orientation="Horizontal">
<StackPanel Margin="3" DockPanel.Dock="Left">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="3" DockPanel.Dock="Left">
<Label Content="Server Name" />
<TextBox Text="{Binding ServerName}" Margin="3,0,3,3" Width="160" />
<Label Content="World Name" />
<TextBox Text="{Binding WorldName}" Margin="3,0,3,3" Width="160" />
<Label Content="Whitelist Group ID" />
<TextBox Text="{Binding GroupId}" Margin="3,0,3,3" Width="160" />
<Label Content="World Name" />
<TextBox Text="{Binding WorldName}" Margin="3,0,3,3" Width="160" />
<Label Content="Server Description" />
<TextBox Text="{Binding ServerDescription}" Margin="3,0,3,3" Width="160" Height="100"
AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
<Label Content="Whitelist Group ID" />
<TextBox Margin="3,0,3,3" Width="160" Style="{StaticResource ValidatedTextBox}">
<TextBox.Text>
<Binding Path="GroupId" UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True" NotifyOnValidationError="True">
<Binding.ValidationRules>
<validationRules:NumberValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Content="Server IP" />
<StackPanel Orientation="Horizontal" Margin="3,0,3,3">
<TextBox Text="{Binding IP}" Width="100" Height="20" />
<Label Content=":" Width="12" />
<TextBox Text="{Binding Port}" Width="48" Height="20" />
</StackPanel>
<Label Content="Server Password"/>
<TextBox Text="{Binding Password}" Width="160"/>
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
<CheckBox IsChecked="{Binding AutodetectDependencies}" Content="Auto Detect Dependencies" Margin="3" />
</StackPanel>
<StackPanel Margin="3">
<StackPanel Grid.Column="1" Margin="3">
<Label Content="Mods" />
<TextBox Text="{Binding Mods}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
<TextBox Margin="3" Height="60" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"
Style="{StaticResource ValidatedTextBox}">
<TextBox.Text>
<Binding Path="Mods" UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
Converter="{StaticResource ListConverterUInt64}">
<Binding.ValidationRules>
<validationRules:ListConverterValidationRule Type="system:UInt64" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Content="Administrators" />
<TextBox Text="{Binding Administrators}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
<TextBox Text="{Binding Administrators, Converter={StaticResource ListConverterString}}"
Margin="3"
Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto" />
<Label Content="Reserved Players" />
<TextBox Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"
Style="{StaticResource ValidatedTextBox}">
<TextBox.Text>
<Binding Path="Reserved" UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
Converter="{StaticResource ListConverterUInt64}">
<Binding.ValidationRules>
<validationRules:ListConverterValidationRule Type="system:UInt64" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Content="Banned Players" />
<TextBox Text="{Binding Banned}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
<TextBox Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"
Style="{StaticResource ValidatedTextBox}">
<TextBox.Text>
<Binding Path="Banned" UpdateSourceTrigger="PropertyChanged"
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
Converter="{StaticResource ListConverterUInt64}">
<Binding.ValidationRules>
<validationRules:ListConverterValidationRule Type="system:UInt64" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</Grid>
</ScrollViewer>
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
</Grid>
<ScrollViewer Grid.Column="1" Margin="3">
<StackPanel DataContext="{Binding SessionSettings}">
<Expander Header="Block Limits">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
<Label Content="Max Blocks Per Player" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
<Label Content="Max Grid Size" />
</StackPanel>
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
<ListView ItemsSource="{Binding BlockLimits}" Margin="3">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding BlockType}" Width="150" Margin="3" />
<TextBox Text="{Binding Limit}" Width="50" Margin="3" />
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Expander>
<Expander Header="Multipliers">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding InventorySizeMultiplier}" Margin="3" Width="70" />
<Label Content="Inventory Size" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding RefinerySpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Refinery Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding AssemblerEfficiencyMultiplier}" Margin="3" Width="70" />
<Label Content="Assembler Efficiency" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding AssemblerSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Assembler Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding WelderSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Welder Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding GrinderSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Grinder Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding HackSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Hacking Speed" />
</StackPanel>
</StackPanel>
</Expander>
<Expander Header="NPCs">
<StackPanel Margin="10,0,0,0">
<CheckBox IsChecked="{Binding EnableDrones}" Content="Enable Drones" Margin="3" />
<CheckBox IsChecked="{Binding EnableEncounters}" Content="Enable Encounters" Margin="3" />
<CheckBox IsChecked="{Binding EnableSpiders}" Content="Enable Spiders" Margin="3" />
<CheckBox IsChecked="{Binding EnableWolves}" Content="Enable Wolves" Margin="3" />
<CheckBox IsChecked="{Binding EnableCargoShips}" Content="Enable Cargo Ships" Margin="3" />
</StackPanel>
</Expander>
<Expander Header="Environment">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal" ToolTip="Increases physics precision at the cost of performance.">
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
<Label Content="Physics Iterations" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
<Label Content="Max Floating Objects" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
Margin="3" />
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
<CheckBox IsChecked="{Binding EnableOxygen}" Content="Enable Oxygen" Margin="3" />
<CheckBox IsChecked="{Binding EnableDestructibleBlocks}"
Content="Enable Destructible Blocks" Margin="3" />
<CheckBox IsChecked="{Binding EnableToolShake}" Content="Enable Tool Shake" Margin="3" />
<CheckBox IsChecked="{Binding EnableVoxelDestruction}" Content="Enable Voxel Destruction"
Margin="3" />
<CheckBox IsChecked="{Binding EnableSunRotation}" Content="Enable Sun Rotation" Margin="3" />
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SunRotationInterval}" Margin="3" Width="70" />
<Label Content="Sun Rotation Interval (mins)" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableFlora}" Content="Enable Flora" Margin="3" />
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding FloraDensity}" Margin="3" Width="70" />
<Label Content="Flora Density" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding FloraDensityMultiplier}" Margin="3" Width="70" />
<Label Content="Flora Density Multiplier" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ViewDistance}" Margin="3" Width="70" />
<Label Content="View Distance (meters)" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding WorldSize}" Margin="3" Width="70" />
<Label Content="World Size (km)" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<ComboBox SelectedItem="{Binding EnvironmentHostility}"
ItemsSource="{Binding EnvironmentHostilityValues}" Margin="3" Width="100"
DockPanel.Dock="Left" />
<Label Content="Environment Hostility" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ProceduralDensity}" Margin="3" Width="70" />
<Label Content="Procedural Density" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ProceduralSeed}" Margin="3" Width="70" />
<Label Content="Procedural Seed" />
</StackPanel>
</StackPanel>
</Expander>
<Expander Header="Players">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
<Label Content="Max Players" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
Margin="3" />
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
<CheckBox IsChecked="{Binding EnableAutoHealing}" Content="Auto Healing" Margin="3" />
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
Margin="3" />
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
<Label Content="Respawn Time Multiplier" />
</StackPanel>
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
</StackPanel>
</Expander>
<Expander Header="Miscellaneous">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
<Label Content="Autosave Interval (minutes)" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
Margin="3" />
<CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}"
Content="Enable Remote Ownership Removal" Margin="3" />
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
Margin="3" />
<CheckBox IsChecked="{Binding EnableScripterRole}" Content="Enable Scripter Role"
Margin="3" />
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
Margin="3" />
<CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff"
Margin="3" />
<CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage"
Margin="3" />
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
Margin="3" />
<StackPanel Orientation="Horizontal">
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
Margin="3" Width="100" DockPanel.Dock="Left" />
<Label Content="Game Mode" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
<Label Content="Max Backup Saves" />
</StackPanel>
</StackPanel>
</Expander>
</StackPanel>
</ScrollViewer>
<views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" IgnoreDisplay ="True" />
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True"
Width="2" />
</Grid>
</Grid>
</UserControl>

View File

@@ -1,6 +1,15 @@
using System.Windows;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;
using Torch.API.Managers;
using Torch.Server.Annotations;
using Torch.Server.Managers;
using Torch.Server.ViewModels;
@@ -9,15 +18,74 @@ namespace Torch.Server.Views
/// <summary>
/// Interaction logic for ConfigControl.xaml
/// </summary>
public partial class ConfigControl : UserControl
public partial class ConfigControl : UserControl, INotifyPropertyChanged
{
private InstanceManager _instanceManager;
private bool _configValid;
public bool ConfigValid { get => _configValid; private set { _configValid = value; OnPropertyChanged(); } }
private List<BindingExpression> _bindingExpressions = new List<BindingExpression>();
public ConfigControl()
{
InitializeComponent();
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
_instanceManager.InstanceLoaded += _instanceManager_InstanceLoaded;
DataContext = _instanceManager.DedicatedConfig;
// Gets called once all children are loaded
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(ApplyStyles));
}
private void CheckValid()
{
ConfigValid = !_bindingExpressions.Any(x => x.HasError);
}
private void ApplyStyles()
{
foreach (var textbox in GetAllChildren<TextBox>(this))
{
textbox.Style = (Style)Resources["ValidatedTextBox"];
var binding = textbox.GetBindingExpression(TextBox.TextProperty);
if (binding == null)
continue;
_bindingExpressions.Add(binding);
textbox.TextChanged += (sender, args) =>
{
binding.UpdateSource();
CheckValid();
};
textbox.LostKeyboardFocus += (sender, args) =>
{
if (binding.HasError)
binding.UpdateTarget();
CheckValid();
};
CheckValid();
}
}
private IEnumerable<T> GetAllChildren<T>(DependencyObject control) where T : DependencyObject
{
var children = LogicalTreeHelper.GetChildren(control).OfType<DependencyObject>();
foreach (var child in children)
{
if (child is T t)
yield return t;
foreach (var grandChild in GetAllChildren<T>(child))
yield return grandChild;
}
}
private void _instanceManager_InstanceLoaded(ConfigDedicatedViewModel obj)
{
Dispatcher.Invoke(() => DataContext = obj);
}
private void Save_OnClick(object sender, RoutedEventArgs e)
@@ -25,20 +93,9 @@ namespace Torch.Server.Views
_instanceManager.SaveConfig();
}
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
private void ImportConfig_OnClick(object sender, RoutedEventArgs e)
{
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Remove(vm);
}
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
{
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_instanceManager.DedicatedConfig.SessionSettings, "", 0));
}
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Feature coming soon :)");
_instanceManager.ImportSelectedWorldConfig();
}
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -46,8 +103,24 @@ namespace Torch.Server.Views
//The control doesn't update the binding before firing the event.
if (e.AddedItems.Count > 0)
{
_instanceManager.SelectWorld((string)e.AddedItems[0]);
var result = MessageBoxResult.Yes; //MessageBox.Show("Do you want to import the session settings from the selected world?", "Import Config", MessageBoxButton.YesNo);
var world = (WorldViewModel)e.AddedItems[0];
_instanceManager.SelectWorld(world.WorldPath, result != MessageBoxResult.Yes);
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
{
var c = new WorldGeneratorDialog(_instanceManager);
c.Show();
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace Torch.Server.Views.Converters
{
public class BooleanAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (var value in values)
{
if (value is bool b && b == false)
{
return false;
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException("BooleanAndConverter is a OneWay converter.");
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using Sandbox.Definitions;
using VRage.Game;
namespace Torch.Server.Views.Converters
{
public class DefinitionToIdConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// ReSharper disable once PossibleNullReferenceException
MyDefinitionId id = ((MyDefinitionBase) value).Id;
string typeName = id.TypeId.ToString();
if (typeName.StartsWith("MyObjectBuilder_"))
typeName = typeName.Substring("MyObjectBuilder_".Length);
string subtype = id.SubtypeName;
return string.IsNullOrWhiteSpace(subtype) ? typeName : $"{typeName}: {subtype}";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// ReSharper disable once PossibleNullReferenceException
string[] parts = value.ToString().Split(':');
Type type;
try
{
type = Type.GetType(parts[0]);
}
catch
{
type = Type.GetType("MyObjectBuilder_" + parts[0]);
}
return MyDefinitionManager.Static.GetDefinition(
new MyDefinitionId(type, parts.Length > 1 ? parts[1].Trim() : ""));
}
}
}

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace Torch.Server.Views.Converters
@@ -15,9 +11,6 @@ namespace Torch.Server.Views.Converters
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Navigation;
namespace Torch.Server.Views.Converters
{
public class ListConverter : IValueConverter
{
public Type Type { get; set; }
/// <inheritdoc />
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is IList list))
throw new InvalidOperationException("Value is not the proper type.");
var sb = new StringBuilder();
foreach (var item in list)
{
sb.AppendLine(item.ToString());
}
return sb.ToString();
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(Type));
var converter = TypeDescriptor.GetConverter(Type);
var input = ((string)value).Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in input)
{
try
{
list.Add(converter.ConvertFromString(item));
}
catch
{
throw new InvalidOperationException("Could not convert input value.");
}
}
return list;
}
}
}

View File

@@ -14,8 +14,8 @@
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Label Content="{Binding FullName}" FontSize="16" />
@@ -25,7 +25,7 @@
</StackPanel>
<Label Content="Properties"/>
</StackPanel>
<Expander Grid.Row="1" Header="Block Properties">
<Expander Grid.Row="1" Header="Block Properties" IsExpanded="true">
<ListView ItemsSource="{Binding Properties}" Margin="3" IsEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>

View File

@@ -28,6 +28,15 @@ namespace Torch.Server.Views.Blocks
public BlockView()
{
InitializeComponent();
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
/*

View File

@@ -3,8 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server.Views.Blocks"
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.Blocks"
xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
mc:Ignorable="d">
<UserControl.Resources>

View File

@@ -26,6 +26,15 @@ namespace Torch.Server.Views.Blocks
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs args)

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="Torch.Server.Views.Entities.CharacterView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
xmlns:local="clr-namespace:Torch.Server.Views.Entities"
mc:Ignorable="d">
<UserControl.DataContext>
<entities:CharacterViewModel />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Label Content="Name" Width="100"/>
<TextBox Text="{Binding Name}" Margin="3"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Label Content="Position" Width="100"/>
<TextBox Text="{Binding Position}" Margin="3" />
</StackPanel>
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
<local:EntityControlsView DataContext="{Binding}"/>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Torch.Server.Views.Entities
{
/// <summary>
/// Interaction logic for GridView.xaml
/// </summary>
public partial class CharacterView : UserControl
{
public CharacterView()
{
InitializeComponent();
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
}
}

View File

@@ -17,6 +17,15 @@ namespace Torch.Server.Views.Entities
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)

View File

@@ -1,4 +1,5 @@
using System.Windows.Controls;
using System.Windows;
using System.Windows.Controls;
namespace Torch.Server.Views.Entities
{
@@ -10,6 +11,15 @@ namespace Torch.Server.Views.Entities
public EntityControlsView()
{
InitializeComponent();
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
}
}

View File

@@ -1,4 +1,4 @@
<UserControl x:Class="Torch.Server.Views.Entities.GridView"
<UserControl x:Class="Torch.Server.Views.Entities.GridView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -6,11 +6,13 @@
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
xmlns:local="clr-namespace:Torch.Server.Views.Entities"
mc:Ignorable="d">
<UserControl.DataContext>
<entities:GridViewModel />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
@@ -23,7 +25,11 @@
<Label Content="Position" Width="100"/>
<TextBox Text="{Binding Position}" Margin="3" />
</StackPanel>
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
<StackPanel Grid.Row="2" Orientation="Horizontal">
<Label Content="EntityID" Width="100"/>
<TextBox Text="{Binding Id, Mode=OneWay}" Margin="3" />
</StackPanel>
<ScrollViewer Grid.Row="3" Margin="3" VerticalScrollBarVisibility="Auto">
<local:EntityControlsView DataContext="{Binding}"/>
</ScrollViewer>
</Grid>

View File

@@ -23,6 +23,15 @@ namespace Torch.Server.Views.Entities
public GridView()
{
InitializeComponent();
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
}
}

View File

@@ -25,6 +25,15 @@ namespace Torch.Server.Views.Entities
{
InitializeComponent();
DataContextChanged += VoxelMapView_DataContextChanged;
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
UpdateResourceDict(ThemeControl.currentTheme);
}
public void UpdateResourceDict(ResourceDictionary dictionary)
{
this.Resources.MergedDictionaries.Clear();
this.Resources.MergedDictionaries.Add(dictionary);
}
private void VoxelMapView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)

View File

@@ -3,39 +3,58 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server.Views"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.Blocks"
xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
mc:Ignorable="d">
<UserControl.DataContext>
<viewModels:EntityTreeViewModel />
</UserControl.DataContext>
<UserControl.Resources>
<converters:DefinitionToIdConverter x:Key="DefinitionConverter" />
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition MinWidth="300" Width="Auto"/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TreeView Grid.Row="0" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged" TreeViewItem.Expanded="TreeViewItem_OnExpanded">
<TreeView Grid.Row="0" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged"
TreeViewItem.Expanded="TreeViewItem_OnExpanded">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type entities:GridViewModel}" ItemsSource="{Binding Blocks}">
<HierarchicalDataTemplate DataType="{x:Type entities:GridViewModel}"
ItemsSource="{Binding Path=Blocks}">
<TextBlock Text="{Binding DescriptiveName}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Value.Values}">
<TextBlock>
<Run Text="{Binding Path=Key, Converter={StaticResource DefinitionConverter}, Mode=OneWay}" />
<Run Text="{Binding Path=Value.Count, StringFormat=' ({0} blocks)', Mode=OneWay}" />
</TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type blocks:BlockViewModel}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type blocks:BlockViewModel}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type entities:VoxelMapViewModel}" ItemsSource="{Binding AttachedGrids}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate DataType="{x:Type entities:VoxelMapViewModel}"
ItemsSource="{Binding AttachedGrids}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeViewItem ItemsSource="{Binding Grids}">
<TreeViewItem ItemsSource="{Binding Path=Grids.Values}">
<TreeViewItem.Header>
<TextBlock Text="{Binding Grids.Count, StringFormat=Grids ({0})}" />
</TreeViewItem.Header>
</TreeViewItem>
<TreeViewItem ItemsSource="{Binding Characters}">
<TreeViewItem ItemsSource="{Binding Characters.Values}">
<TreeViewItem.Header>
<TextBlock Text="{Binding Characters.Count, StringFormat=Characters ({0})}" />
</TreeViewItem.Header>
@@ -45,7 +64,7 @@
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
<TreeViewItem ItemsSource="{Binding VoxelMaps}">
<TreeViewItem ItemsSource="{Binding VoxelMaps.Values}">
<TreeViewItem.Header>
<TextBlock Text="{Binding VoxelMaps.Count, StringFormat=Voxel Maps ({0})}" />
</TreeViewItem.Header>
@@ -55,7 +74,7 @@
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
<TreeViewItem ItemsSource="{Binding FloatingObjects}">
<TreeViewItem ItemsSource="{Binding FloatingObjects.Values}">
<TreeViewItem.Header>
<TextBlock Text="{Binding FloatingObjects.Count, StringFormat=Floating Objects ({0})}" />
</TreeViewItem.Header>
@@ -73,5 +92,6 @@
</StackPanel>
</Grid>
<Frame Grid.Column="1" x:Name="EditorFrame" Margin="3" NavigationUIVisibility="Hidden" />
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True" Width="2" />
</Grid>
</UserControl>

View File

@@ -48,6 +48,8 @@ namespace Torch.Server.Views
EditorFrame.Content = new BlockView {DataContext = bvm};
if (e.NewValue is VoxelMapViewModel vvm)
EditorFrame.Content = new VoxelMapView {DataContext = vvm};
if (e.NewValue is CharacterViewModel cvm)
EditorFrame.Content = new CharacterView {DataContext = cvm};
}
else
{

View File

@@ -1,30 +0,0 @@
<Window x:Class="Torch.Server.Views.FirstTimeSetup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Torch.Server.Views"
xmlns:torch="clr-namespace:Torch;assembly=Torch"
xmlns:server="clr-namespace:Torch.Server"
mc:Ignorable="d"
Title="Torch First Time Setup" Height="200" Width="500">
<Window.DataContext>
<server:TorchConfig/>
</Window.DataContext>
<StackPanel>
<DockPanel ToolTip="This should be set to the folder that contains your mods and saves.">
<Label Content="Instance Path:" Width="150"/>
<TextBox Text="{Binding InstancePath}" Margin="3"/>
</DockPanel>
<DockPanel ToolTip="The name of your instance, this doesn't really matter.">
<Label Content="Instance Name:" Width="150"/>
<TextBox Text="{Binding InstanceName}" Margin="3"></TextBox>
</DockPanel>
<DockPanel ToolTip="This enables/disables automatic plugin updating.">
<Label Content="Automatic Plugin Updates:" Width="150"/>
<CheckBox IsChecked="{Binding EnableAutomaticUpdates}" VerticalAlignment="Center" Margin="3"/>
</DockPanel>
<Label Content="You can change these settings later by editing TorchConfig.xml"/>
<Button Content="Done" Margin="3" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Window>

View File

@@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server"
mc:Ignorable="d">
<StackPanel Margin="0,0,0,0" Orientation="Vertical">
<Label Content="Mods" Margin="5,5,5,5"/>

View File

@@ -15,7 +15,6 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Sandbox.Engine.Networking;
using SteamSDK;
using VRage.Game;
namespace Torch.Server
@@ -45,16 +44,16 @@ namespace Torch.Server
mods = dialog.Result;
}
foreach (var id in mods)
{
var details = SteamHelper.GetItemDetails(id);
if (details.FileType != WorkshopFileType.Community)
continue;
//foreach (var id in mods)
//{
// var details = SteamHelper.GetItemDetails(id);
// if (details.FileType != WorkshopFileType.Community)
// continue;
var item = SteamHelper.GetModItem(details);
var desc = details.Description.Length < 500 ? details.Description : details.Description.Substring(0, 500);
ModList.Items.Add(new ModViewModel(item, desc));
}
// var item = SteamHelper.GetModItem(details);
// var desc = details.Description.Length < 500 ? details.Description : details.Description.Substring(0, 500);
// ModList.Items.Add(new ModViewModel(item, desc));
//}
}
private void modList_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)

View File

@@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server"
mc:Ignorable="d">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom">

View File

@@ -19,7 +19,6 @@ using Sandbox.Engine.Multiplayer;
using Sandbox.Game.Multiplayer;
using Sandbox.Game.World;
using Sandbox.ModAPI;
using SteamSDK;
using Torch.API;
using Torch.API.Managers;
using Torch.API.Session;
@@ -47,8 +46,12 @@ namespace Torch.Server
public void BindServer(ITorchServer server)
{
_server = server;
_server.Initialized += Server_Initialized ;
}
var sessionManager = server.Managers.GetManager<ITorchSessionManager>();
private void Server_Initialized(ITorchServer obj)
{
var sessionManager = _server.Managers.GetManager<ITorchSessionManager>();
sessionManager.SessionStateChanged += SessionStateChanged;
}

View File

@@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server.Views"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
@@ -29,7 +28,9 @@
</ListView>
<Button Grid.Row="1" Content="Open Folder" Margin="3" DockPanel.Dock="Bottom" Click="OpenFolder_OnClick"/>
</Grid>
<Frame Grid.Column="1" NavigationUIVisibility="Hidden" Content="{Binding SelectedPlugin.Control}"/>
<ScrollViewer Name="PScroll" Grid.Column="1" Margin="3">
<Frame NavigationUIVisibility="Hidden" Content="{Binding SelectedPlugin.Control}"/>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
@@ -19,6 +20,7 @@ using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using Torch.Server.ViewModels;
using Torch.Views;
namespace Torch.Server.Views
{
@@ -35,17 +37,39 @@ namespace Torch.Server.Views
InitializeComponent();
}
private void PluginManagerOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (propertyChangedEventArgs.PropertyName == nameof(PluginManagerViewModel.SelectedPlugin))
{
if (((PluginManagerViewModel)DataContext).SelectedPlugin.Control is PropertyGrid)
PScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
else
PScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
}
}
public void BindServer(ITorchServer server)
{
_server = server;
_plugins = _server.Managers.GetManager<PluginManager>();
var pluginManager = new PluginManagerViewModel(_plugins);
DataContext = pluginManager;
_server.Initialized += Server_Initialized;
}
private void Server_Initialized(ITorchServer obj)
{
Dispatcher.InvokeAsync(() =>
{
_plugins = _server.Managers.GetManager<PluginManager>();
var pluginManager = new PluginManagerViewModel(_plugins);
DataContext = pluginManager;
pluginManager.PropertyChanged += PluginManagerOnPropertyChanged;
});
}
private void OpenFolder_OnClick(object sender, RoutedEventArgs e)
{
Process.Start("explorer.exe", _plugins.PluginDir);
if (_plugins?.PluginDir != null)
Process.Start(_plugins.PluginDir);
}
}
}

View File

@@ -1,15 +0,0 @@
<UserControl x:Class="Torch.Server.Views.PropertyGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<DataGrid x:Name="Grid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="NameCol" Width="1*" Header="Name" Binding="{Binding Name}" IsReadOnly="True"/>
<DataGridTemplateColumn x:Name="ValCol" Width="1*" Header="Value"/>
</DataGrid.Columns>
</DataGrid>
</UserControl>

View File

@@ -1,68 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Torch.Server.Views
{
/// <summary>
/// Interaction logic for PropertyGrid.xaml
/// </summary>
public partial class PropertyGrid : UserControl
{
public PropertyGrid()
{
InitializeComponent();
}
public void SetObject(object obj)
{
var props = obj.GetType().GetProperties();
foreach (var prop in props)
{
var p = prop.GetValue(obj);
Grid.Items.Add(new PropertyView(p, prop.Name));
}
}
}
public class PropertyView : ViewModel
{
private object _obj;
public string Name { get; }
public string Value { get { return _obj.ToString(); } }
public DataTemplate ValueEditTemplate;
public PropertyView(object obj, string name)
{
Name = name;
_obj = obj;
ValueEditTemplate = new DataTemplate();
}
}
/*
public class PropertyGridDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is IEnumerable)
{
}
}
}*/
}

View File

@@ -0,0 +1,22 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<Style x:Key="{x:Type Window}" TargetType="{x:Type Window}" BasedOn="{StaticResource {x:Type Window}}">
<Style.Setters>
<Setter Property="Background" Value="Black"/>
</Style.Setters>
</Style>
<Style x:Key="ValidatedTextBox" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
<Setter Property="Background" Value="Pink"/>
</Trigger>
</Style.Triggers>
</Style>
<converters:ListConverter x:Key="ListConverterString" Type="system:String"/>
<converters:ListConverter x:Key="ListConverterUInt64" Type="system:UInt64"/>
<converters:BooleanAndConverter x:Key="BooleanAndConverter"/>
</ResourceDictionary>

View File

@@ -0,0 +1,188 @@
<UserControl x:Class="Torch.Server.Views.SessionSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Expander Header="Block Limits">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
<Label Content="Max Blocks Per Player" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
<Label Content="Max Grid Size" />
</StackPanel>
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
<ListView ItemsSource="{Binding BlockTypeLimits.Dictionary}" Margin="3">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Key, Mode=OneTime}" Width="150" Margin="3" />
<TextBox Text="{Binding Value, Mode=OneTime}" Width="50" Margin="3" />
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Expander>
<Expander Header="Multipliers">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding InventorySizeMultiplier}" Margin="3" Width="70" />
<Label Content="Inventory Size" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding RefinerySpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Refinery Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding AssemblerEfficiencyMultiplier}" Margin="3" Width="70" />
<Label Content="Assembler Efficiency" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding AssemblerSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Assembler Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding WelderSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Welder Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding GrinderSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Grinder Speed" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding HackSpeedMultiplier}" Margin="3" Width="70" />
<Label Content="Hacking Speed" />
</StackPanel>
</StackPanel>
</Expander>
<Expander Header="NPCs">
<StackPanel Margin="10,0,0,0">
<CheckBox IsChecked="{Binding EnableDrones}" Content="Enable Drones" Margin="3" />
<CheckBox IsChecked="{Binding EnableEncounters}" Content="Enable Encounters" Margin="3" />
<CheckBox IsChecked="{Binding EnableSpiders}" Content="Enable Spiders" Margin="3" />
<CheckBox IsChecked="{Binding EnableWolfs}" Content="Enable Wolves" Margin="3" />
<CheckBox IsChecked="{Binding CargoShipsEnabled}" Content="Enable Cargo Ships" Margin="3" />
</StackPanel>
</Expander>
<Expander Header="Environment">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal" ToolTip="Increases physics precision at the cost of performance.">
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
<Label Content="Physics Iterations" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
<Label Content="Max Floating Objects" />
</StackPanel>
<CheckBox IsChecked="{Binding RealisticSound}" Content="Enable Realistic Sound"
Margin="3" />
<CheckBox IsChecked="{Binding EnableOxygenPressurization}" Content="Enable Airtightness" Margin="3" />
<CheckBox IsChecked="{Binding EnableOxygen}" Content="Enable Oxygen" Margin="3" />
<CheckBox IsChecked="{Binding DestructibleBlocks}"
Content="Enable Destructible Blocks" Margin="3" />
<CheckBox IsChecked="{Binding EnableToolShake}" Content="Enable Tool Shake" Margin="3" />
<CheckBox IsChecked="{Binding EnableVoxelDestruction}" Content="Enable Voxel Destruction"
Margin="3" />
<CheckBox IsChecked="{Binding EnableSunRotation}" Content="Enable Sun Rotation" Margin="3" />
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SunRotationIntervalMinutes}" Margin="3" Width="70" />
<Label Content="Sun Rotation Interval (mins)" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableFlora}" Content="Enable Flora" Margin="3" />
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding FloraDensity}" Margin="3" Width="70" />
<Label Content="Flora Density" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding FloraDensityMultiplier}" Margin="3" Width="70" />
<Label Content="Flora Density Multiplier" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ViewDistance}" Margin="3" Width="70" />
<Label Content="View Distance (meters)" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding WorldSizeKm}" Margin="3" Width="70" />
<Label Content="World Size (km)" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<ComboBox SelectedItem="{Binding EnvironmentHostility}"
ItemsSource="{Binding EnvironmentHostilityValues}" Margin="3" Width="100"
DockPanel.Dock="Left" />
<Label Content="Environment Hostility" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ProceduralDensity}" Margin="3" Width="70" />
<Label Content="Procedural Density" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding ProceduralSeed}" Margin="3" Width="70" />
<Label Content="Procedural Seed" />
</StackPanel>
</StackPanel>
</Expander>
<Expander Header="Players">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
<Label Content="Max Players" />
</StackPanel>
<CheckBox IsChecked="{Binding Enable3rdPersonView}" Content="Enable 3rd Person Camera"
Margin="3" />
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
<CheckBox IsChecked="{Binding AutoHealing}" Content="Auto Healing" Margin="3" />
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
Margin="3" />
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SpawnShipTimeMultiplier}" Margin="3" Width="70" />
<Label Content="Respawn Time Multiplier" />
</StackPanel>
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
</StackPanel>
</Expander>
<Expander Header="Miscellaneous">
<StackPanel Margin="10,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding AutoSaveInMinutes}" Margin="3" Width="70" />
<Label Content="Autosave Interval (minutes)" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
Margin="3" />
<CheckBox IsChecked="{Binding EnableRemoteBlockRemoval}"
Content="Enable Remote Ownership Removal" Margin="3" />
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
Margin="3" />
<CheckBox IsChecked="{Binding EnableScripterRole}" Content="Enable Scripter Role"
Margin="3" />
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
Margin="3" />
<CheckBox IsChecked="{Binding RespawnShipDelete}" Content="Remove Respawn Ships on Logoff"
Margin="3" />
<CheckBox IsChecked="{Binding ThrusterDamage}" Content="Enable Thruster Damage"
Margin="3" />
<CheckBox IsChecked="{Binding WeaponsEnabled}" Content="Enable Weapons" Margin="3" />
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
Margin="3" />
<StackPanel Orientation="Horizontal">
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
Margin="3" Width="100" DockPanel.Dock="Left" />
<Label Content="Game Mode" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
<Label Content="Max Backup Saves" />
</StackPanel>
</StackPanel>
</Expander>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Torch.Server.ViewModels;
namespace Torch.Server.Views
{
/// <summary>
/// Interaction logic for SessionSettingsView.xaml
/// </summary>
public partial class SessionSettingsView : UserControl
{
public SessionSettingsView()
{
InitializeComponent();
}
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
{
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
//_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Remove(vm);
}
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
{
//_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_instanceManager.DedicatedConfig.SessionSettings, "", 0));
}
}
}

View File

@@ -0,0 +1,31 @@
<UserControl x:Class="Torch.Server.Views.ThemeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
xmlns:managers="clr-namespace:Torch.Server.Managers"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:validationRules="clr-namespace:Torch.Server.Views.ValidationRules"
xmlns:views="clr-namespace:Torch.Views;assembly=Torch"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Label Content="Theme:" DockPanel.Dock="Left" />
<ComboBox ItemsSource="{Binding Themes}"
SelectionChanged="Selector_OnSelectionChanged">
</ComboBox>
</DockPanel>
<TextBlock Width="Auto" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Bottom" Name="LegalshtuffBlock">
<Hyperlink Name="LegalshtuffLink" RequestNavigate="Hyperlink_RequestNavigate">
Theme License
</Hyperlink>
</TextBlock>
</Grid>
</UserControl>

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Windows.Navigation;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;
using Torch.API.Managers;
using Torch.API.Plugins;
using Torch.Server.Annotations;
using Torch.Server.Managers;
using Torch.Server.ViewModels;
namespace Torch.Server.Views
{
/// <summary>
/// Interaction logic for ThemeControl.xaml
/// </summary>
public partial class ThemeControl : UserControl, INotifyPropertyChanged
{
/// <summary>
/// Action other views can subscribe to to update their views if they dont inherit the style from the window for some reason.
/// </summary>
public static Action<ResourceDictionary> UpdateDynamicControls;
/// <summary>
/// Current theme other views can set their theme to when they first spawn
/// </summary>
public static ResourceDictionary currentTheme = new ResourceDictionary() { Source = new Uri(@"/Views/Resources.xaml", UriKind.Relative) };
/// <summary>
/// The current torch window and config.
/// </summary>
public TorchUI uiSource;
private TorchConfig _torchConfig;
/// <summary>
/// List of available themes
/// </summary>
public List<string> Themes
{
get => _themes.Keys.ToList();
}
private Dictionary<string, ResourceDictionary> _themes = new Dictionary<string, ResourceDictionary>();
private HashSet<ITorchPlugin> plugins;
public event PropertyChangedEventHandler PropertyChanged;
public ThemeControl()
{
InitializeComponent();
this.DataContext = this;
_themes["Dark theme"] = new ResourceDictionary() { Source = new Uri(@"/Themes/Dark Theme.xaml", UriKind.Relative) };
_themes["Animated Dark theme"] = new ResourceDictionary() { Source = new Uri(@"/Themes/Dark Theme Animated.xaml", UriKind.Relative) };
_themes["Light theme"] = new ResourceDictionary() { Source = new Uri(@"/Themes/Light Theme.xaml", UriKind.Relative) };
_themes["Light theme animated"] = new ResourceDictionary() { Source = new Uri(@"/Themes/Light Theme Animated.xaml", UriKind.Relative) };
_themes["Torch Theme"] = new ResourceDictionary() { Source = new Uri(@"/Views/Resources.xaml", UriKind.Relative) };
}
public void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox box = (ComboBox)sender;
var boxText = box.SelectedItem.ToString();
ChangeTheme(_themes[boxText].Source);
if (_torchConfig != null)
{
_torchConfig.LastUsedTheme = boxText;
_torchConfig.Save();
}
}
public void ChangeTheme(Uri uri)
{
uiSource.Resources.MergedDictionaries.Clear();
var resource = new ResourceDictionary() { Source = uri };
uiSource.Resources.MergedDictionaries.Add(resource);
UpdateDynamicControls?.Invoke(resource);
currentTheme = resource;
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
public void SetConfig(TorchConfig config)
{
_torchConfig = config;
if (_themes.ContainsKey(config.LastUsedTheme))
ChangeTheme(_themes[config.LastUsedTheme].Source);
}
}
}

View File

@@ -7,20 +7,39 @@
xmlns:views="clr-namespace:Torch.Server.Views"
xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
mc:Ignorable="d"
Title="Torch">
Title="Torch"
Name="MainWindow">
<Window.Resources>
<converters:InverseBooleanConverter x:Key="InverseBool"/>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<converters:InverseBooleanConverter x:Key="InverseBool"/>
</ResourceDictionary>
</Window.Resources>
<!--
<Window.DataContext>
<local:TorchServer/>
</Window.DataContext>
-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="5,5,5,5" Orientation="Horizontal">
<StackPanel Grid.Row="1" Margin="5,5,5,5" Orientation="Horizontal">
<Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0"
HorizontalAlignment="Left" Click="BtnStart_Click" IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}"/>
HorizontalAlignment="Left" Click="BtnStart_Click">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource BooleanAndConverter}">
<Binding ElementName="MainWindow" Path="DataContext.CanRun"/>
<Binding ElementName="ConfigControl" Path="ConfigValid"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"
Click="BtnStop_Click" IsEnabled="{Binding IsRunning}" />
Click="BtnStop_Click" IsEnabled="{Binding IsRunning}"/>
<Label>
<Label.Content>
<TextBlock Text="{Binding State, StringFormat=Status: {0}}"></TextBlock>
@@ -36,10 +55,18 @@
<TextBlock Text="{Binding ElapsedPlayTime, StringFormat=Uptime: {0:g}}"/>
</Label.Content>
</Label>
<Label x:Name="LabelPlayers">
<Label.Content>
<TextBlock ></TextBlock>
</Label.Content>
</Label>
</StackPanel>
<TabControl Grid.Row="1" Height="Auto" x:Name="TabControl" Margin="5,0,5,5">
<TabControl Grid.Row="2" Height="Auto" x:Name="TabControl" Margin="5,10,5,5">
<TabItem Header="Log">
<RichTextBox x:Name="ConsoleText" VerticalScrollBarVisibility="Visible" FontFamily="Consolas" IsReadOnly="True"/>
</TabItem>
<TabItem Header="Configuration">
<Grid IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}">
<Grid IsEnabled="{Binding CanRun}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
@@ -49,11 +76,8 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Instance Path: " Margin="3" />
<TextBox Grid.Column="1" x:Name="InstancePathBox" Margin="3" Height="20"
LostKeyboardFocus="InstancePathBox_OnLostKeyboardFocus" />
</Grid>
<views:ConfigControl Grid.Row="1" x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom" IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}"/>
<views:ConfigControl Grid.Row="1" x:Name="ConfigControl" Margin="3" DockPanel.Dock="Bottom" IsEnabled="{Binding CanRun}"/>
</Grid>
</TabItem>
<TabItem Header="Chat/Players">
@@ -72,6 +96,9 @@
<TabItem Header="Plugins">
<views:PluginsControl x:Name="Plugins" />
</TabItem>
<TabItem Header="Themes">
<views:ThemeControl x:Name="Themes" />
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
@@ -13,14 +10,16 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NLog;
using NLog.Targets.Wrappers;
using Sandbox;
using Torch.API;
using Torch.API.Managers;
using Torch.Server.Managers;
using Timer = System.Timers.Timer;
using MessageBoxResult = System.Windows.MessageBoxResult;
namespace Torch.Server
{
@@ -36,19 +35,38 @@ namespace Torch.Server
{
_config = (TorchConfig)server.Config;
_server = server;
//TODO: data binding for whole server
DataContext = server;
InitializeComponent();
AttachConsole();
Left = _config.WindowPosition.X;
Top = _config.WindowPosition.Y;
Width = _config.WindowSize.X;
Height = _config.WindowSize.Y;
//TODO: data binding for whole server
DataContext = server;
Chat.BindServer(server);
PlayerList.BindServer(server);
Plugins.BindServer(server);
LoadConfig((TorchConfig)server.Config);
Themes.uiSource = this;
Themes.SetConfig(_config);
Title = $"{_config.InstanceName} - Torch {server.TorchVersion}, SE {server.GameVersion}";
}
private void AttachConsole()
{
const string target = "wpf";
var doc = LogManager.Configuration.FindTargetByName<FlowDocumentTarget>(target)?.Document;
if (doc == null)
{
var wrapped = LogManager.Configuration.FindTargetByName<WrapperTargetBase>(target);
doc = (wrapped?.WrappedTarget as FlowDocumentTarget)?.Document;
}
ConsoleText.Document = doc ?? new FlowDocument(new Paragraph(new Run("No target!")));
ConsoleText.TextChanged += (sender, args) => ConsoleText.ScrollToEnd();
}
public void LoadConfig(TorchConfig config)
@@ -59,19 +77,22 @@ namespace Torch.Server
_config = config;
Dispatcher.Invoke(() =>
{
InstancePathBox.Text = config.InstancePath;
//InstancePathBox.Text = config.InstancePath;
});
}
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
_server.GetManager<InstanceManager>().SaveConfig();
_server.Start();
_server.DedicatedInstance.SaveConfig();
Task.Run(() => _server.Start());
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
_server.Stop();
var result = MessageBox.Show("Are you sure you want to stop the server?", "Stop Server", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
_server.Invoke(() => _server.Stop());
}
protected override void OnClosing(CancelEventArgs e)
@@ -83,6 +104,8 @@ namespace Torch.Server
if (_server?.State == ServerState.Running)
_server.Stop();
Process.GetCurrentProcess().Kill();
}
private void BtnRestart_Click(object sender, RoutedEventArgs e)
@@ -98,7 +121,7 @@ namespace Torch.Server
return;
_config.InstancePath = name;
_server.GetManager<InstanceManager>().LoadInstance(_config.InstancePath);
_server.Managers.GetManager<InstanceManager>().LoadInstance(_config.InstancePath);
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Controls;
namespace Torch.Server.Views.ValidationRules
{
public class ListConverterValidationRule : ValidationRule
{
public Type Type { get; set; }
/// <inheritdoc />
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var converter = TypeDescriptor.GetConverter(Type);
var input = ((string)value).Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in input)
{
try
{
converter.ConvertFromString(item);
}
catch
{
return new ValidationResult(false, $"{item} is not a valid value.");
}
}
return ValidationResult.ValidResult;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Torch.Server.Views.ValidationRules
{
public class NumberValidationRule : ValidationRule
{
/// <inheritdoc />
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (!float.TryParse(value?.ToString(), out _))
return new ValidationResult(false, "Not a number.");
return ValidationResult.ValidResult;
}
}
}

View File

@@ -0,0 +1,44 @@
<Window x:Class="Torch.Server.WorldGeneratorDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Torch.Server"
xmlns:views="clr-namespace:Torch.Server.Views"
xmlns:views1="clr-namespace:Torch.Views;assembly=Torch"
mc:Ignorable="d"
Title="WorldGeneratorDialog" Height="500" Width="700">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0" x:Name="PremadeCheckpoints" ScrollViewer.CanContentScroll="False" HorizontalContentAlignment="Center" Margin="3" SelectionChanged="PremadeCheckpoints_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate DataType="local:PremadeCheckpointItem">
<StackPanel HorizontalAlignment="Center">
<Label Content="{Binding Name}" HorizontalAlignment="Center"/>
<Image Stretch="Uniform" MaxHeight="100" HorizontalAlignment="Center">
<Image.Source>
<BitmapImage UriSource="{Binding Icon}"/>
</Image.Source>
</Image>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Column="1" Margin="3">
<Grid.RowDefinitions>
<RowDefinition Height ="Auto"/>
<RowDefinition Height ="*"/>
<RowDefinition Height ="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Label Content="World Name: "/>
<TextBox x:Name="WorldName" Width="300" Margin="3"/>
</StackPanel>
<views1:PropertyGrid Grid.Row="1" x:Name="SettingsView" Margin="3"/>
<Button Grid.Row="2" Content="Create World" Click="ButtonBase_OnClick" Margin ="3"/>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using NLog;
using Sandbox.Definitions;
using Sandbox.Engine.Networking;
using Sandbox.Game.World;
using Torch.Server.Managers;
using Torch.Server.ViewModels;
using Torch.Utils;
using VRage;
using VRage.Dedicated;
using VRage.FileSystem;
using VRage.Game;
using VRage.Game.Localization;
using VRage.Utils;
namespace Torch.Server
{
/// <summary>
/// Interaction logic for WorldGeneratorDialog.xaml
/// </summary>
public partial class WorldGeneratorDialog : Window
{
private InstanceManager _instanceManager;
private List<PremadeCheckpointItem> _checkpoints = new List<PremadeCheckpointItem>();
private PremadeCheckpointItem _currentItem;
[ReflectedStaticMethod(Type = typeof(ConfigForm), Name = "LoadLocalization")]
private static Action _loadLocalization;
public WorldGeneratorDialog(InstanceManager instanceManager)
{
_instanceManager = instanceManager;
InitializeComponent();
_loadLocalization();
var scenarios = MyLocalCache.GetAvailableWorldInfos(Path.Combine(MyFileSystem.ContentPath, "CustomWorlds"));
foreach (var tup in scenarios)
{
string directory = tup.Item1;
MyWorldInfo info = tup.Item2;
string localizedName = MyTexts.GetString(MyStringId.GetOrCompute(info.SessionName));
var checkpoint = MyLocalCache.LoadCheckpoint(directory, out _);
checkpoint.OnlineMode = MyOnlineModeEnum.PUBLIC;
_checkpoints.Add(new PremadeCheckpointItem { Name = localizedName, Icon = Path.Combine(directory, "thumb.jpg"), Path = directory, Checkpoint = checkpoint});
}
/*
var premadeCheckpoints = Directory.EnumerateDirectories(Path.Combine("Content", "CustomWorlds"));
foreach (var path in premadeCheckpoints)
{
var thumbPath = Path.GetFullPath(Directory.EnumerateFiles(path).First(x => x.Contains("thumb")));
_checkpoints.Add(new PremadeCheckpointItem
{
Path = path,
Icon = thumbPath,
Name = Path.GetFileName(path)
});
}*/
PremadeCheckpoints.ItemsSource = _checkpoints;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
string worldName = string.IsNullOrEmpty(WorldName.Text) ? _currentItem.Name : WorldName.Text;
var worldPath = Path.Combine("Instance", "Saves", worldName);
var checkpoint= _currentItem.Checkpoint;
if (Directory.Exists(worldPath))
{
MessageBox.Show("World already exists with that name.");
return;
}
Directory.CreateDirectory(worldPath);
foreach (var file in Directory.EnumerateFiles(_currentItem.Path, "*", SearchOption.AllDirectories))
{
File.Copy(file, Path.Combine(worldPath, file.Replace($"{_currentItem.Path}\\", "")));
}
checkpoint.SessionName = worldName;
MyLocalCache.SaveCheckpoint(checkpoint, worldPath);
_instanceManager.SelectWorld(worldPath, false);
_instanceManager.LoadInstance(worldPath);
_instanceManager.ImportSelectedWorldConfig();
Close();
}
private void PremadeCheckpoints_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = (PremadeCheckpointItem)PremadeCheckpoints.SelectedItem;
_currentItem = selected;
SettingsView.DataContext = new SessionSettingsViewModel(_currentItem.Checkpoint.Settings);
}
}
public class PremadeCheckpointItem
{
public string Path { get; set; }
public string Name { get; set; }
public string Icon { get; set; }
public MyObjectBuilder_Checkpoint Checkpoint { get; set; }
}
}

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ControlzEx" version="3.0.2.4" targetFramework="net461" />
<package id="MahApps.Metro" version="1.6.1" targetFramework="net461" />
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
<package id="NLog" version="4.4.12" targetFramework="net461" />

View File

@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Torch.Managers.PatchManager;
using Torch.Managers.PatchManager.MSIL;
using Torch.Utils;
@@ -17,6 +19,7 @@ namespace Torch.Tests
public class PatchTest
{
#region TestRunner
private static readonly PatchManager _patchContext = new PatchManager(null);
[Theory]
@@ -48,6 +51,133 @@ namespace Torch.Tests
}
[Fact]
public void TestTryCatchNop()
{
var ctx = _patchContext.AcquireContext();
ctx.GetPattern(TryCatchTest._target).Transpilers.Add(_nopTranspiler);
_patchContext.Commit();
Assert.False(TryCatchTest.Target());
Assert.True(TryCatchTest.FinallyHit);
_patchContext.FreeContext(ctx);
_patchContext.Commit();
}
[Fact]
public void TestTryCatchCancel()
{
var ctx = _patchContext.AcquireContext();
ctx.GetPattern(TryCatchTest._target).Transpilers.Add(TryCatchTest._removeThrowTranspiler);
ctx.GetPattern(TryCatchTest._target).DumpTarget = @"C:\tmp\dump.txt";
ctx.GetPattern(TryCatchTest._target).DumpMode = MethodRewritePattern.PrintModeEnum.Original | MethodRewritePattern.PrintModeEnum.Patched;
_patchContext.Commit();
Assert.True(TryCatchTest.Target());
Assert.True(TryCatchTest.FinallyHit);
_patchContext.FreeContext(ctx);
_patchContext.Commit();
}
private static readonly MethodInfo _nopTranspiler = typeof(PatchTest).GetMethod(nameof(NopTranspiler), BindingFlags.Static | BindingFlags.NonPublic);
private static IEnumerable<MsilInstruction> NopTranspiler(IEnumerable<MsilInstruction> input)
{
return input;
}
private class TryCatchTest
{
public static readonly MethodInfo _removeThrowTranspiler =
typeof(TryCatchTest).GetMethod(nameof(RemoveThrowTranspiler), BindingFlags.Static | BindingFlags.NonPublic);
private static IEnumerable<MsilInstruction> RemoveThrowTranspiler(IEnumerable<MsilInstruction> input)
{
foreach (var i in input)
if (i.OpCode == OpCodes.Throw)
yield return i.CopyWith(OpCodes.Pop);
else
yield return i;
}
public static readonly MethodInfo _target = typeof(TryCatchTest).GetMethod(nameof(Target), BindingFlags.Public | BindingFlags.Static);
public static bool FinallyHit = false;
public static bool Target()
{
FinallyHit = false;
try
{
try
{
// shim to prevent compiler optimization
if ("test".Length > "".Length)
throw new Exception();
return true;
}
catch (IOException ioe)
{
return false;
}
catch (Exception e)
{
return false;
}
finally
{
FinallyHit = true;
}
}
catch (Exception e)
{
throw;
}
}
}
[Fact]
public void TestAsyncNop()
{
var candidates = new List<Type>();
var nestedTypes = typeof(PatchTest).GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Static);
foreach (var nested in nestedTypes)
if (nested.Name.StartsWith("<" + nameof(TestAsyncMethod) + ">"))
{
var good = false;
foreach (var itf in nested.GetInterfaces())
if (itf.FullName == typeof(IAsyncStateMachine).FullName)
{
good = true;
break;
}
if (good)
candidates.Add(nested);
}
if (candidates.Count != 1)
throw new Exception("Couldn't find async worker");
var method = candidates[0].GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (method == null)
throw new Exception("Failed to find state machine move next instruction, cannot proceed");
var ctx = _patchContext.AcquireContext();
ctx.GetPattern(method).Transpilers.Add(_nopTranspiler);
ctx.GetPattern(method).DumpTarget = @"C:\tmp\dump.txt";
ctx.GetPattern(method).DumpMode = MethodRewritePattern.PrintModeEnum.Original | MethodRewritePattern.PrintModeEnum.Patched;
_patchContext.Commit();
Assert.Equal("TEST", TestAsyncMethod().Result);
_patchContext.FreeContext(ctx);
_patchContext.Commit();
}
private async Task<string> TestAsyncMethod()
{
var first = await Task.Run(() => "TE");
var last = await Task.Run(() => "ST");
return await Task.Run(() => first + last);
}
public class TestBootstrap
{
@@ -82,7 +212,7 @@ namespace Torch.Tests
if (_targetAssert == null)
throw new Exception($"{t.FullName} must have a method named AssertNormal");
_instance = !_targetMethod.IsStatic ? Activator.CreateInstance(t) : null;
_targetParams = (object[])t.GetField("_targetParams", flags)?.GetValue(null) ?? new object[0];
_targetParams = (object[]) t.GetField("_targetParams", flags)?.GetValue(null) ?? new object[0];
}
private void Invoke(MethodBase i, params object[] args)
@@ -185,10 +315,11 @@ namespace Torch.Tests
_patchTest.Add(new TestBootstrap(type));
}
public static IEnumerable<object[]> Prefixes => _patchTest.Where(x => x.HasPrefix).Select(x => new object[] { x });
public static IEnumerable<object[]> Transpilers => _patchTest.Where(x => x.HasTranspile).Select(x => new object[] { x });
public static IEnumerable<object[]> Suffixes => _patchTest.Where(x => x.HasSuffix).Select(x => new object[] { x });
public static IEnumerable<object[]> Combo => _patchTest.Where(x => x.HasPrefix || x.HasTranspile || x.HasSuffix).Select(x => new object[] { x });
public static IEnumerable<object[]> Prefixes => _patchTest.Where(x => x.HasPrefix).Select(x => new object[] {x});
public static IEnumerable<object[]> Transpilers => _patchTest.Where(x => x.HasTranspile).Select(x => new object[] {x});
public static IEnumerable<object[]> Suffixes => _patchTest.Where(x => x.HasSuffix).Select(x => new object[] {x});
public static IEnumerable<object[]> Combo => _patchTest.Where(x => x.HasPrefix || x.HasTranspile || x.HasSuffix).Select(x => new object[] {x});
#endregion
#region PatchTests
@@ -220,7 +351,8 @@ namespace Torch.Tests
{
yield return new MsilInstruction(OpCodes.Ldnull);
yield return new MsilInstruction(OpCodes.Ldc_I4_1);
yield return new MsilInstruction(OpCodes.Stfld).InlineValue(typeof(StaticNoRetNoParm).GetField("_transpileHit", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public));
yield return new MsilInstruction(OpCodes.Stfld).InlineValue(typeof(StaticNoRetNoParm).GetField("_transpileHit",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public));
foreach (MsilInstruction i in instructions)
yield return i;
}
@@ -255,7 +387,7 @@ namespace Torch.Tests
private class StaticNoRetParam
{
private static bool _prefixHit, _normalHit, _suffixHit;
private static readonly object[] _targetParams = { "test", 1, new StringBuilder("test1") };
private static readonly object[] _targetParams = {"test", 1, new StringBuilder("test1")};
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Prefix(string str, int i, StringBuilder o)
@@ -306,8 +438,8 @@ namespace Torch.Tests
private class StaticNoRetParamReplace
{
private static bool _prefixHit, _normalHit, _suffixHit;
private static readonly object[] _targetParams = { "test", 1, new StringBuilder("stest1") };
private static readonly object[] _replacedParams = { "test2", 2, new StringBuilder("stest2") };
private static readonly object[] _targetParams = {"test", 1, new StringBuilder("stest1")};
private static readonly object[] _replacedParams = {"test2", 2, new StringBuilder("stest2")};
private static object[] _calledParams;
[MethodImpl(MethodImplOptions.NoInlining)]
@@ -316,16 +448,16 @@ namespace Torch.Tests
Assert.Equal(_targetParams[0], str);
Assert.Equal(_targetParams[1], i);
Assert.Equal(_targetParams[2], o);
str = (string)_replacedParams[0];
i = (int)_replacedParams[1];
o = (StringBuilder)_replacedParams[2];
str = (string) _replacedParams[0];
i = (int) _replacedParams[1];
o = (StringBuilder) _replacedParams[2];
_prefixHit = true;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Target(string str, int i, StringBuilder o)
{
_calledParams = new object[] { str, i, o };
_calledParams = new object[] {str, i, o};
_normalHit = true;
}
@@ -380,6 +512,7 @@ namespace Torch.Tests
Assert.True(_prefixHit, "Failed to prefix");
}
}
#endregion
}
#pragma warning restore 414

View File

@@ -86,6 +86,7 @@
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />

Some files were not shown because too many files have changed in this diff Show More