Compare commits

...

759 Commits

Author SHA1 Message Date
z__
0a9f299527 we dont need it anymore 2022-02-09 15:34:13 +07:00
z__
67f25ab20b transform ctor to extension method 2022-02-08 23:36:39 +07:00
z__
ad19a7dc9e better exception for invalid operand type 2022-02-08 23:26:25 +07:00
z__
dd854a159a ok high iq solutions is bad for compatibility 2022-02-04 15:11:49 +07:00
z__
879a373e6a added possibility to call SetConfiguration again if needed 2022-02-04 14:42:37 +07:00
z__
ec1b017946 added nlog custom targets assemblies loading 2022-02-04 14:33:32 +07:00
z__
cf75210304 fixed game analytics logger crash 2022-02-04 13:44:04 +07:00
z__
3696f18714 move nlog config to instance and move default to resources 2022-02-04 13:25:56 +07:00
z__
1f7e4e869d final fixes for warfare 2 2022-02-04 12:09:17 +07:00
z__
ba5b611994 add STA thread back, not being added automatically on local build 2022-02-04 10:38:47 +07:00
z__
2bcf79efdd to trigger autobuild to fix warfare 2 update 2022-02-04 09:47:22 +07:00
z__
d5c101bf19 i believe we're done 2022-02-03 19:49:16 +07:00
z__
e9841b4de1 now should work 2022-02-03 19:43:54 +07:00
z__
aea0011683 maybe 2022-02-03 19:41:13 +07:00
z__
d10528f9fe fixed RID 2022-02-03 19:39:07 +07:00
z__
66ea4d7307 build script update 2022-02-03 19:34:06 +07:00
z__
f6ce40a854 again updated ci buildfile 2022-02-03 19:04:42 +07:00
zznty
bd62d31298 trash dockerfile, it will be on separate repo 2022-02-03 18:50:15 +07:00
z__
b1cf5fb638 i wanna believe this is the last for appveyor.yml 2022-02-03 18:44:02 +07:00
z__
1d6a2a9a60 fix fucking github's new lines 2022-02-03 18:41:55 +07:00
zznty
455be2393e fix build error 2022-02-03 18:38:49 +07:00
zznty
6131a9003b corrently assign version on publish 2022-02-03 18:35:50 +07:00
zznty
2a17c4bc09 Update appveyor.yml
it cannot cache if size larger than 1gb 
so delete unnecessary files
2022-02-03 18:29:25 +07:00
zznty
ccc1df9349 Update appveyor.yml 2022-02-03 18:17:26 +07:00
z__
d80c7b9fba Merge remote-tracking branch 'zznty/master'
# Conflicts:
#	README.md
2022-02-03 18:03:57 +07:00
z__
64c6ce289c appveyor badge in README.md 2022-02-03 18:02:12 +07:00
z__
67de82a103 added appveyor as CI/CD 2022-02-03 18:00:47 +07:00
z__
357e0446df got it working on docker
fixed log closing exception
2022-02-03 17:03:49 +07:00
zznty
f2291a57c3 update discord link 2022-02-02 14:31:52 +07:00
z__
8b862df6ea Merge remote-tracking branch 'zznty/master' 2022-02-02 14:29:10 +07:00
z__
1c92f69bd4 updated NLog to v5
fixed most of issues with world creating/loading
fixed log window lags
fixed some compiler warnings
fixed empty log files creating
fixed logging performance
added better logging of load process
commands autocomplete in torch GUI
chat in torch GUI now has entered history (like console, use up/down arrows)
abstraction of instance manager
session name now correctly displaying in torchSessionManager messages
TorchSession now has loaded world property (as torch now has more control about world load process)
now only dedicated config locks after session start
2022-02-02 14:28:53 +07:00
z__
70833adb44 updated NLog to v5
fixed most of issues with world creating/loading
fixed log window lags
fixed some compiler warnings
fixed empty log files creating
fixed logging performance
added better logging of load process
2022-02-02 14:09:08 +07:00
z__
ab61674b47 integrity analysis for invalid IL 2022-02-02 00:39:43 +07:00
z__
c8ddf691ba fixed world configuration updating 2022-02-02 00:11:15 +07:00
zznty
76afccd9db shitty gc calls removal 2022-02-01 15:11:04 +07:00
zznty
4882dc673a xml config formatting fix 2022-02-01 15:10:51 +07:00
zznty
8546165e59 Merge branch 'tests/try-finally' 2022-02-01 12:04:01 +07:00
z__
4cab214635 fixed planet heightmap loading 2022-02-01 12:01:29 +07:00
z__
9a6253d0ef NRE fixes 2022-02-01 11:39:00 +07:00
zznty
55eac7ecbf test for try/finally transpiler 2022-01-31 20:28:43 +07:00
zznty
057d126658 exception fixes 2022-01-30 19:23:43 +07:00
z__
8efe8ae398 removed useless thing 2022-01-26 11:00:44 +07:00
z__
e2cdd803af remove redundant saving on stop/restart
saving in unload controlled by setting in dedicated config
2022-01-26 10:57:20 +07:00
zznty
b6d8cf07f8 we dont need link to build server anymore 2022-01-23 21:28:52 +07:00
zznty
1b23cd564e Update README.md 2022-01-23 21:28:04 +07:00
z__
f6ef662344 queries and restart fixes 2022-01-23 21:03:32 +07:00
z__
58b9c926ae net6 things 2022-01-22 21:13:38 +07:00
Bishbash777
81d11a32fe Update Jenkinsfile 2022-01-17 00:08:01 +00:00
Bishbash777
eb9afd5e36 Update Jenkinsfile 2022-01-16 23:29:09 +00:00
Bishbash777
1de0b63907 Merge pull request #459 from Bishbash777/master
Net 4.8 upgrade
2022-01-16 23:15:35 +00:00
Bishbash777
f89170c37d Net 4.8 upgrade (#27)
* Framework 4.8 Upgrade and small memory access change

* update tests to 4.8

* update mod_id for torch mod
2022-01-16 23:12:55 +00:00
Bishbash777
73ee686a84 Update Jenkinsfile 2022-01-16 22:51:02 +00:00
Bishbash777
e49910e121 Merge pull request #458 from Bishbash777/torch-migration-p2
point to new (temporary?) build server
2022-01-16 14:09:55 +00:00
Jack Bishop
83805cba77 point to new (temporary?) build server 2022-01-16 14:09:18 +00:00
Bishbash777
32a61d2feb Update Torch.Server.Tests.csproj 2022-01-16 03:38:14 +00:00
Bishbash777
133e20c758 Update Torch.Tests.csproj 2022-01-16 03:30:31 +00:00
Bishbash777
d7c5aa3fa8 Update Torch.API.csproj 2022-01-16 03:24:38 +00:00
Bishbash777
f6d59c75c6 Update Torch.csproj 2022-01-16 03:19:18 +00:00
Bishbash777
fd8c731713 Update Jenkinsfile 2022-01-16 02:53:31 +00:00
Bishbash777
55a552af60 Update Torch.csproj 2022-01-16 02:52:29 +00:00
Bishbash777
770d18859b Update Torch.csproj 2022-01-16 02:48:27 +00:00
Bishbash777
22e60edc1f Update Jenkinsfile 2022-01-16 02:12:33 +00:00
Bishbash777
c97fd07f89 Update README.md 2022-01-15 23:47:00 +00:00
Bishbash777
7e87cdd5a0 Update README.md 2022-01-15 23:44:01 +00:00
Bishbash777
b67eff406f Update README.md 2022-01-15 21:33:10 +00:00
Bishbash777
29a5c09ac7 Update README.md 2022-01-15 21:28:53 +00:00
Bishbash777
a1668abcd4 Merge pull request #457 from Bishbash777/master
Phase 1 migration
2022-01-14 03:03:37 +00:00
Bishbash777
2d8ca29935 Merge pull request #19 from Bishbash777/torch-migration
Torch migration
2022-01-14 03:03:05 +00:00
Jack Bishop
4f9af817a5 phase-1 changes 2022-01-14 02:31:51 +00:00
Jack Bishop
7bd4beb82d update plugin query to new endpoint 2022-01-10 17:02:42 +00:00
Jack Bishop
1ec57bc4c9 QOL - Order plugin controls by name 2021-09-27 01:12:15 +01:00
Bishbash777
63f14c984d Merge pull request #455 from N1Ran/master
Some fixes.
2021-09-03 16:54:54 +01:00
Olatide F
6c227b4a6e merge Friendly 2021-09-03 08:27:25 -04:00
Olatide F
b846112c43 fix for the plugin browser crash 2021-09-03 08:20:46 -04:00
N1Ran
23f9b1507d Merge branch 'master' of https://github.com/TorchAPI/Torch 2021-09-03 02:13:30 -04:00
Bishbash777
2a0555ea4f Update TorchServer.cs 2021-07-28 18:37:45 +01:00
Bishbash777
0395d0bc7f Merge pull request #452 from Bishbash777/199-update
199 update changes and UI updates + various
2021-07-28 18:07:04 +01:00
Bishbash777
f2c03cbc34 fix log in release mode 2021-07-28 18:53:58 +02:00
Bishbash777
147c94fd93 Merge pull request #15 from zznty/fix/xbox
Fixes for EOS & consoles
2021-07-28 09:46:13 +01:00
Bishbash777
0e2ecf16f3 Merge pull request #14 from zznty/fix/duplicate-clientId
fixed crash due to clientId duplicate
2021-07-28 09:43:34 +01:00
Bishbash777
36eb256553 Update ChatManagerClient.cs 2021-07-28 09:06:04 +01:00
Bishbash777
e90ca41c3c properly init new updated PluginBrowser 2021-07-28 09:05:13 +01:00
Bishbash777
5528231cc2 Create PluginDownloader.xaml.cs 2021-07-28 09:04:08 +01:00
Bishbash777
4d01b976f1 Create PluginDownloader.xaml 2021-07-28 09:03:36 +01:00
Bishbash777
ff12b214cd new plugin loader init 2021-07-28 09:03:09 +01:00
Bishbash777
55c9f5296f Added new data into PluginBrowser 2021-07-28 08:58:10 +01:00
Bishbash777
61e1ed7a59 fix for role editor crash when roles are null 2021-07-28 08:55:27 +01:00
Bishbash777
218cd30152 New window views 2021-07-28 08:54:23 +01:00
Bishbash777
bf3b819d02 get setter for Name and a new Installed tag 2021-07-28 08:52:34 +01:00
zznty
b56c8f711c fixed crash due to clientId duplicate
fixes for System.ArgumentException: Key <clientId> already exists
Parameter name: key
2021-07-17 17:36:36 +07:00
zznty
c5c6ef1cd2 xbox compat support 2021-07-09 01:26:39 +07:00
zznty
3e01cecdcc mod.io support and eos fix 2021-05-07 19:02:28 +07:00
N1Ran
966b2f5756 Merge branch 'master' of https://github.com/TorchAPI/Torch 2021-04-25 21:30:51 -04:00
Jimmacle
a5743701ea Merge pull request #442 from Bishbash777/master
Fix server discovery issue on main "Join Game" Screen
2021-04-25 09:09:23 -07:00
Bishbash777
7ea1a523e1 Fix server discovery issue on main "Join Game" Screen 2021-04-25 12:36:46 +01:00
Jimmacle
5445459dc7 Merge pull request #441 from Bishbash777/update-198
fix for 198
2021-04-22 10:29:02 -07:00
Bishbash777
f2eed13473 fix for 198 2021-04-22 18:28:03 +01:00
Olatide F
af894e7507 update 2021-03-21 09:19:01 -04:00
Olatide F
94475f7bd1 update 2021-03-21 09:17:41 -04:00
Jimmacle
b5ca0bf392 Merge pull request #438 from Bishbash777/hotfix-197.181-changes
Add missing parameter into initNetworking if service type is EOS
2021-03-18 07:23:57 -07:00
Bishbash777
fdd8479016 Add missing parameter into initNetworking if service type is EOS 2021-03-18 12:28:49 +00:00
N1Ran
c915b385e3 update 2021-03-18 06:29:00 -04:00
Jimmacle
f451c552ec Merge pull request #431 from theltp/fix/cross-play
final fix for cross-play update
2021-03-18 06:27:55 -04:00
John Gross
0aa17a26ac Update for hotfix 2021-03-11 10:08:27 -08:00
Jimmacle
41b16a6d75 Merge pull request #433 from Bishbash777/stop-cancel-fix
Fix the stop cancel command
2021-02-14 14:16:01 -08:00
Bishbash777
b5f53dd6c6 Fix the stop cancel command 2021-02-14 14:24:43 +01:00
Jimmacle
5cc2d6652d Merge pull request #432 from theltp/fix/cross-play
totally fix for cross play
2021-02-12 14:21:24 -08:00
LTP
6fb6226be5 Merge remote-tracking branch 'theltp/fix/cross-play' into fix/cross-play 2021-02-12 19:36:37 +07:00
LTP
cee5cb3c4d totally fixed 2021-02-12 19:36:28 +07:00
Jimmacle
b6fb470b84 Merge pull request #431 from theltp/fix/cross-play
final fix for cross-play update
2021-02-11 21:27:13 -08:00
Jimmacle
6c74ed3869 Merge branch 'master' into fix/cross-play 2021-02-11 21:26:31 -08:00
LTP
2c44860a70 fixed mistake 2021-02-12 12:08:43 +07:00
LTP
169b543f7d final fix + new config option to change service between Steam and EOS 2021-02-12 12:05:16 +07:00
Jimmacle
efd15fcd62 Merge pull request #428 from theltp/fix/cross-play
fix for cross-play update
2021-02-11 13:34:28 -08:00
LTP
b372230eae idk why it's not working 2021-02-12 00:45:51 +07:00
LTP
e5cb3e5ae0 fix for cross-play update 2021-02-11 23:15:46 +07:00
John Gross
8f30b7605b Update Jenkinsfile 2020-12-03 22:11:23 -08:00
John Gross
bec29c5bb4 Fix world generator - inconsistent paths 2020-12-03 22:01:18 -08:00
John Gross
f84a27d705 Fix more world generator path weirdness 2020-11-25 23:45:04 -08:00
Jimmacle
19f9388a43 Merge pull request #415 from foogs/master
YOUR PRIVACY HAS BEEN DELETED
2020-11-25 21:59:43 -08:00
foogs
f25a412a47 YOUR PRIVACY HAS BEEN DELETED 2020-11-26 08:43:56 +03:00
John Gross
f7a63f17cc Patch steam timeout to 5 seconds to work around callback issues 2020-11-25 16:44:01 -08:00
John Gross
aa5963b29b Merge branch 'master' of https://github.com/TorchAPI/Torch into master 2020-11-25 12:26:26 -08:00
John Gross
15c4276a81 More fixes for 25th 2020-11-25 12:26:18 -08:00
Jimmacle
04b13cbd6c Merge pull request #413 from TorchAPI/command-perms
Command perms
2020-11-25 11:35:51 -08:00
Jimmacle
f6971c715b Merge branch 'master' into command-perms 2020-11-25 11:35:18 -08:00
John Gross
c3e65a5bdd Tentative fix for 2020-11-25 update 2020-11-25 11:32:05 -08:00
Bishbash777
93167f349d new chat event to allow access to msg properties (#411)
* new chat event to allow access to msg properties

* Naming conventions reee

Co-authored-by: Jimmacle <mindstorms11@cox.net>
2020-11-03 14:04:17 -08:00
Jimmacle
b12acddab3 Command perms (#408)
* Add event to override Torch command permission checker

* Correct permission override condition
2020-10-29 14:04:30 -07:00
John Gross
fb9dbf40da Correct permission override condition 2020-10-29 13:27:35 -07:00
John Gross
1b8bddabc8 Add event to override Torch command permission checker 2020-10-29 13:25:23 -07:00
Foogs
a4edbf3bd9 Not count voxel physics pls (#405) 2020-09-18 11:28:08 -07:00
Yuri.Sh
3d13460302 Added ModSDK Logging (#404)
This change was made by @BobDaRoss
I like to help with correct pull request.
2020-08-23 12:40:50 -07:00
N1Ran
fad656e837 Added new features from SE to UI config page (#398)
Co-authored-by: Olatide F <tide1988@live.com>
Co-authored-by: Jimmacle <mindstorms11@cox.net>
2020-08-11 14:07:32 -07:00
Jimmacle
fc986c87a6 Copy and paste bad 2020-08-10 14:40:41 -07:00
Bob Da Ross
80a820ae3d Fixed ModSDK players from joining and getting sandbox file. (#399) 2020-08-10 14:21:13 -07:00
Equinox
e9e446f8ab Fix async command responses (#393)
Fix logs not being written for invalid IL
2020-07-09 18:06:35 -07:00
John Gross
b53194184c Fix world generator dialog 2020-06-25 14:48:46 -07:00
John Gross
bcb921cd9d Add code to automatically delete the bad DLL 2020-04-24 19:27:20 -07:00
John Gross
c1632b6b20 Don't serialize temporary autostart flag 2020-04-23 13:03:29 -07:00
John Gross
88d10d1955 Change KeenLogPatch to handle errors better 2020-04-23 12:59:46 -07:00
Jimmacle
d6fe6234d7 Forced MSBuild too much 2020-04-22 21:22:56 -07:00
Brant Martin
29fcdc0bf8 Add temporary autostart flag for restart commands and autostart commandline switch (#382)
Co-authored-by: Jimmacle <mindstorms11@cox.net>
2020-04-22 12:31:55 -07:00
Brant Martin
48ddc6389f Fix property grid stretching rows with low item count (#381) / Fix Jenkins packaging unnecessary binaries (#383)
* Fix property grid stretching rows with low item count

* Fix Jenkins packaging unnecessary binaries (#383)

* Fix Jenkins packaging DLLs it shouldn't

* Zap more sneaky copy locals

* MSBuild won't cooperate so it will be forced to comply

Co-authored-by: Jimmacle <mindstorms11@cox.net>
2020-04-22 12:29:28 -07:00
John Gross
a3d722eb16 Fix missing sender argument 2020-04-13 06:44:11 -07:00
John Gross
b386e53ff5 Force language version so Jenkins is happy 2020-04-13 05:08:52 -07:00
Jimmacle
1dd444759b Add support for new chat colors (#379)
* Add backwards-compatible support for new chat color system

* Use default

* Set LangVersion properly

* Add backwards compatible ctors in TorchChatMessage

* Fix not setting Font if it's not MyFontEnum

* Use correct color mappings
2020-04-13 04:44:12 -07:00
Brant Martin
76ea5fdbc1 Quick hack to reduce sim slowdown during save (#375) 2020-04-09 08:32:23 -07:00
Jimmacle
035325da22 Assert option and other fixes (#370)
* Add option to control assert logging

* Fix logging errors from calling InitMultithreading before InvokeBeforeRun
2020-03-20 09:44:54 -07:00
Equinox
33b2fa7094 Fix init (#369) 2020-03-19 12:34:47 -07:00
Equinox
b220243b75 Fix frostbite (#368)
Stop using playtest dir
2020-03-19 12:15:32 -07:00
John Gross
f2a077deed Whomst removed the plugin update option check 2020-02-23 09:18:11 -08:00
Brant Martin
d3de1426b2 Fix plugin update race condition 2020-02-23 00:27:09 -05:00
Equinox
86b6b94d25 Merge pull request #360 from sirhamsteralot/master
Add injecting of private class fields into patching methods
2020-02-15 15:19:12 -08:00
Equinox
0a19663c33 Merge branch 'master' into master 2020-02-15 15:15:28 -08:00
sirhamsteralot
d236fd9bd9 Bind to anything 2020-02-15 22:52:38 +01:00
Neko Boi Nick
bc54b79098 Fixed #364 (#365)
* Added Move Function

* Fixed #364

* Quick fix as the issue still sort of persisted.

* Update ModToIdConverter.cs

* Fixed the logic inverse.
2020-02-15 15:56:51 -05:00
Jimmacle
5035fa39b7 Fix inaccurate error tooltip
This enum isn't used for any actual features yet
2020-02-03 16:37:45 -08:00
sirhamsteralot
2395c33995 Formatting Part deux 2019-12-26 14:45:12 +01:00
sirhamsteralot
92ae252210 formatting 2019-12-26 00:07:18 +01:00
sirhamsteralot
5d40cf373d injecting of fields -- equinox 2019-12-25 23:22:22 +01:00
sirhamsteralot
9cbcc3ed85 Merge branch 'master' of git://github.com/TorchAPI/Torch 2019-12-25 23:13:24 +01:00
John Gross
a1f397f9bf Keep config disabled after stopping server 2019-12-21 12:22:29 -08:00
John Gross
540b17448a Fix overwriting Sandbox with stale cached version 2019-12-07 18:15:48 -08:00
John Gross
21fd997554 Support NPC identities in GetGridOwnerName 2019-12-07 14:11:27 -08:00
John Gross
23b0318591 Disable forcing IgnoreLastSession true 2019-11-29 15:58:44 -08:00
John Gross
5727e3b1b8 Rewrite Initializer.Run so it doesn't look like a toddler made it 2019-11-10 15:59:30 -08:00
John Gross
28164b491b Merge branch 'master' of https://github.com/TorchAPI/Torch 2019-11-05 10:19:46 -08:00
John Gross
b02d613a28 Fix crash when logging pre-formatted messages 2019-11-05 10:19:16 -08:00
Brant Martin
c18d6d4795 Unhack the hack 2019-11-01 12:18:21 -04:00
Brant Martin
5780cf7c95 Quick and dirty hack to resolve dll conflicts 2019-10-31 18:18:19 -04:00
John Gross
cc91fa3653 Fix mod downloading, more descriptive sandbox error message 2019-10-26 11:38:38 -07:00
John Gross
52ef0b4d6d Add MySteamService wrapper, add UGC service to init, fix chat intercept 2019-10-24 14:35:06 -07:00
John Gross
ef2be78102 Merge branch 'Patron' 2019-10-24 12:59:37 -07:00
Brant Martin
9c2dc69e3c patch prep 2019-10-23 20:36:15 -04:00
Brant Martin
8107bf131b Revert "Set default tab to chat because of spookyness"
This reverts commit 792151a8b7.
2019-09-29 18:16:26 -04:00
Brant Martin
0220f46741 Save changes to config when using plugin browser 2019-09-25 17:44:11 -04:00
Brant Martin
e4ad517cb1 Handle exceptions when clearing temp files BECAUSE NO ONE REPORTED IT 2019-09-25 17:36:16 -04:00
Brant Martin
792151a8b7 Set default tab to chat because of spookyness 2019-09-25 17:22:53 -04:00
John Gross
fb67b2f3d1 Merge branch 'master' of https://github.com/TorchAPI/Torch 2019-09-21 12:28:50 -07:00
John Gross
30c1f07207 Add file to project 2019-09-21 12:28:15 -07:00
John Gross
562a4554f3 Update instance manager for new config format 2019-09-21 12:19:14 -07:00
N1Ran
9f22b63227 This fixed it (#346)
Let's get this help back online
2019-09-21 10:24:30 -04:00
John Gross
c96e7a284a Merge branch 'master' into Patron 2019-09-19 11:34:09 -07:00
Jimmacle
dbcd696936 How did this get here 2019-09-19 11:10:52 -07:00
John Gross
79368aa6dd Fix MessageBox spam if Torch crashes with UI enabled and autorestart disabled 2019-09-13 11:46:35 -07:00
Brant Martin
d661c893d5 Fix a dumb 2019-09-10 18:17:45 -04:00
N1Ran
ca99404b42 Stop command countdown (#343)
Hopefully this works
2019-09-10 17:58:50 -04:00
Brant Martin
aec03840cc Nothing to see here, move along 2019-09-07 12:10:08 -04:00
John Gross
5630eb14c2 Stop service "properly" 2019-09-01 10:22:42 -07:00
John Gross
6680b0adb2 Fix services? 2019-08-31 23:25:05 -07:00
Brant Martin
ff0d881273 Gracefully handle corrupt plugin zips 2019-08-27 16:43:31 -04:00
Brant Martin
ed5d0ea474 Merge branch 'master' into Patron 2019-08-27 16:34:46 -04:00
Brant Martin
997a3ca31c Adjust behavior of restarts. Closes #337 2019-08-25 11:55:14 -04:00
Brant Martin
ef444730b7 Fix dumb arithmetic error breaking saves. Closes #336 2019-08-25 11:20:35 -04:00
Brant Martin
7bfc6077b9 Set game thread locale to English. Should give English exceptions now. 2019-08-25 11:04:08 -04:00
Brant Martin
624bd5a2a4 Fix references 2019-08-25 11:03:24 -04:00
John Gross
1e19f9aedf Fix save tracking desync 2019-08-22 20:38:35 -07:00
John Gross
1b4c5ace61 Fix saving 2019-08-22 15:46:40 -07:00
John Gross
28037e11df Fix more chat, add missing config UI 2019-08-22 12:36:35 -07:00
John Gross
036f21de81 Fixy fix time, Keen touched the chat system 2019-08-22 11:24:19 -07:00
Brant Martin
43adecaf99 Publictest (#335)
* Fix compiler errors

* Fix Jenkins

* Build public test as Release

* Fix more things

* Oops

* Remove obsolete code

* Fix GameStatePatchShim
2019-08-22 13:25:53 -04:00
Brant Martin
c58e4dccc0 Merge branch 'master' into Patron 2019-08-19 14:40:25 -04:00
Brant Martin
1a1f8b2235 Fix dumb in torch mod 2019-08-16 12:51:51 -04:00
Brant Martin
f1616f6532 Implement kick on restart 2019-08-16 12:48:16 -04:00
Brant Martin
a42f5ab273 Fix editing admins with role editor 2019-08-08 21:58:12 -04:00
Brant Martin
f15139e907 Add -testplugin commandline switch 2019-07-15 11:10:10 -04:00
John Gross
f4399d441d Only disable service mode on Windows Server 2019 2019-07-11 23:35:24 -07:00
John Gross
fd4b49e0d5 Fix remote API load order 2019-07-11 13:26:43 -07:00
John Gross
496a276524 Disable starting as service to fix compatibility with Windows Server 2019 2019-07-10 18:43:32 -07:00
Brant Martin
c831df74d0 Fix regression in PluginManager.
Closes #321
2019-07-10 14:12:04 -04:00
John Gross
e77d53fd41 Better SortedView fix 2019-07-06 17:18:15 -07:00
John Gross
983aff2091 Merge branch 'master' into Patron 2019-07-06 17:00:22 -07:00
John Gross
570bc83a6d Fix remote API, file copying, entity manager sorting 2019-07-06 16:58:36 -07:00
Brant Martin
8dca8bbe65 Fix build issues 2019-06-26 11:18:48 -04:00
Brant Martin
0e91b47dba Merge branch 'Patron' of https://github.com/TorchAPI/Torch into Patron 2019-06-25 16:48:24 -04:00
Brant Martin
3f7e95b502 Log errors when a plugin's control throws exceptions instead of just letting WPF die.
Also add some new types for Essentials
2019-06-25 16:47:54 -04:00
Brant Martin
4f30a47c07 Handle conflicting connection attempts gracefully 2019-06-25 16:11:13 -04:00
John Gross
ecbbd6fbf2 Copy native DLLs if newer 2019-06-24 19:47:08 -07:00
Brant Martin
13f5648963 Remove false positive in debug code 2019-06-23 21:34:20 -04:00
Brant Martin
dfb7314207 Automatically delete and replace steam_api.dll to fix weirdness 2019-06-23 19:33:22 -04:00
Brant Martin
9293801037 Tweak debug output 2019-06-23 08:42:53 -04:00
Brant Martin
d8ac3b2353 Add debug info to InstancePath setter to track down a stupid bug 2019-06-22 23:36:57 -04:00
Jimmacle
056f0e5614 Merge pull request #322 from biscuitWizard/Patron
autoscroll disabler for log
2019-06-21 03:01:32 -07:00
J Iaccarino
ada5418413 Merge pull request #1 from TorchAPI/Patron
Patron
2019-06-21 01:35:51 -07:00
bitMuse
b79e970e66 Added autoscroll disable for log window.. fuck wpf. 2019-06-21 01:34:01 -07:00
John Gross
6fc02edd1e Add save plugin list on first load 2019-06-20 19:06:05 -07:00
Jimmacle
9a8c03106e Merge pull request #320 from biscuitWizard/Patron
Patron
2019-06-20 16:30:22 -07:00
asdfasdfa
4a5e41b747 Added code to finish registering the plugins. 2019-06-17 21:19:51 -07:00
asdfasdfa
649dcf4019 Refactored PluginManager to accept load hints. 2019-06-17 21:17:00 -07:00
Brant Martin
ae3d1262f5 Merge branch 'master' into Patron 2019-06-16 08:31:35 -04:00
John Gross
7b2b0edbdf Merge remote-tracking branch 'origin/PreRelease' 2019-06-16 02:20:43 -07:00
Brant Martin
353746ec04 Exclude abstract types from plugin scan 2019-06-13 09:53:09 -04:00
Brant Martin
2fb9b4173a Revert back to local temp folder 2019-06-13 08:05:03 -04:00
Brant Martin
fe7242f36f more error handling 2019-06-11 06:27:37 -04:00
Brant Martin
8015f8486c Add error handling to world view loading 2019-06-10 21:04:40 -04:00
Brant Martin
92d7f8f578 Merge branch 'Patron' of https://github.com/TorchAPI/Torch into Patron 2019-06-09 17:45:56 -04:00
Brant Martin
fc9a9f8e09 Merge branch 'master' into PreRelease 2019-06-09 17:45:48 -04:00
Brant Martin
f94bfc2850 Merge branch 'master' into Patron 2019-06-09 17:45:37 -04:00
Brant Martin
db283fad15 Fix admin permission/logger errors. Probably. 2019-06-08 09:33:35 -04:00
John Gross
dd7288b787 Merge branch 'master' into Patron 2019-06-06 19:36:40 -07:00
John Gross
a6e41b2a90 Fix native Havok.dll loading 2019-06-06 18:23:39 -07:00
John Gross
0e6ac14b74 Error handling 2019-06-06 18:06:28 -07:00
John Gross
97f380fb7b Fix moar 2019-06-06 18:03:58 -07:00
John Gross
3e5b29c84d I think I actually fixed it 2019-06-06 17:07:25 -07:00
John Gross
5d19cc7cf1 I think I fixed it 2019-06-06 15:48:29 -07:00
Brant Martin
19995eba1a Update protobuf to 2.4 2019-06-06 15:54:05 -04:00
Brant Martin
990ed0f6bf Merge branch 'master' into PreRelease 2019-06-06 15:36:39 -04:00
Brant Martin
dc0a085d86 Merge branch 'master' into Patron 2019-06-06 15:36:32 -04:00
Brant Martin
0a852c49e8 Copy missing dll 2019-06-06 15:35:54 -04:00
Brant Martin
59588517d8 Merge branch 'master' into PreRelease 2019-06-06 15:15:44 -04:00
Brant Martin
3a7cbe48b8 Merge branch 'master' into Patron 2019-06-06 15:15:31 -04:00
Brant Martin
ed298cdfb0 Fix for update 2019-06-06 15:11:39 -04:00
Brant Martin
3925c34610 Remove return statement in plugin loader because I'm stupid 2019-06-06 08:28:23 -04:00
Brant Martin
a6fa112050 Fix random MT error in SortedView 2019-06-05 13:56:28 -04:00
Brant Martin
d97a6a52a4 Handle xml errors in Persistent.Load gracefully 2019-06-05 13:56:08 -04:00
Brant Martin
121846eeae Fix rare crash in entity view sorting 2019-06-03 19:56:29 -04:00
Brant Martin
ebef1edc09 Plugin loading, init, and update now actually catch and report errors. 2019-06-03 16:16:25 -04:00
Brant Martin
bbaf1384ba Allow property grid description field to resize 2019-06-03 00:07:59 -04:00
Brant Martin
7036a4da81 Enable deleting characters when there is no controller attached.
Increase !save timeout to 5 mintues
2019-06-02 11:15:16 -04:00
Brant Martin
91ceb0aa22 Implement ScrollContainerProperty to allow plugins to disable the default scrollviewer wrapper on their controls 2019-05-08 18:34:09 -04:00
John Gross
de12327ac2 Merge branch 'master' into Patron 2019-05-06 23:54:52 -07:00
John Gross
6f65b54883 stop 2019-05-06 23:54:02 -07:00
John Gross
c0ffd7e641 Merge branch 'master' into Patron 2019-05-06 18:59:12 -07:00
John Gross
b0c1ccf9b4 Add NLog-user.cfg to build output 2019-05-06 18:01:40 -07:00
Brant Martin
ed3e1aa846 Merge branch 'master' into Patron 2019-05-06 19:35:11 -04:00
Brant Martin
7f720a1753 Implement user nlog configs, update updater to ignore user config. Resolves #309
Also changes FilesystemManager to use system temp directory when appropriate. (reduces clutter on disk)
2019-05-06 19:34:12 -04:00
Brant Martin
6cbcbc6f3f Save config on backwards-compatible load 2019-05-04 18:42:27 -04:00
Brant Martin
53ae9bc42d Remove debug code 2019-05-04 18:40:17 -04:00
Brant Martin
9813d6946a Refactor plugin loading. All active plugins must now be listed in torch.cfg 2019-05-03 21:39:56 -04:00
John Gross
c7651c9949 Fix NRE if help command is used from server 2019-04-29 11:43:19 -07:00
Brant Martin
34211c4b3f Filter help commands by promote level of requesting user. Closes #306 2019-04-28 18:25:53 -04:00
Brant Martin
8bc4c247e6 Enable the scrollbar on the config page 2019-04-22 15:55:02 -04:00
Brant Martin
92406a051a Add bulk editing of roles 2019-04-22 15:44:34 -04:00
Brant Martin
56f7578d13 Implement chat muting TorchAPI/Essentials#81 2019-04-20 11:43:55 -04:00
Brant Martin
068b074de0 Add player display to main UI window. Ref TorchAPI/Essentials#106 2019-04-20 09:02:21 -04:00
Brant Martin
bb0ee3b861 Add editor for player promotion levels. Resolves #280 2019-04-19 21:30:10 -04:00
John Gross
93bb11c135 Fix crash on invalid command from UI 2019-04-19 11:23:04 -07:00
John Gross
2fd8c17525 Merge branch 'master' into Patron 2019-04-19 08:26:04 -07:00
John Gross
b974487fc4 Fix for command response in UI. How did this ever work? 2019-04-16 11:57:13 -07:00
John Gross
8198243425 Add backwards compatibility to CommandContext.Respond for plugins compiled against older Torch 2019-04-16 10:20:13 -07:00
John Gross
b4addcc125 Add nullchecks to player action buttons 2019-04-15 15:41:13 -07:00
John Gross
2d7893b9de Merge branch 'master' of https://github.com/TorchAPI/Torch 2019-04-15 15:39:09 -07:00
John Gross
60c0ae1049 Fix message target bug 2019-04-15 15:38:34 -07:00
Brant Martin
aaabfa2d01 Merge branch 'master' into Patron 2019-04-10 16:31:19 -04:00
Brant Martin
5c8a1f3677 Don't throw exceptions when saving is abnormal 2019-04-10 16:29:41 -04:00
Brant Martin
b37cd74e56 Fix gross initializer error on first time installations 2019-04-10 14:28:13 -04:00
Brant Martin
af9a31f4ad Keen broke network intercept. Disable it until we decide what to do. 2019-04-09 21:20:56 -04:00
John Gross
7d8838c0ee Add customizable server chat name and color 2019-04-08 19:46:58 -07:00
Brant Martin
2c47cfd60e Enable PatchShim for plugins 2019-03-13 14:41:25 -04:00
Brant Martin
6422f7c576 Fail *gracefully* when Havok runs out of memory. 2019-03-12 20:49:45 -04:00
Brant Martin
369a3217f7 Change plugin update IO logic 2019-03-10 16:45:18 -04:00
Jimmacle
f44bf935d3 Merge pull request #299 from N1Ran/Patron
Fix for the GUI entity count.  Revert just the count from Sorted
2019-03-09 10:25:25 -08:00
N1Ran
0e43eee2f3 Fix for the GUI entity count. Revert just the count from Sorted 2019-03-09 09:51:03 -05:00
Jimmacle
c6121ab590 Merge pull request #298 from TorchAPI/master
Update events/UI to reflect new chat
2019-03-08 12:58:45 -08:00
John Gross
72ceeffdad Update events/UI to reflect new chat 2019-03-05 11:31:00 -08:00
Brant Martin
5eb6e9990c Don't die when updating plugins with no releases 2019-03-03 19:02:20 -05:00
Brant Martin
3d3769cf5a Enable plugin updating from website 2019-03-03 18:46:11 -05:00
Brant Martin
2a64151f67 There's a damn typo in the jenkins file 2019-03-03 17:44:26 -05:00
Brant Martin
34616607a8 Implement Torch auto-update. Sadly does not work for Patron branch. 2019-03-03 17:42:02 -05:00
Brant Martin
796feb05e6 I shouldn't be allowed to have a computer 2019-03-03 15:23:43 -05:00
Brant Martin
14ae58ca0c Versioning should work now 2019-03-03 15:21:57 -05:00
Brant Martin
b555f46f56 Try versioning again 2019-03-03 15:17:00 -05:00
Brant Martin
dc5e7e4cb0 Try new version scheme 2019-03-03 15:01:51 -05:00
Brant Martin
deb0b202bf Merge remote-tracking branch 'origin/master' into Patron 2019-03-03 13:01:46 -05:00
Brant Martin
0a5a70f583 csproj didn't update for some reason 2019-03-02 14:38:52 -05:00
John Gross
f16e825b57 Fix command hiding, remove duplicate event invoke 2019-03-02 11:14:23 -08:00
Brant Martin
263a8229fa Enable hyperlinks.
Sanitize hyperlinks.
2019-03-02 12:14:32 -05:00
Brant Martin
dc7a27a5e3 Merge branch 'master' of https://github.com/TorchAPI/Torch into Patron 2019-03-02 11:42:24 -05:00
Brant Martin
651865f28a Implement plugin browser and downloader 2019-03-02 11:42:08 -05:00
John Gross
27493d3b23 Revert "Replace the stupid MyMultiplayer.GetMemberName with something that actually works."
This reverts commit 8c59098c28.
2019-03-01 20:28:38 -08:00
John Gross
295bbd62b8 Fix window size shenanigans breaking the config 2019-02-28 13:44:14 -08:00
John Gross
5cf3ae1203 Fix command hiding, add new chat properties to TorchChatMessage 2019-02-28 12:33:26 -08:00
John Gross
c650e190fb Clean up mess from last commit 2019-02-28 12:03:26 -08:00
John Gross
5cd5873ec3 Replace chat intercept with patch 2019-02-28 11:57:36 -08:00
John Gross
42a66b04c5 Revert build scripts for merge 2019-02-28 08:28:33 -08:00
Brant Martin
a237185e4c Move all deprecated junk to the deprecated file 2019-02-27 19:11:47 -05:00
Brant Martin
df225a3d2f Add new config options 2019-02-27 17:05:15 -05:00
Brant Martin
8c59098c28 Replace the stupid MyMultiplayer.GetMemberName with something that actually works. 2019-02-27 16:31:28 -05:00
Brant Martin
bbd45df54d fix jenkins for real 2019-02-25 17:03:36 -05:00
Brant Martin
8aa0ccd437 . 2019-02-25 16:58:22 -05:00
Brant Martin
74cdd9d055 fix the thing 2019-02-25 16:56:57 -05:00
Brant Martin
704f202ce8 asdg 2019-02-25 16:24:15 -05:00
John Gross
fb3082094a Revert unnecessary playtest things 2019-02-25 12:47:25 -08:00
Brant Martin
7a71cbf756 branch name 2019-02-25 15:39:27 -05:00
Brant Martin
53c279fa00 Reconfigure jenkins data 2019-02-25 15:38:02 -05:00
Brant Martin
8dc542a31c Merge branch 'Patron' of https://github.com/TorchAPI/Torch into survival 2019-02-25 15:27:34 -05:00
Brant Martin
4dee127ce8 Merge branch 'playtest' of https://github.com/TorchAPI/Torch into survival 2019-02-25 15:27:14 -05:00
Brant Martin
3e341e02c8 Make Torch UI remember its last screen size/position. Fixes #260
(this is Jim's fault)
2019-02-25 14:06:38 -05:00
Brant Martin
a673848089 Implement a truly terrible bulk mod editing mode. 2019-02-25 13:59:13 -05:00
Brant Martin
642186678e Fix new world generator breaking with non-default instance paths #286 2019-02-25 12:55:04 -05:00
Brant Martin
e37357aea5 Don't build in debug please 2019-02-20 21:17:37 -05:00
Brant Martin
967d8ce068 Add independent console option. Various random fixes 2019-02-20 20:20:09 -05:00
Brant Martin
2bb3aa84a7 Fix entity sorting 2019-02-20 20:19:46 -05:00
Brant Martin
82ddd3942b Remove mod textbox 2019-02-20 16:04:59 -05:00
Brant Martin
ac672092f1 Implement delegate caching 2019-02-10 19:14:15 -05:00
Brant Martin
1c6eec61af Add basic, experimental entity sorting 2019-02-10 16:29:08 -05:00
Brant Martin
dda7864c1a Add !uptime command 2019-02-10 14:58:31 -05:00
Brant Martin
ddc13cccec Overhaul NetworkManager.
Old systems of network injection and intercept are deprecated and will be removed in the future!
2019-02-09 12:17:36 -05:00
Brant Martin
95ac2392e8 Fix mod injection 2019-02-09 08:56:46 -05:00
Brant Martin
0122f9e989 Add API for injecting client mods 2019-02-08 13:53:37 -05:00
Tobias K
f265f7e773 Replace mod text box by separate tab with workshop support (#263)
* Implement ModList tab which fetches and displays mod information from the workshop.

* ModListEditor: Implement drag and drop ordering, adding, removing and saving.

* Add SteamWorkshopService to VCS

* Add missing file to SteamworkshopService project.

* ModlistControl: Implement checkbox for hiding/showing dependency mods
disable until config is loaded.
design improvements.

* Add documentation for the new classes.

* Comply to naming conventions.

* Update Torch.Server.csproj

* Fix Mod.IsDependency not being serialized when saving

* Remove superfluous update of mod meta data.
Remove commented section in ConfigControl.xaml.

* Optimized SteamworkshopService according to commit review.

* Move SteamWorkshopService to Torch.Utils.SteamworkshopTools

* Remove debug output.

* Don't break stack trace with custom exception in SteamWorkshopTools.

* User ViewModel base class for ModItemInfo instead of implementing INotifyProperty directly.

* Wrap ModListControl in ScrollViewer.

* Rename SteamWorkshopTools utility to WebAPI.

* Revert steamkit call to use dynamic typing for clarity :/

* Mark webAPI based method for downloading workshop content as obsolete.

* Update Torch project definition.

* Disable building Torch client

* Update readme

* Change init order to ensure paths are initialized for plugins

* Reorder exception logging to reduce duplication

* Use thread safe queues in MtObservableCollectionBase

* Revert "Change init order to ensure paths are initialized for plugins"

This reverts commit 3f803b8107.

* Fix layout of ModListControl

* Combine Invokes to reduce allocations

* Replace string comparisons by string.Equals / string.IsNullOrEmpty

* Replace string comparisons by string.Equals / string.IsNullOrEmpty

* Use MtObservableList for Modlist to avoid race conditions.
2019-02-02 06:26:55 -05:00
Brant Martin
61307ce584 Merge branch 'Patron' 2019-02-02 06:25:40 -05:00
Jimmacle
8f755a5cfc Update Initializer.cs 2019-01-31 11:45:11 -08:00
Jimmacle
c3addd05f7 Keen why did you change the beta password 2019-01-31 08:46:19 -08:00
Jimmacle
bbcfc9fb07 Update jenkins-grab-se.ps1 2019-01-31 08:45:01 -08:00
John Gross
63ac99f97a Revert "Change init order to ensure paths are initialized for plugins"
This reverts commit 3f803b8107.
2019-01-25 13:16:14 -08:00
John Gross
6d29bce267 Use thread safe queues in MtObservableCollectionBase 2019-01-25 12:36:41 -08:00
John Gross
6b039288d5 Reorder exception logging to reduce duplication 2019-01-25 12:31:34 -08:00
John Gross
3f803b8107 Change init order to ensure paths are initialized for plugins 2019-01-25 12:22:30 -08:00
John Gross
814a9def7f Update readme 2019-01-21 16:13:14 -08:00
John Gross
8c7891809e Update readme 2019-01-21 16:11:44 -08:00
John Gross
30729049b3 Give Mr. Parenthesis a friend 2019-01-17 17:02:03 -08:00
John Gross
1a0f80dce7 Actually finish making Jenkins work 2019-01-17 16:07:13 -08:00
John Gross
6973bc8e7d Make Jenkins download playtest branch 2019-01-17 15:58:35 -08:00
John Gross
ae3edd67da Update chat schtuff 2019-01-17 15:52:16 -08:00
John Gross
b66db19c0a Fix post world generation behavior 2019-01-17 15:07:50 -08:00
John Gross
36b931f680 Fix compilation errors, disable XML warnings 2019-01-17 14:51:24 -08:00
John Gross
9c63054926 Add SteamCMD beta arguments 2019-01-17 10:29:05 -08:00
John Gross
276a4522d6 Disable building Torch client 2019-01-17 10:22:09 -08:00
John Gross
4e2e58bb4c Merge branch 'master' into Patron 2019-01-16 21:17:47 -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
ffc6a60ee9 Merge branch 'master' into Patron 2019-01-10 19:11:33 -05:00
Brant Martin
f990d27851 Enable creation of new worlds in Torch 2019-01-10 19:11:19 -05:00
Brant Martin
fa1a968b8f Merge branch 'master' into Patron 2019-01-10 09:29:29 -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
471912759a Merge branch 'master' into Patron 2019-01-06 22:22:16 -05:00
Brant Martin
844d4be96a Rearrange client join auth again 2019-01-06 21:21:36 -05:00
Yannick
0b76ded5aa Jam this in a grid cuz rexxar didnt want to (#267) 2019-01-03 16:15:47 -05:00
sirhamsteralot
76aa95588e Merge branch 'Patron' of https://github.com/TorchAPI/Torch into Patron 2019-01-03 22:11:04 +01:00
Brant Martin
6618752b84 Merge branch 'master' into Patron 2019-01-03 15:59:34 -05:00
sirhamsteralot
af5f39af44 Jam this in a grid cuz rexxar didnt want to 2019-01-03 21:53:26 +01:00
sirhamsteralot
03ef57daeb Merge branch 'Patron' of https://github.com/TorchAPI/Torch 2019-01-03 21:46:43 +01:00
Brant Martin
a059d18195 Add promote/demote buttons to player tab.
Add player promoted event to IMultiplayerManagerServer
2019-01-03 15:41:41 -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
Westin Miller
897f75c069 Merge branch 'new_init' 2017-12-02 12:16:30 -08:00
Westin Miller
8167e04383 Merge branch 'staging' 2017-12-02 12:16:23 -08:00
Westin Miller
72b6d0e7bb Tweaks to versioning scheme
Nuke staging
2017-12-02 12:14:48 -08:00
Westin Miller
039c5d9244 Fields on internal types can be reflected 2017-11-25 23:02:01 -08:00
Westin Miller
7ea982c903 Future methods for loading/unloading sessions.
Fix reflected manager to work with instance methods on internal types.
2017-11-25 17:44:50 -08:00
Westin Miller
f0adeddb66 Split game initialization and starting into a separate thread and file. 2017-11-25 17:25:06 -08:00
Westin Miller
e709b6c321 Tweaked initializer to keep the SpaceEngineersGame instance around. 2017-11-25 15:52:53 -08:00
Westin Miller
1b0dcc9808 Validate auth ticket event exposed to other parts of Torch and plugins 2017-11-24 20:46:41 -08:00
John Gross
fe5dfa0ea7 Another restart fix and invoke tweaks 2017-11-23 10:46:28 -08:00
John Gross
25e6f27854 Fixes for 2017-11-17 major 2017-11-17 12:11:11 -08:00
Westin Miller
c07a01a427 Patcher correctly translates catch clauses 2017-11-11 03:50:25 -08:00
Westin Miller
11bc7cb60c Trace level messages
Collection change events are all deferred, and replaced with reset if it makes sense.
2017-11-09 09:42:48 -08:00
Westin Miller
6e3b7e7a04 Merge branch 'fix-dependent-dll-loading' of https://github.com/susu/Torch into staging 2017-11-06 19:42:31 -08:00
Westin Miller
ff58cf5b19 Merge pull request #161 from TorchAPI/plugin-wpf-controls
Pluggable WPF Controls for the Entity Manager UI
2017-11-06 19:36:24 -08:00
Marton Suranyi
25a708a3d4 PluginManager: load dependent DLLs first
When there are dependencies among a plugin's DLLs,
those dependencies needs a custom resolving.
2017-11-06 20:30:48 +01:00
Marton Suranyi
004dcc19dc PluginManager: make zip plugins working
Let Torch to load the plugin's assembly.
2017-11-06 20:28:15 +01:00
Marton Suranyi
ac95f5f89c PluginLoader: load only files with DLL extension from directories and zips
I had several Xy.dll.xml files there
and it was tried to be loaded as an Assembly.
2017-11-06 20:24:09 +01:00
Westin Miller
0fc9b49fba Didn't work well 2017-11-04 10:19:33 -07:00
Westin Miller
600e73ad43 Post transpilers (happen after prefix and suffix)
More debugging options
More robust inline writing and reading
2017-11-04 10:09:22 -07:00
Westin Miller
0bc0b0dc77 Fix integrity analysis to work with loops 2017-11-04 04:18:47 -07:00
Westin Miller
86f62e1f37 Plugin controls are scroll viewers. 2017-11-03 21:16:49 -07:00
Westin Miller
7850b8368a Fix hard clean
Remove leftover logging
2017-11-03 20:15:11 -07:00
Westin Miller
496bde733f Kill repeated file 2017-11-03 19:30:38 -07:00
Westin Miller
8b98deafca Fixed patch manager to emit try-catch-finally blocks.
Solves issue with PBs not running
2017-11-02 22:49:17 -07:00
Westin Miller
b3ab0cbd74 Fix loading plugins from ZIP files 2017-11-01 19:50:02 -07:00
Westin Miller
462eb77e0d Try to load .exe files when looking for assemblies in test framework 2017-11-01 18:19:22 -07:00
Westin Miller
d5702d3065 Progress report when patching 2017-11-01 17:47:21 -07:00
Westin Miller
c8377b318e EntityControlManager partial rewrite to do lazy updating
Auto commit patches once SE instance has been created
2017-11-01 05:08:17 -07:00
Westin Miller
6814a833be Removed some duplicated code. 2017-11-01 01:03:10 -07:00
Westin Miller
98aae10126 Tools for debugging patch transpilers. 2017-10-31 23:42:25 -07:00
Westin Miller
0558675132 If plugins or Torch call the static ctor of MyEntities too early the game doesn't break.
Freeze isn't fatal when compiled with DEBUG.  (So you can use breakpoints)
2017-10-31 20:55:48 -07:00
Westin Miller
c8f42e8a48 Method to determine if a MtObservableCollection is observed by anyone. 2017-10-31 17:39:45 -07:00
Westin Miller
d30d16b855 Fixed issue with not being on dispatcher thread 2017-10-31 04:43:50 -07:00
Westin Miller
178957642c Revert NLog level back to Info 2017-10-31 04:22:10 -07:00
Westin Miller
cd77fe74d5 Pluggable system for providing WPF controls inside the Entity Manager 2017-10-31 04:18:27 -07:00
Westin Miller
90c91c3ebc Initial version of profiler
Moved reflected manager into separate files
Extracted MtObservableEvent
Added a patch to Keencode that lets us call the static cctor of MyEntities in the wrong spot
2017-10-30 13:48:04 -07:00
Westin Miller
11dbf83faf NLog works again 2017-10-30 13:43:35 -07:00
Westin Miller
b7fa57c9b7 Non-indexed remove causes a full reset (@Xero's issue)
Removed ignore last session option.  Implicitly true now.
Asteroid generation options (fixes #151)
2017-10-29 09:53:20 -07:00
Westin Miller
7a63527d8f Archive before test 2017-10-28 00:06:00 -07:00
Westin Miller
4b33bedccd Disable NLog based KeenLog.
NLog is being derpy
2017-10-27 23:28:15 -07:00
Westin Miller
473637ceaf Fixed issue where constructor overloads killed me 2017-10-27 20:10:19 -07:00
Westin Miller
6f5142393b MT observable collections
(UI sync fix)
2017-10-27 17:52:27 -07:00
Westin Miller
fdc20d4e9d Keen's logs now go to Logs/Keen-*
Various formatting options
2017-10-27 14:13:56 -07:00
Westin Miller
4d0dcede41 Log unhandled exceptions very verbosely 2017-10-26 12:41:03 -07:00
Westin Miller
4ed262a330 Report loader exceptions 2017-10-26 12:16:28 -07:00
Westin Miller
48f0f81f12 Hard clean v2 2017-10-26 10:54:19 -07:00
Westin Miller
7b9f2d680a Hard clean 2017-10-26 10:51:56 -07:00
Westin Miller
c69537b173 Warning guard around some unsafe stuff 2017-10-26 10:11:51 -07:00
Westin Miller
794a4a23d3 VRage.Steam snuck in 2017-10-26 09:55:49 -07:00
Westin Miller
998ff6a13a Keen removed infinario 2017-10-26 09:17:36 -07:00
Westin Miller
4ff4a60106 Merge branch 'event-mgr' into staging 2017-10-22 03:31:46 -07:00
Westin Miller
eaaca5b003 Register aux assemblies when they aren't loaded from a zip 2017-10-21 04:21:04 -07:00
Westin Miller
388b4731c7 Merge pull request #137 from TorchAPI/observable-collections
Refactor observable collections and add read-only proxies for them
2017-10-12 02:23:48 -07:00
Westin Miller
f285d67c87 Fixed synchronization in assembly resolver
Close #145
2017-10-10 03:58:21 -07:00
Westin Miller
bc1a612a20 Add server state to ITorchServer
Fixes #101
2017-10-10 03:54:23 -07:00
Westin Miller
b67879577d Remove example shim pending writing actual shims 2017-10-10 03:49:48 -07:00
Westin Miller
2b5b9d44e6 Fix load order issues with reflected manager and patches 2017-10-10 03:08:41 -07:00
Westin Miller
3e48638d8c Merge branch 'app_domains' into event-mgr 2017-10-10 03:01:18 -07:00
Westin Miller
3307d2d23d Patch manager supports communicating method cancel state
Game state patch split out into a patch shim
Special NS for events
Core and auxillary assembly concepts
Reflected manager stops working for mods/scripts
2017-10-10 02:47:45 -07:00
Westin Miller
62d73cbf96 Moved event stuff to a non-manager NS.
Core assembly concept: Assemblies that will never need to get unloaded (Torch.API, Torch, Torch.Server)
Event shims and patch shims on the core assemblies.
2017-10-09 20:52:22 -07:00
Westin Miller
2004f71290 Patch manager state is static, registration methods are non-static.
Patch manager tracks patch context ownership by assembly internally
2017-10-09 01:06:01 -07:00
Westin Miller
013dc43c2f Tagging interface for event handlers
Support events in the TorchAPI
Static registry, non-static registration methods.
Support unregister by assembly.
2017-10-09 00:42:21 -07:00
Westin Miller
716e6cbc04 Event dispatch and registration service 2017-10-08 05:32:16 -07:00
Westin Miller
9e81b6316f Better debugging for freezing 2017-10-05 12:56:16 -07:00
Westin Miller
d709bf68dd New system for resolving binaries 2017-10-04 21:40:42 -07:00
Westin Miller
c14b8ed23a Fixes #138 2017-10-04 18:57:47 -07:00
John Gross
7ba6fb5a2e Add observable read only list implementation 2017-10-03 11:44:35 -07:00
John Gross
4f1a03811a Refactor property changed implementations 2017-10-02 23:44:06 -07:00
John Gross
0946d5a138 Update README.md 2017-09-28 13:57:19 -07:00
Westin Miller
6f650c8bbd More MSIL utilities 2017-09-23 01:29:06 -07:00
John Gross
ad1502e998 Small tweaks 2017-09-22 20:08:47 -07:00
Westin Miller
bb42dd026c Some more misc Msil stuff 2017-09-22 14:45:07 -07:00
Westin Miller
95b6c9dfe5 Fix so plugins properly register commands. 2017-09-22 14:30:21 -07:00
Westin Miller
9b1754a431 Merge pull request #113 from TorchAPI/session-mgr-cmp
Managers for Clients
2017-09-22 00:40:14 -07:00
Westin Miller
0574d59e12 Merge branch 'staging' into session-mgr-cmp 2017-09-22 00:22:45 -07:00
Westin Miller
4f7c35dfcf Use the correct binaries 2017-09-21 23:41:29 -07:00
Westin Miller
967384ccfe Clean before build 2017-09-21 23:07:37 -07:00
John Gross
b906a32e23 Merge pull request #121 from TorchAPI/plugins
Plugin loader improvements
2017-09-21 22:58:02 -07:00
John Gross
9b9a4c5ee1 Merge branch 'staging' into plugins 2017-09-21 22:56:32 -07:00
Westin Miller
13f3e7ee11 Jenkins only releases from master, and builds in Release only on master.
Packages don't contain PDB files on master builds either.
2017-09-21 22:54:48 -07:00
John Gross
b7f2a62b3c Expose read-only collections in PluginManager instead of full collections 2017-09-21 22:30:48 -07:00
John Gross
1f4197ce67 Merge branch 'staging' into plugins 2017-09-21 20:26:32 -07:00
John Gross
f377d044d6 Observable type improvements 2017-09-21 20:15:18 -07:00
Westin Miller
205dd1a201 Merge pull request #120 from blaho/session-mgr-cmp
Exposed information about banned players
2017-09-20 08:24:28 -07:00
Tomas Blaho
9c505c4f5d banned player list made readonly, lobby fakes support 2017-09-20 17:16:15 +02:00
Tomas Blaho
eb7f7f4244 MultiplayerManagerLobby doesn't implement IMultiplayerManagerServer 2017-09-20 17:05:23 +02:00
Tomas Blaho
f1fc49d276 Merge branch 'staging' into session-mgr-cmp
# Conflicts:
#	Torch.API/Managers/IMultiplayerManager.cs
#	Torch/Managers/MultiplayerManager.cs
2017-09-20 16:13:38 +02:00
Tomas Blaho
d8e2072493 documentation added 2017-09-20 13:03:06 +02:00
Tomas Blaho
96f813a17b IMultiplayerManager: added list of banned steam ID's 2017-09-20 12:34:09 +02:00
John Gross
a97542e649 Add "Open Folder" functionality to plugins tab, crash-safe plugin updating 2017-09-14 21:09:20 -07:00
Westin Miller
5eceb21ec7 Comments 2017-09-12 01:44:13 -07:00
Westin Miller
a61b646295 ReflectedMethodInfo allows non-public type names.
MsilInstructionExtensions to make life easier
2017-09-11 22:32:37 -07:00
Westin Miller
373c476d2d Better guessing on the generic operand type 2017-09-11 20:26:33 -07:00
Westin Miller
b1145c8926 Utility method to invert common load and store instructions 2017-09-11 19:50:07 -07:00
Westin Miller
0810e76474 Once the game is created we can patch it with impunity. 2017-09-11 18:55:09 -07:00
Westin Miller
57acb274c6 Don't crash when modifying constructor
Tweak log level of assembly resolver.
Events relating to game initialization and shutdown.
Plugin manager loads plugins right before the dependency manager is attached.
2017-09-11 18:28:53 -07:00
Westin Miller
e57f885d3b Log errors in ReflectedManager 2017-09-11 05:19:02 -07:00
Westin Miller
0073e101dd Fixed issues with operand replacing, and branch instructions. 2017-09-11 05:18:07 -07:00
Westin Miller
b42d43c0e1 Redirect Keen console to Info, all else to trace. 2017-09-11 05:17:21 -07:00
Westin Miller
9d8988a2ec MyLog logs to the Torch logging system
Fixed issue with labels not being transferred correctly when transpiling.
2017-09-10 17:40:43 -07:00
Westin Miller
aa784c121b Null protection in multiplayer manager detach 2017-09-10 15:17:50 -07:00
Westin Miller
a36e8a4065 Merge branch 'staging' into session-mgr-cmp 2017-09-10 15:11:08 -07:00
John Gross
3fd7b66905 Rewrite plugin loader/updater to support loading from .zip and more complex versions. 2017-09-10 01:44:15 -07:00
John Gross
44ebcc5d25 Merge pull request #110 from TorchAPI/patching
Patch Manager
2017-09-10 04:05:50 -04:00
Westin Miller
9a68ed6bd0 Added patching tests
Fixed error when reverting patches
Made the LoggingILGenerator not break without a logger
2017-09-09 23:30:49 -07:00
Westin Miller
4f84cd8963 Normal logging for patch manager
Fix: RE treating constructors as normal methods
2017-09-09 22:15:42 -07:00
Westin Miller
837b56462f Use reflected manager for patch internals 2017-09-09 00:50:45 -07:00
Westin Miller
9c3a22c556 Method patching framework 2017-09-09 00:37:34 -07:00
Westin Miller
9471d83a45 Merge pull request #108 from TorchAPI/staging
Prepare Release v1.2.0
2017-09-07 19:56:23 -07:00
Westin Miller
52b225d944 Merge branch 'master' into staging 2017-09-07 15:39:04 -07:00
Westin Miller
c8f0a61209 Provide DS config path to Save
This keeps MyFileSystem from getting initialized too early.
2017-09-07 14:54:57 -07:00
John Gross
59c3e9eb54 Merge pull request #107 from TorchAPI/patch-96
Proper scrolling and formatting for the ChatPanel.
2017-09-07 13:39:15 -04:00
Westin Miller
d20d68b831 Proper scrolling and formatting for the ChatPanel.
- Users are blue, server is dark blue.
- Autoscroll when it's scrolled to the bottom, else leave it alone.
2017-09-07 02:54:10 -07:00
Westin Miller
cfda1f8eef Fixes issues with the assembly resolver getting disposed when launched in nogui mode.
This should solve #102, #104.
2017-09-07 02:03:48 -07:00
Westin Miller
2fb222125a Merge pull request #106 from TorchAPI/reflected-internals
Specify internal types by Assembly Qualified Name for reflection
2017-09-06 20:58:29 -07:00
John Gross
c7c3c00783 Merge branch 'staging' into reflected-internals 2017-09-06 23:57:34 -04:00
Westin Miller
dcc130e2cf Fixed bad exit code.
Fixed creds GUID
2017-09-06 20:33:37 -07:00
Westin Miller
491f3d3af4 Auto-release 2017-08-31 19:36:01 -07:00
Westin Miller
140000df55 Test-time reflected event checker
Server UI components work with new system
Events for loading and unloading TorchSessions
Commands report information again.
Catch, log, and rethrow errors that occur in Torch-handled SE events
2017-08-31 16:27:55 -07:00
Westin Miller
4b2fee7614 Offline mode fallbacks for the chat manager. 2017-08-31 16:27:54 -07:00
Westin Miller
2c7b522378 Proper delegate naming 2017-08-31 16:27:54 -07:00
Westin Miller
d9ef60d4e8 Client manager components
- ReflectedManager now supports MemberInfo and event replacement
- New chat manager interfaces for both client and server
- New multiplayer manager interfaces for both client and server
2017-08-31 16:27:54 -07:00
Westin Miller
178fcb8164 Specify internal types by Assembly Qualified Name for reflection 2017-08-31 16:22:38 -07:00
John Gross
3d6806b63a Merge pull request #88 from szczepix/patch-1
When path contains spaces
2017-08-31 18:01:00 -04:00
Szczepan Zaskalski
22e3019610 Fixed batch mklink command argument 2017-08-31 22:25:10 +02:00
Szczepan Zaskalski
7937cbd122 When path contains spaces 2017-08-31 22:18:46 +02:00
John Gross
2bef34ee5b Merge pull request #94 from TorchAPI/jenkins
Unified versions.
2017-08-31 01:37:24 -04:00
Westin Miller
efe7236d31 TorchVersion is read-only 2017-08-30 22:35:50 -07:00
Westin Miller
67dba9c820 PluginAttribute now lets you get the version from the loaded assembly
Documentation in PluginAttribute
2017-08-30 14:13:49 -07:00
Westin Miller
52c509aba0 Salt after revision. 2017-08-30 13:38:43 -07:00
Westin Miller
437c7d293e Make local development versions clearly local dev 2017-08-30 11:19:13 -07:00
Westin Miller
2b6ce4f25b Unified versions. 2017-08-29 19:54:06 -07:00
John Gross
64f123abe9 Specify PR base branch 2017-08-27 09:08:50 -07:00
John Gross
599a98bceb Merge pull request #87 from TorchAPI/server-refactor
Server initialization refactor
2017-08-24 23:12:02 -04:00
John Gross
2cd1b8bd4e Merge branch 'staging' into server-refactor
# Conflicts:
#	Torch.Server/Torch.Server.csproj
#	Torch/TorchBase.cs
2017-08-24 17:44:43 -07:00
John Gross
c0be9c25da Refactor server initialization for service support 2017-08-24 17:30:38 -07:00
John Gross
5cea66374f Merge pull request #84 from TorchAPI/jenkins
Generate Assembly Versions on Build
2017-08-23 16:44:29 -04:00
Westin Miller
ee1c270c68 Merge branch 'staging' into jenkins 2017-08-23 03:02:26 -07:00
Westin Miller
4ab08e2faf Automatically create zip file 2017-08-23 01:17:19 -07:00
Westin Miller
dd094edb88 Using UTC time 2017-08-22 23:25:18 -07:00
Westin Miller
56e45236d8 Create empty generated files to MSBuild doesn't freak. 2017-08-22 23:03:44 -07:00
Westin Miller
be9a8c5839 Transform Templates on Build
Templates for all assemblies
2017-08-22 22:31:24 -07:00
John Gross
8c11baf3b9 Merge pull request #81 from TorchAPI/sessions-manager-71
Session Management System
2017-08-22 11:09:04 -04:00
Westin Miller
6ce679bd83 Proper delegate naming 2017-08-22 08:06:30 -07:00
Westin Miller
a4b1b9bb96 CurrentSession available directly from TorchBase
SteamService is now properly injected for clients, and reports errors
Obsolete tags on the manager properties in TorchBase
2017-08-22 08:06:30 -07:00
Westin Miller
91ad78e6a2 Private session field of proper type
Project Setup
2017-08-22 08:06:30 -07:00
Westin Miller
4a68d66ab0 Session management system
- Managers bound to the session instead of to Torch
- TorchSession automatic creation and destruction
- Automatic manager creation for sessions
2017-08-22 08:06:30 -07:00
John Gross
4cb50b556f Merge pull request #80 from TorchAPI/shutdown-crash-fix
Check if thread is running before suspending
2017-08-22 01:31:02 -04:00
John Gross
b5f73a99cc Use Thread.IsAlive property 2017-08-21 20:39:54 -07:00
John Gross
e9476a59e8 Fix ambiguous reference resolution 2017-08-21 20:15:16 -07:00
John Gross
f48f23c2eb Check if thread is running before suspending 2017-08-21 18:57:35 -07:00
John Michael Gross
ddf465d8c9 Merge pull request #79 from TorchAPI/reflection-manager
Testing Framework
2017-08-21 19:38:29 -04:00
Westin Miller
7149287b8e Let's pretend that typo didn't happen. 2017-08-21 15:44:15 -07:00
Westin Miller
cc709c6bb3 Torch.Client.Tests
Fixed jenkinsfile to create reports directory properly
Removed Any CPU configurations from Torch.sln
2017-08-21 15:38:17 -07:00
Westin Miller
8d101c4c11 Many more things use the new reflection system
Project for Torch.Server tests.
Refactoring some of the torch utility classes into a different namespace.
2017-08-20 23:46:19 -07:00
Westin Miller
64eef6cd8e Typo Number 3 2017-08-20 21:56:41 -07:00
Westin Miller
f8ae3c0dd1 Typo Number 2 2017-08-20 21:55:18 -07:00
Westin Miller
589205edc3 :( 2017-08-20 21:50:07 -07:00
Westin Miller
48b212faaf Reflection unit testing
Jenkins integration (we can only hope)
2017-08-20 21:48:42 -07:00
Westin Miller
0554dbc608 Complete documentation for the reflection manager. 2017-08-20 18:30:52 -07:00
Westin Miller
3564eb805c Reflection manager capable of handling field, property, and method resolution. 2017-08-20 17:56:42 -07:00
John Michael Gross
62a8064edd Update README.md 2017-08-20 17:11:49 -07:00
John Michael Gross
55ed45190b Update README.md 2017-08-20 17:09:58 -07:00
Westin Miller
a6ae96093f CI Archiving 2017-08-19 21:49:57 -07:00
Westin Miller
5b1afe6d50 Patch for uninitialized CI directories. 2017-08-19 21:46:29 -07:00
Westin Miller
60df71a74c Merge pull request #77 from TorchAPI/jenkins
Continuous Integration Support
2017-08-19 21:43:18 -07:00
Westin Miller
a6d5da861f Pipeline DSL 2017-08-19 21:36:42 -07:00
Westin Miller
afc10911f7 More descriptive stages 2017-08-19 21:17:58 -07:00
Westin Miller
0686e95c72 Delete existing link 2017-08-19 20:52:03 -07:00
Westin Miller
234754fd49 Initial continuous integration support 2017-08-19 20:31:08 -07:00
John Michael Gross
efb8d0f226 Merge pull request #76 from TorchAPI/dedi-issue-69
Stronger runtime checks in MultiplayerManager and Reflection
2017-08-19 20:13:27 -07:00
Westin Miller
3f881f7d67 Fixed tag parsing in UpdateManager 2017-08-19 18:22:26 -07:00
Westin Miller
64d38abc99 Typo 2017-08-19 18:20:50 -07:00
Westin Miller
4a39362702 Stronger runtime checks in MultiplayerManager and Reflection
MultiplayerManager now uses Keen's wrapper wrapper (wrapper?) of SteamSDK
Removed rogue file in the csproj
2017-08-19 04:56:41 -07:00
John Michael Gross
db2d3794ae Merge pull request #73 from TorchAPI/dedi-issue-69
Multiplayer Authentication Hook Removal
2017-08-19 01:28:38 -07:00
Westin Miller
526ff6fff0 Provide some semblance of compile time checking. 2017-08-18 22:31:03 -07:00
Westin Miller
eebc0e428e Fix that actually fixes the underlying problem with #69 2017-08-18 21:27:09 -07:00
John Michael Gross
80aca514b2 Merge pull request #72 from TorchAPI/dedi-issue-69
Remove player if already registered
2017-08-18 19:52:28 -07:00
Westin Miller
3e8068e82d Fixes a stupid problem with the DS multiplayer not checking if a
player is already registered.  (#69)
2017-08-18 19:34:41 -07:00
John Michael Gross
601fbcd176 Merge pull request #70 from Equinox-/master
Dependency Resolution and Sorting
2017-08-18 16:24:33 -07:00
Westin Miller
40eab15d69 More explanatory naming for Manager Init (Attach) and Dispose (Detach).
Torch.Client uses the registry to discover Steam's installation directory.
2017-08-18 16:19:59 -07:00
Westin Miller
ceb272c0b4 Moved extension classes to seperate files as Jimmacle requires. 2017-08-18 15:30:36 -07:00
Westin Miller
80d4f62694 Added load order restrictions for DependencyAttribute
Abstracted dependency manager away as an interface
2017-08-18 02:03:53 -07:00
Westin Miller
42f58a8649 Recursive dependency trees
- Now TorchBase and TorchSession can each have seperate managers
- Managers now have a Dispose function that's run on shutdown
2017-08-17 23:52:39 -07:00
Westin Miller
6b9af71967 Automatic dependency resolution
- All managers now use automatic dependency resolution
2017-08-17 18:14:23 -07:00
Westin Miller
dbd98a09c5 Early initialization for Torch Client.
- Assembly resolution
- SE installation directory locating
Dependency manager with automatic sorting and resolution
- Drop in replacement for the system currently in TorchBase
Shared binary directory for all Torch projects
2017-08-17 17:32:08 -07:00
John Michael Gross
c6a6363163 Merge pull request #67 from Mavy87/master
Fix to allow Torch to run as a service.
2017-08-17 09:29:04 -07:00
John Gross
82815f66e5 # Torch 1.1.229.265
* Features
    - Added more lenient version parsing for plugins (v#.# should work)
    - Added countdown option to restart command (!restart [seconds])
* Fixes
    - General fixes to work with the latest SE version
    - Fixed config changes not saving
    - Fixed crash on servers using the Windows Classic theme
2017-08-17 09:09:51 -07:00
Rene K
0a38eb770d Fix to allow Torch to run as a service. 2017-08-05 13:06:15 +02:00
John Gross
97da740e7e Catch errors in updater and fix loading error 2017-08-01 13:01:10 -07:00
John Gross
42bb24ca6a Hotfix for save issues 2017-08-01 12:31:49 -07:00
John Gross
2f3b6cdda7 Fix crashes and save issues 2017-07-31 13:12:01 -07:00
John Michael Gross
525b496774 Update README.md 2017-07-26 09:35:43 -07:00
John Michael Gross
562bb77dda Update README.md 2017-07-26 00:57:47 -07:00
John Gross
76a13dc53a # Torch 1.1.207.7
* Notes
    - This release makes significant changes to TorchConfig.xml. It has been renamed to Torch.cfg and has different options.
* Features
    - Plugins, Torch, and the DS can now all update automatically
    - Changed command prefix to !
    - Added manual save command (thanks to Maldark)
    - Added restart command
    - Improved instance creation: now creates an entire skeleton instance with blank config
    - Added instance name to console title
* Fixes
    - Optimized UI so it's snappier and freezes less often
    - Fixed NetworkManager.RaiseEvent overload that had an off-by-one bug
    - Fixed chat window so it automatically scrolls down
2017-07-26 00:40:46 -07:00
John Gross
df0f8072a9 Fix config attributes 2017-07-26 00:36:23 -07:00
John Gross
87d9825c91 Catch exceptions thrown by commands 2017-07-26 00:15:31 -07:00
John Michael Gross
3dba4f744f Create CONTRIBUTING.md 2017-07-24 15:44:14 -07:00
John Gross
1fcfe6fb5f Refactor instance management, assorted bugfixes/tweaks 2017-07-22 23:11:16 -07:00
John Michael Gross
3ece4baba6 Create index.md 2017-07-21 14:49:51 -07:00
John Michael Gross
f49dae2cbf Rename _config.yml to docs/_config.yml 2017-07-21 14:49:01 -07:00
John Michael Gross
ddf15d756a Set theme jekyll-theme-modernist 2017-07-21 14:48:02 -07:00
John Gross
96d1faddbe Update NLog, change init order, fix block delete in UI, change config to JSON 2017-07-18 17:31:08 -07:00
John Gross
17ee96038c Optimize UI more and fix some layout weirdness 2017-07-17 18:45:36 -07:00
John Gross
e9b432288e Optimize UI, add easily accessible restart code, fix bug in network manager RaiseEvent 2017-07-16 10:14:04 -07:00
John Michael Gross
b814d1210b Update README.md 2017-07-12 19:13:43 -07:00
John Michael Gross
c137fb4953 Merge pull request #42 from Maldark/master
Add async /save command for admins+ and server console.
2017-07-06 16:04:36 -07:00
Alexander Qvist-Hellum
4acce1c9c9 Merge branch 'master' into master 2017-07-07 00:39:00 +02:00
Alexander Qvist-Hellum
8ab16c3d30 Moved SaveGameStatus to seperate file, guarded against null callbacks and added documentation 2017-07-07 00:34:45 +02:00
John Gross
7373dd37a6 Refactor, fix chat scroll, rework automatic update system, remove manual install method, add documentation 2017-07-06 14:44:29 -07:00
Alexander Qvist-Hellum
1251b945bc Added async /save command for admins+ and server console.
Redesigned TorchBase.SaveGameAsync to take a callback function for error/success handling. Also removed local host checks as we are hosting a dedicated server.
2017-07-06 16:18:10 +02:00
John Gross
79fe6a08ab * Torch 1.0.182.329
- Improved logging, logs now to go the Logs folder and aren't deleted on start
    - Fixed chat tab not enabling with -autostart
    - Fixed player list
    - Watchdog time-out is now configurable in TorchConfig.xml
    - Fixed infinario log spam
    - Fixed crash when sending empty message from chat tab
    - Fixed permissions on Torch commands
    - Changed plugin StoragePath to the current instance path (per-instance configs)
2017-07-01 11:16:14 -07:00
John Gross
5e0f69e0e6 Update version 2017-06-29 15:48:25 -07:00
345 changed files with 26558 additions and 4974 deletions

6
.gitignore vendored
View File

@@ -1,6 +1,9 @@
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
#Rider directory
.idea/
# User-specific files # User-specific files
*.suo *.suo
*.user *.user
@@ -252,3 +255,6 @@ ModelManifest.xml
# FAKE - F# Make # FAKE - F# Make
.fake/ .fake/
GameBinaries GameBinaries
# Generated Files
**Gen.cs

33
CHANGELOG.md Normal file
View File

@@ -0,0 +1,33 @@
# Torch 1.1.229.265
* Features
- Added more lenient version parsing for plugins (v#.# should work)
- Added countdown option to restart command (!restart [seconds])
* Fixes
- General fixes to work with the latest SE version
- Fixed config changes not saving
- (hopefully) Fixed issue causing crashes on servers using the Windows Classic theme
# Torch 1.1.207.7
* Notes
- This release makes significant changes to TorchConfig.xml. It has been renamed to Torch.cfg and has different options.
* Features
- Plugins, Torch, and the DS can now all update automatically
- Changed command prefix to !
- Added manual save command (thanks to Maldark)
- Added restart command
- Improved instance creation: now creates an entire skeleton instance with blank config
- Added instance name to console title
* Fixes
- Optimized UI so it's snappier and freezes less often
- Fixed NetworkManager.RaiseEvent overload that had an off-by-one bug
- Fixed chat window so it automatically scrolls down
# Torch 1.0.182.329
* Improved logging, logs now to go the Logs folder and aren't deleted on start
* Fixed chat tab not enabling with -autostart
* Fixed player list
* Watchdog time-out is now configurable in TorchConfig.xml
* Fixed infinario log spam
* Fixed crash when sending empty message from chat tab
* Fixed permissions on Torch commands
* Changed plugin StoragePath to the current instance path (per-instance configs)

26
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,26 @@
# Making a Pull Request
* Fork this repository and make sure your local **staging** branch is up to date with the main repository.
* Create a new branch from the **staging** branch for your addition with an appropriate name, e.g. **add-restart-command**
* PRs work by submitting the *entire* branch, so this allows you to continue work without locking up your whole repository.
* Commit your changes to that branch, making sure that you **follow the code guidelines below**.
* Submit your branch as a PR to be reviewed, with Torch's **staging** branch as the base.
## Naming Conventions
* Types: **PascalCase**
* Prefix interfaces with "**I**"
* Suffix delegates with "**Del**"
* Methods: **PascalCase**
* Method names should generally use verbs in the infinitive tense, for example `GetValue()` or `OpenFile()`. Callbacks and events should use present continuous (-ing) or past tense depending on the context.
* Non-Private Members: **PascalCase**
* Private Members: **_camelCase**
## Code Design
* **One type per file** with the exception of nested types and delegate declarations.
* **No public fields** except for consts, use properties instead
* **No stateful static types.** These are a pain to clean up, static types should not store any information.
* Use **[dependency injection](https://stackoverflow.com/a/130862)** when possible. Most Torch code uses constructor injection.
* **Events and actions** should be null checked before calling or invoked with the `action?.Invoke()` syntax.
## Documentation
* All types and members not marked **private** or **internal** should have XML documentation using the `/// <summary>` tag.
* Interface implementations and overridden methods should use the `/// <inheritdoc />` tag unless the summary needs to be changed from the base/interface summary.

View File

@@ -1,15 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<variable name="logStamp" value="${time} ${pad:padding=-8:inner=[${level:uppercase=true}]}" />
<variable name="logContent" value="${message:withException=true}"/>
<targets> <targets>
<target name="logfile" layout="${longdate} [${level:uppercase=true}] ${logger}: ${message}" xsi:type="File" fileName="Torch.log" deleteOldFileOnStartup="true"/> <default-wrapper xsi:type="AsyncWrapper" overflowAction="Block" optimizeBufferReuse="true" />
<target name="console" layout="${longdate} [${level:uppercase=true}] ${logger}: ${message}" xsi:type="ColoredConsole" /> <target xsi:type="Null" name="null" formatMessage="false" />
<target xsi:type="File" keepFileOpen="true" concurrentWrites="false" name="keen" layout="${var:logStamp} ${logger}: ${var:logContent}"
fileName="Logs\Keen-${shortdate}.log" />
<target xsi:type="File" keepFileOpen="true" concurrentWrites="false" 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:shortName=true}: ${var:logContent}" />
<target xsi:type="File" name="patch" layout="${var:logContent}" fileName="Logs\patch.log"/>
<target xsi:type="LogViewerTarget" name="wpf" layout="[${level:uppercase=true}] ${logger:shortName=true}: ${var:logContent}" />
</targets> </targets>
<rules> <rules>
<logger name="*" minlevel="Info" writeTo="logfile" /> <!-- Do not define custom rules here. Use NLog-user.config -->
<logger name="*" 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, wpf" />
<logger name="Chat" minlevel="Info" writeTo="chat" />
<!--<logger name="Torch.Managers.PatchManager.*" minlevel="Trace" writeTo="patch"/>-->
</rules> </rules>
</nlog> </nlog>

View File

@@ -1,32 +1,29 @@
Discord: [![Discord](https://discordapp.com/api/guilds/230191591640268800/widget.png)](https://discord.gg/8uHZykr) [![Discord](https://discordapp.com/api/guilds/929141809769226271/widget.png)](https://discord.gg/trK6sYdcNE)
[![Build status](https://ci.appveyor.com/api/projects/status/us64kmwshl50f5a3/branch/master?svg=true)](https://ci.appveyor.com/project/zznty/torch/branch/master)
# What is Torch? # 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. 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.
# Features ## Torch.Server
### Features
* WPF-based user interface * WPF-based user interface
* Chat: interact with the game chat and run commands without having to join the game. * Chat: interact with the game chat and run commands without having to join the game.
* Entity manager: realtime modification of ingame entities such as stopping grids and changing block settings without having to join the game * Entity manager: realtime modification of ingame entities such as stopping grids and changing block settings without having to join the game
* Organized, easy to use configuration editor * Organized, easy to use configuration editor
* Extensible using the Torch plugin system * Extensible using the Torch plugin system
### Fork Difference
* .NET 6.0 runtime
* Additional options & features
### Installation
* 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.
# Building # 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. 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.
# Installation Guide If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon. (https://www.patreon.com/TorchSE)
Note: Until Torch is in a stable, nearly feature complete state there will not be any binaries available. You'll have to compile the solution yourself.
### Automatic (recommended)
* Unzip Torch to its own folder, run Torch.Server.exe and enter 'y' in the prompt for automatic updates. Torch will automatically download the Space Engineers files and generate all of the configs/folders necessary.
### Manual (for hosting companies or the paranoid)
* Install the Space Engineers DS and then unzip the Torch files into the server's DedicatedServer64 directory. It will automatically detect the manual install and disable automatic updates.
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](https://www.patreon.com/bePatron?u=847269)!

View File

@@ -3,11 +3,11 @@
@echo off @echo off
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: " set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
cd %~dp0 cd %~dp0
mklink /D GameBinaries %path% mklink /J GameBinaries "%path%"
if errorlevel 1 goto Error if errorlevel 1 goto Error
echo Done! You can now open the Torch solution without issue. echo Done! You can now open the Torch solution without issue.
goto End goto End
:Error :Error
echo An error occured creating the symlink. echo An error occured creating the symlink.
:End :End
pause pause

View File

@@ -0,0 +1,28 @@
using System;
namespace Torch.API.Event
{
/// <summary>
/// Attribute indicating that a method should be invoked when the event occurs.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class EventHandlerAttribute : Attribute
{
/// <summary>
/// Events are executed from low priority to high priority.
/// </summary>
/// <remarks>
/// While this may seem unintuitive this gives the high priority events the final say on changing/canceling events.
/// </remarks>
public int Priority { get; set; } = 0;
/// <summary>
/// Specifies if this handler should ignore a consumed event.
/// </summary>
/// <remarks>
/// If <see cref="SkipCancelled"/> is <em>true</em> and the event is cancelled by a lower priority handler this handler won't be invoked.
/// </remarks>
/// <seealso cref="IEvent.Cancelled"/>
public bool SkipCancelled { get; set; } = false;
}
}

11
Torch.API/Event/IEvent.cs Normal file
View File

@@ -0,0 +1,11 @@
namespace Torch.API.Event
{
public interface IEvent
{
/// <summary>
/// An event that has been cancelled will no be processed in the default manner.
/// </summary>
/// <seealso cref="EventHandlerAttribute.SkipCancelled"/>
bool Cancelled { get; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Torch.API.Event
{
/// <summary>
/// Interface used to tag an event handler. This does <b>not</b> register it with the event manager.
/// </summary>
public interface IEventHandler
{
}
}

View File

@@ -0,0 +1,28 @@
using System.Runtime.CompilerServices;
using Torch.API.Managers;
namespace Torch.API.Event
{
/// <summary>
/// Manager class responsible for registration of event handlers.
/// </summary>
public interface IEventManager : IManager
{
/// <summary>
/// Registers all event handler methods contained in the given instance
/// </summary>
/// <param name="handler">Instance to register</param>
/// <returns><b>true</b> if added, <b>false</b> otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
bool RegisterHandler(IEventHandler handler);
/// <summary>
/// Unregisters all event handler methods contained in the given instance
/// </summary>
/// <param name="handler">Instance to unregister</param>
/// <returns><b>true</b> if removed, <b>false</b> otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
bool UnregisterHandler(IEventHandler handler);
}
}

View File

@@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API
{
public interface IChatMessage
{
/// <summary>
/// The time the message was created.
/// </summary>
DateTime Timestamp { get; }
/// <summary>
/// The SteamID of the message author.
/// </summary>
ulong SteamId { get; }
/// <summary>
/// The name of the message author.
/// </summary>
string Name { get; }
/// <summary>
/// The content of the message.
/// </summary>
string Message { get; }
}
}

View File

@@ -1,41 +1,174 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Torch.API.Managers; using Torch.API.Managers;
using Torch.API.Session;
using VRage.Game.ModAPI; using VRage.Game.ModAPI;
using Version = SemanticVersioning.Version;
namespace Torch.API namespace Torch.API
{ {
/// <summary>
/// API for Torch functions shared between client and server.
/// </summary>
public interface ITorchBase public interface ITorchBase
{ {
/// <summary>
/// Fired when the session begins loading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionLoading; event Action SessionLoading;
/// <summary>
/// Fired when the session finishes loading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionLoaded; event Action SessionLoaded;
/// <summary>
/// Fires when the session begins unloading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionUnloading; event Action SessionUnloading;
/// <summary>
/// Fired when the session finishes unloading.
/// </summary>
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
event Action SessionUnloaded; event Action SessionUnloaded;
/// <summary>
/// Gets the currently running session instance, or null if none exists.
/// </summary>
ITorchSession CurrentSession { get; }
/// <summary>
/// Configuration for the current instance.
/// </summary>
ITorchConfig Config { get; } ITorchConfig Config { get; }
IMultiplayerManager Multiplayer { get; }
/// <inheritdoc cref="IPluginManager"/>
[Obsolete]
IPluginManager Plugins { get; } IPluginManager Plugins { get; }
Version TorchVersion { get; }
void Invoke(Action action); /// <inheritdoc cref="IDependencyManager"/>
void InvokeBlocking(Action action); IDependencyManager Managers { get; }
Task InvokeAsync(Action action);
string[] RunArgs { get; set; } [Obsolete("Prefer using Managers.GetManager for global managers")]
bool IsOnGameThread();
void Start();
void Stop();
void Init();
T GetManager<T>() where T : class, IManager; T GetManager<T>() where T : class, IManager;
[Obsolete("Prefer using Managers.AddManager for global managers")]
bool AddManager<T>(T manager) where T : class, IManager;
/// <summary>
/// The binary version of the current instance.
/// </summary>
Version TorchVersion { get; }
/// <summary>
/// Invoke an action on the game thread.
/// </summary>
void Invoke(Action action, [CallerMemberName] string caller = "");
/// <summary>
/// Invoke an action on the game thread and block until it has completed.
/// </summary>
/// <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>
void Start();
/// <summary>
/// Signals the torch instance to stop, then blocks until it's stopped.
/// </summary>
void Stop();
/// <summary>
/// Restart the Torch instance, blocking until the restart has been performed.
/// </summary>
void Restart(bool save = true);
/// <summary>
/// Initializes a save of the game.
/// </summary>
/// <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.
/// </summary>
void Init();
/// <summary>
/// Disposes the Torch instance. After this <see cref="Start"/> is invalid.
/// </summary>
void Destroy();
/// <summary>
/// The current state of the game this instance of torch is controlling.
/// </summary>
TorchGameState GameState { get; }
/// <summary>
/// Event raised when <see cref="GameState"/> changes.
/// </summary>
event TorchGameStateChangedDel GameStateChanged;
} }
/// <summary>
/// API for the Torch server.
/// </summary>
public interface ITorchServer : ITorchBase public interface ITorchServer : ITorchBase
{ {
/// <summary>
/// The current <see cref="ServerState"/>
/// </summary>
ServerState State { get; }
/// <summary>
/// Path of the dedicated instance folder.
/// </summary>
string InstancePath { get; } string InstancePath { get; }
/// <summary>
/// Name of the dedicated instance.
/// </summary>
string InstanceName { get; }
/// <summary>
/// Raised when the server's Init() method has completed.
/// </summary>
event Action<ITorchServer> Initialized;
TimeSpan ElapsedPlayTime { get; set; }
} }
/// <summary>
/// API for the Torch client.
/// </summary>
public interface ITorchClient : ITorchBase public interface ITorchClient : ITorchBase
{ {
} }
} }

View File

@@ -1,13 +1,37 @@
namespace Torch using System;
using System.Collections.Generic;
using Torch.API;
namespace Torch
{ {
public interface ITorchConfig public interface ITorchConfig
{ {
bool Autostart { get; set; }
bool ForceUpdate { get; set; }
bool GetPluginUpdates { get; set; }
bool GetTorchUpdates { get; set; }
[Obsolete("Use Torch.InstanceName instead")]
string InstanceName { get; set; } string InstanceName { get; set; }
[Obsolete("Use Torch.InstancePath instead")]
string InstancePath { get; set; } string InstancePath { get; set; }
bool RedownloadPlugins { get; set; } bool NoGui { get; set; }
bool AutomaticUpdates { get; set; } bool NoUpdate { get; set; }
List<Guid> Plugins { get; set; }
bool LocalPlugins { get; set; }
bool RestartOnCrash { get; set; } bool RestartOnCrash { get; set; }
bool ShouldUpdatePlugins { get; }
bool Save(string path = null); bool ShouldUpdateTorch { get; }
int TickTimeout { get; set; }
string WaitForPID { get; set; }
string ChatName { get; set; }
string ChatColor { get; set; }
string TestPlugin { get; set; }
bool DisconnectOnRestart { get; set; }
int WindowWidth { get; set; }
int WindowHeight { get; set; }
int FontSize { get; set; }
UGCServiceType UgcServiceType { get; set; }
bool EntityManagerEnabled { get; set; }
void Save(string path = null);
} }
} }

View File

@@ -0,0 +1,30 @@
using System;
namespace Torch.API.Managers
{
public static class DependencyManagerExtensions
{
/// <summary>
/// Removes a single manager from this dependency manager.
/// </summary>
/// <param name="managerType">The dependency type to remove</param>
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
public static IManager RemoveManager(this IDependencyManager depManager, Type managerType)
{
IManager mgr = depManager.GetManager(managerType);
return depManager.RemoveManager(mgr) ? mgr : null;
}
/// <summary>
/// Removes a single manager from this dependency manager.
/// </summary>
/// <typeparam name="T">The dependency type to remove</typeparam>
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
public static IManager RemoveManager<T>(this IDependencyManager depManager)
{
return depManager.RemoveManager(typeof(T));
}
}
}

View File

@@ -0,0 +1,15 @@
namespace Torch.API.Managers
{
public static class DependencyProviderExtensions
{
/// <summary>
/// Gets the manager that provides the given type. If there is no such manager, returns null.
/// </summary>
/// <typeparam name="T">Type of manager</typeparam>
/// <returns>manager, or null if none exists</returns>
public static T GetManager<T>(this IDependencyProvider depProvider) where T : class, IManager
{
return (T)depProvider.GetManager(typeof(T));
}
}
}

View File

@@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.Gui;
using Sandbox.Game.Multiplayer;
using Torch.Utils;
using VRage.Game;
using VRage.Network;
using VRage.Replication;
using VRageMath;
using VRageRender;
namespace Torch.API.Managers
{
/// <summary>
/// Represents a scripted or user chat message.
/// </summary>
public readonly struct TorchChatMessage
{
private const string DEFAULT_FONT = MyFontEnum.Blue;
#region Backwards compatibility
[Obsolete]
public TorchChatMessage(string author, string message, string font = DEFAULT_FONT)
: this(author, message, default, font) { }
[Obsolete]
public TorchChatMessage(string author, ulong authorSteamId, string message, ChatChannel channel, long target, string font = DEFAULT_FONT)
: this(author, authorSteamId, message, channel, target, default, font) { }
[Obsolete]
public TorchChatMessage(ulong authorSteamId, string message, ChatChannel channel, long target, string font = DEFAULT_FONT)
: this(authorSteamId, message, channel, target, default, font) { }
#endregion
/// <summary>
/// Creates a new torch chat message with the given author and message.
/// </summary>
/// <param name="author">Author's name</param>
/// <param name="message">Message</param>
/// <param name="font">Font</param>
public TorchChatMessage(string author, string message, Color color, string font = DEFAULT_FONT)
{
Timestamp = DateTime.Now;
AuthorSteamId = null;
Author = author;
Message = message;
Channel = ChatChannel.Global;
Target = 0;
Font = font;
Color = color == default ? ColorUtils.TranslateColor(font) : color;
}
/// <summary>
/// Creates a new torch chat message with the given author and message.
/// </summary>
/// <param name="author">Author's name</param>
/// <param name="authorSteamId">Author's steam ID</param>
/// <param name="message">Message</param>
/// <param name="font">Font</param>
public TorchChatMessage(string author, ulong authorSteamId, string message, ChatChannel channel, long target, Color color, string font = DEFAULT_FONT)
{
Timestamp = DateTime.Now;
AuthorSteamId = authorSteamId;
Author = author;
Message = message;
Channel = channel;
Target = target;
Font = font;
Color = color == default ? ColorUtils.TranslateColor(font) : color;
}
/// <summary>
/// Creates a new torch chat message with the given author and message.
/// </summary>
/// <param name="authorSteamId">Author's steam ID</param>
/// <param name="message">Message</param>
/// <param name="font">Font</param>
public TorchChatMessage(ulong authorSteamId, string message, ChatChannel channel, long target, Color color, string font = DEFAULT_FONT)
{
Timestamp = DateTime.Now;
AuthorSteamId = authorSteamId;
Author = MyMultiplayer.Static?.GetMemberName(authorSteamId) ?? "Player";
Message = message;
Channel = channel;
Target = target;
Font = font;
Color = color == default ? ColorUtils.TranslateColor(font) : color;
}
/// <summary>
/// This message's timestamp.
/// </summary>
public readonly DateTime Timestamp;
/// <summary>
/// The author's steam ID, if available. Else, null.
/// </summary>
public readonly ulong? AuthorSteamId;
/// <summary>
/// The author's name, if available. Else, null.
/// </summary>
public readonly string Author;
/// <summary>
/// The message contents.
/// </summary>
public readonly string Message;
/// <summary>
/// The chat channel the message is part of.
/// </summary>
public readonly ChatChannel Channel;
/// <summary>
/// The intended recipient of the message.
/// </summary>
public readonly long Target;
/// <summary>
/// The font, or null if default.
/// </summary>
public readonly string Font;
/// <summary>
/// The chat message color.
/// </summary>
public readonly Color Color;
}
/// <summary>
/// Callback used to indicate that a messaage has been recieved.
/// </summary>
/// <param name="msg"></param>
/// <param name="consumed">If true, this event has been consumed and should be ignored</param>
public delegate void MessageRecievedDel(TorchChatMessage msg, ref bool consumed);
/// <summary>
/// Callback used to indicate the user is attempting to send a message locally.
/// </summary>
/// <param name="msg">Message the user is attempting to send</param>
/// <param name="consumed">If true, this event has been consumed and should be ignored</param>
public delegate void MessageSendingDel(string msg, ref bool consumed);
public interface IChatManagerClient : IManager
{
/// <summary>
/// Event that is raised when a message addressed to us is recieved. <see cref="MessageRecievedDel"/>
/// </summary>
event MessageRecievedDel MessageRecieved;
/// <summary>
/// Event that is raised when we are attempting to send a message. <see cref="MessageSendingDel"/>
/// </summary>
event MessageSendingDel MessageSending;
/// <summary>
/// Triggers the <see cref="MessageSending"/> event,
/// typically raised by the user entering text into the chat window.
/// </summary>
/// <param name="message">The message to send</param>
void SendMessageAsSelf(string message);
/// <summary>
/// Displays a message on the UI given an author name and a message.
/// </summary>
/// <param name="author">Author name</param>
/// <param name="message">Message content</param>
/// <param name="font">font to use</param>
void DisplayMessageOnSelf(string author, string message, string font = "Blue" );
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VRage.Collections;
using VRage.Game;
using VRage.Network;
using VRageMath;
namespace Torch.API.Managers
{
/// <summary>
/// Callback used to indicate the server has recieved a message to process and forward on to others.
/// </summary>
/// <param name="authorId">Steam ID of the user sending a message</param>
/// <param name="msg">Message the user is attempting to send</param>
/// <param name="consumed">If true, this event has been consumed and should be ignored</param>
public delegate void MessageProcessingDel(TorchChatMessage msg, ref bool consumed);
public interface IChatManagerServer : IChatManagerClient
{
/// <summary>
/// Event triggered when the server has recieved a message and should process it. <see cref="MessageProcessingDel"/>
/// </summary>
event MessageProcessingDel MessageProcessing;
/// <summary>
/// Sends a message with the given author and message to the given player, or all players by default.
/// </summary>
/// <param name="authorId">Author's steam ID</param>
/// <param name="message">The message to send</param>
/// <param name="targetSteamId">Player to send the message to, or everyone by default</param>
void SendMessageAsOther(ulong authorId, string message, ulong targetSteamId = 0);
[Obsolete("Use the other overload with a Color parameter.")]
void SendMessageAsOther(string author, string message, string font, ulong targetSteamId = 0);
/// <summary>
/// Sends a scripted message with the given author and message to the given player, or all players by default.
/// </summary>
/// <param name="author">Author name</param>
/// <param name="message">The message to send</param>
/// <param name="color">Name color</param>
/// <param name="font">Font to use</param>
/// <param name="targetSteamId">Player to send the message to, or everyone by default</param>
void SendMessageAsOther(string author, string message, Color color = default, ulong targetSteamId = 0, string font = MyFontEnum.White);
/// <summary>
/// Mute user from global chat.
/// </summary>
/// <param name="steamId"></param>
/// <returns></returns>
bool MuteUser(ulong steamId);
/// <summary>
/// Unmute user from global chat.
/// </summary>
/// <param name="steamId"></param>
/// <returns></returns>
bool UnmuteUser(ulong steamId);
/// <summary>
/// Users which are not allowed to chat.
/// </summary>
HashSetReader<ulong> MutedUsers { get; }
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Managers
{
/// <summary>
/// Manages a set of <see cref="IManager"/> and the dependencies between them.
/// </summary>
public interface IDependencyManager : IDependencyProvider
{
/// <summary>
/// Registers the given manager into the dependency system.
/// </summary>
/// <remarks>
/// This method only returns false when there is already a manager registered with a type derived from this given manager,
/// or when the given manager is derived from an already existing manager.
/// </remarks>
/// <param name="manager">Manager to register</param>
/// <exception cref="InvalidOperationException">When adding a new manager to an initialized dependency manager</exception>
/// <returns>true if added, false if not</returns>
bool AddManager(IManager manager);
/// <summary>
/// Clears all managers registered with this dependency manager
/// </summary>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
void ClearManagers();
/// <summary>
/// Removes a single manager from this dependency manager.
/// </summary>
/// <param name="manager">The manager to remove</param>
/// <returns>true if successful, false if the manager wasn't found</returns>
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
bool RemoveManager(IManager manager);
/// <summary>
/// Sorts the dependency manager, then attaches all its registered managers in <see cref="AttachOrder" />
/// </summary>
void Attach();
/// <summary>
/// Detaches all registered managers in <see cref="DetachOrder"/>
/// </summary>
void Detach();
/// <summary>
/// The order that managers should be attached in. (Dependencies, then dependents)
/// </summary>
/// <exception cref="InvalidOperationException">When trying to determine load order before this dependency manager is initialized</exception>
IEnumerable<IManager> AttachOrder { get; }
/// <summary>
/// The order that managers should be detached in. (Dependents, then dependencies)
/// </summary>
/// <exception cref="InvalidOperationException">When trying to determine unload order before this dependency manager is initialized</exception>
IEnumerable<IManager> DetachOrder { get; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Managers
{
public interface IDependencyProvider
{
/// <summary>
/// Gets the manager that provides the given type. If there is no such manager, returns null.
/// </summary>
/// <param name="type">Type of manager</param>
/// <returns>manager, or null if none exists</returns>
IManager GetManager(Type type);
}
}

View File

@@ -0,0 +1,21 @@
using VRage.Game;
namespace Torch.API.Managers;
public interface IInstanceManager : IManager
{
IWorld SelectedWorld { get; }
void LoadInstance(string path, bool validate = true);
void SelectCreatedWorld(string worldPath);
void SelectWorld(string worldPath, bool modsOnly = true);
void ImportSelectedWorldConfig();
void SaveConfig();
}
public interface IWorld
{
string FolderName { get; }
string WorldPath { get; }
MyObjectBuilder_SessionSettings KeenSessionSettings { get; }
MyObjectBuilder_Checkpoint KeenCheckpoint { get; }
}

View File

@@ -6,8 +6,19 @@ using System.Threading.Tasks;
namespace Torch.API.Managers namespace Torch.API.Managers
{ {
/// <summary>
/// Base interface for Torch managers.
/// </summary>
public interface IManager public interface IManager
{ {
void Init(); /// <summary>
/// Attaches the manager to the session. Called once this manager's dependencies have been attached.
/// </summary>
void Attach();
/// <summary>
/// Detaches the manager from the session. Called before this manager's dependencies are detached.
/// </summary>
void Detach();
} }
} }

View File

@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using VRage.Game;
using VRage.Game.ModAPI;
namespace Torch.API.Managers
{
public delegate void MessageReceivedDel(IChatMessage message, ref bool sendToOthers);
public interface IMultiplayerManager : IManager
{
event Action<IPlayer> PlayerJoined;
event Action<IPlayer> PlayerLeft;
event MessageReceivedDel MessageReceived;
void SendMessage(string message, string author = "Server", long playerId = 0, string font = MyFontEnum.Blue);
void KickPlayer(ulong steamId);
void BanPlayer(ulong steamId, bool banned = true);
IMyPlayer GetPlayerBySteamId(ulong id);
IMyPlayer GetPlayerByName(string name);
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using VRage.Game;
using VRage.Game.ModAPI;
namespace Torch.API.Managers
{
/// <summary>
/// API for multiplayer related functions common to servers and clients.
/// </summary>
public interface IMultiplayerManagerBase : IManager
{
/// <summary>
/// Fired when a player joins.
/// </summary>
event Action<IPlayer> PlayerJoined;
/// <summary>
/// Fired when a player disconnects.
/// </summary>
event Action<IPlayer> PlayerLeft;
/// <summary>
/// Gets a player by their Steam64 ID or returns null if the player isn't found.
/// </summary>
IMyPlayer GetPlayerBySteamId(ulong id);
/// <summary>
/// Gets a player by their display name or returns null if the player isn't found.
/// </summary>
IMyPlayer GetPlayerByName(string name);
/// <summary>
/// Gets the steam username of a member's steam ID
/// </summary>
/// <param name="steamId">steam ID</param>
/// <returns>steam username</returns>
string GetSteamUsername(ulong steamId);
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Managers
{
public interface IMultiplayerManagerClient : IMultiplayerManagerBase
{
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VRage.Game.ModAPI;
namespace Torch.API.Managers
{
/// <summary>
/// API for multiplayer functions that exist on servers and lobbies
/// </summary>
public interface IMultiplayerManagerServer : IMultiplayerManagerBase
{
/// <summary>
/// Kicks the player from the game.
/// </summary>
void KickPlayer(ulong steamId);
/// <summary>
/// Bans or unbans a player from the game.
/// </summary>
void BanPlayer(ulong steamId, bool banned = true);
/// <summary>
/// Promotes user if possible.
/// </summary>
/// <param name="steamId"></param>
void PromoteUser(ulong steamId);
/// <summary>
/// Demotes user if possible.
/// </summary>
/// <param name="steamId"></param>
void DemoteUser(ulong steamId);
/// <summary>
/// Gets a user's promote level.
/// </summary>
/// <param name="steamId"></param>
/// <returns></returns>
MyPromoteLevel GetUserPromoteLevel(ulong steamId);
/// <summary>
/// List of the banned SteamID's
/// </summary>
IReadOnlyList<ulong> BannedPlayers { get; }
/// <summary>
/// Checks if the player with the given SteamID is banned.
/// </summary>
/// <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;
/// <summary>
/// Raised when a player is promoted or demoted. Passes SteamID of player, and new promote level.
/// </summary>
event Action<ulong, MyPromoteLevel> PlayerPromoted;
}
}

View File

@@ -9,14 +9,37 @@ using VRage.Network;
namespace Torch.API.Managers namespace Torch.API.Managers
{ {
/// <summary>
/// API for the network intercept.
/// </summary>
public interface INetworkManager : IManager public interface INetworkManager : IManager
{ {
/// <summary>
/// Register a network handler.
/// </summary>
void RegisterNetworkHandler(INetworkHandler handler); void RegisterNetworkHandler(INetworkHandler handler);
/// <summary>
/// Unregister a network handler.
/// </summary>
/// <returns>true if the handler was unregistered, false if it wasn't registered to begin with</returns>
bool UnregisterNetworkHandler(INetworkHandler handler);
} }
/// <summary>
/// Handler for multiplayer network messages.
/// </summary>
public interface INetworkHandler public interface INetworkHandler
{ {
/// <summary>
/// Returns if the handler can process the call site.
/// </summary>
bool CanHandle(CallSite callSite); bool CanHandle(CallSite callSite);
/// <summary>
/// Processes a network message.
/// </summary>
/// <returns>true if the message should be discarded</returns>
bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet); bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet);
} }
} }

View File

@@ -6,11 +6,29 @@ using VRage.Plugins;
namespace Torch.API.Managers namespace Torch.API.Managers
{ {
/// <summary>
/// API for the Torch plugin manager.
/// </summary>
public interface IPluginManager : IManager, IEnumerable<ITorchPlugin> public interface IPluginManager : IManager, IEnumerable<ITorchPlugin>
{ {
event Action<List<ITorchPlugin>> PluginsLoaded; /// <summary>
List<ITorchPlugin> Plugins { get; } /// Fired when plugins are loaded.
/// </summary>
event Action<IReadOnlyCollection<ITorchPlugin>> PluginsLoaded;
/// <summary>
/// Collection of loaded plugins.
/// </summary>
IReadOnlyDictionary<Guid, ITorchPlugin> Plugins { get; }
/// <summary>
/// Updates all loaded plugins.
/// </summary>
void UpdatePlugins(); void UpdatePlugins();
void DisposePlugins();
/// <summary>
/// Load plugins.
/// </summary>
void LoadPlugins();
} }
} }

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@@ -17,7 +17,7 @@ namespace Torch.API.Plugins
/// <summary> /// <summary>
/// The version of the plugin. /// The version of the plugin.
/// </summary> /// </summary>
Version Version { get; } string Version { get; }
/// <summary> /// <summary>
/// The name of the plugin. /// The name of the plugin.
@@ -34,5 +34,22 @@ namespace Torch.API.Plugins
/// This is called on the game thread after each tick. /// This is called on the game thread after each tick.
/// </summary> /// </summary>
void Update(); void Update();
/// <summary>
/// Plugin's enabled state. Mainly for UI niceness
/// </summary>
PluginState State { get; }
}
public enum PluginState
{
NotInitialized,
DisabledError,
DisabledUser,
UpdateRequired,
UninstallRequested,
NotInstalled,
MissingDependency,
Enabled
} }
} }

View File

@@ -1,22 +1,56 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Torch.API.Plugins namespace Torch.API.Plugins
{ {
/// <summary>
/// Indicates that the given type should be loaded by the plugin manager as a plugin.
/// </summary>
[Obsolete("All plugin meta-information is now defined in the manifest.xml.")]
[AttributeUsage(AttributeTargets.Class)]
public class PluginAttribute : Attribute public class PluginAttribute : Attribute
{ {
/// <summary>
/// The display name of the plugin
/// </summary>
public string Name { get; } public string Name { get; }
/// <summary>
/// The version of the plugin
/// </summary>
public Version Version { get; } public Version Version { get; }
/// <summary>
/// The GUID of the plugin
/// </summary>
public Guid Guid { get; } public Guid Guid { get; }
/// <summary>
/// Creates a new plugin attribute with the given attributes
/// </summary>
/// <param name="name"></param>
/// <param name="version"></param>
/// <param name="guid"></param>
public PluginAttribute(string name, string version, string guid) public PluginAttribute(string name, string version, string guid)
{ {
Name = name; Name = name;
Version = Version.Parse(version); Version = Version.Parse(version);
Guid = Guid.Parse(guid); Guid = Guid.Parse(guid);
} }
/// <summary>
/// Creates a new plugin attribute with the given attributes. Version is computed as the version of the assembly containing the given type.
/// </summary>
/// <param name="name"></param>
/// <param name="versionSupplier">Version is this type's assembly's version</param>
/// <param name="guid"></param>
public PluginAttribute(string name, Type versionSupplier, string guid)
{
Name = name;
Version = versionSupplier.Assembly.GetName().Version;
Guid = Guid.Parse(guid);
}
} }
} }

View File

@@ -1,36 +1,17 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following [assembly: AssemblyTitle("Torch API")]
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TorchAPI")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TorchAPI")] [assembly: AssemblyProduct("Torch")]
[assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyCopyright("Copyright © Torch API 2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM #if DEBUG
[assembly: Guid("fba5d932-6254-4a1e-baf4-e229fa94e3c2")] [assembly: AssemblyConfiguration("Debug")]
#else
// Version information for an assembly consists of the following four values: [assembly: AssemblyConfiguration("Release")]
// #endif
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -6,11 +6,29 @@ using System.Threading.Tasks;
namespace Torch.API namespace Torch.API
{ {
/// <summary>
/// Used to indicate the state of the dedicated server.
/// </summary>
public enum ServerState public enum ServerState
{ {
/// <summary>
/// The server is not running.
/// </summary>
Stopped, Stopped,
/// <summary>
/// The server is starting/loading the session.
/// </summary>
Starting, Starting,
/// <summary>
/// The server is running.
/// </summary>
Running, Running,
/// <summary>
/// The server encountered an error.
/// </summary>
Error Error
} }
} }

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

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Game.World;
using Torch.API.Managers;
namespace Torch.API.Session
{
/// <summary>
/// Represents the Torch code working with a single game session
/// </summary>
public interface ITorchSession
{
/// <summary>
/// The Torch instance this session is bound to
/// </summary>
ITorchBase Torch { get; }
/// <summary>
/// The Space Engineers game session this session is bound to.
/// </summary>
MySession KeenSession { get; }
/// <summary>
/// Currently running world
/// </summary>
IWorld World { get; }
/// <inheritdoc cref="IDependencyManager"/>
IDependencyManager Managers { get; }
/// <summary>
/// The current state of the session
/// </summary>
TorchSessionState State { get; }
/// <summary>
/// Event raised when the <see cref="State"/> changes.
/// </summary>
event TorchSessionStateChangedDel StateChanged;
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Torch.API.Managers;
using VRage.Game;
namespace Torch.API.Session
{
/// <summary>
/// Creates a manager for the given session if applicable.
/// </summary>
/// <remarks>
/// This is for creating managers that will live inside the session, not the manager that controls sesssions.
/// </remarks>
/// <param name="session">The session to construct a bound manager for</param>
/// <returns>The manager that will live in the session, or null if none.</returns>
public delegate IManager SessionManagerFactoryDel(ITorchSession session);
/// <summary>
/// Manages the creation and destruction of <see cref="ITorchSession"/> instances for each <see cref="Sandbox.Game.World.MySession"/> created by Space Engineers.
/// </summary>
public interface ITorchSessionManager : IManager
{
/// <summary>
/// The currently running session
/// </summary>
ITorchSession CurrentSession { get; }
/// <summary>
/// Raised when any <see cref="ITorchSession"/> <see cref="ITorchSession.State"/> changes.
/// </summary>
event TorchSessionStateChangedDel SessionStateChanged;
/// <summary>
/// Adds the given factory as a supplier for session based managers
/// </summary>
/// <param name="factory">Session based manager supplier</param>
/// <returns>true if added, false if already present</returns>
/// <exception cref="ArgumentNullException">If the factory is null</exception>
bool AddFactory(SessionManagerFactoryDel factory);
/// <summary>
/// Remove the given factory from the suppliers for session based managers
/// </summary>
/// <param name="factory">Session based manager supplier</param>
/// <returns>true if removed, false if not present</returns>
/// <exception cref="ArgumentNullException">If the factory is null</exception>
bool RemoveFactory(SessionManagerFactoryDel factory);
/// <summary>
/// Add a mod to be injected into client's world download.
/// </summary>
/// <param name="modId"></param>
/// <returns></returns>
bool AddOverrideMod(ulong modId);
/// <summary>
/// Removes a mod from the injected mod list.
/// </summary>
/// <param name="modId"></param>
/// <returns></returns>
bool RemoveOverrideMod(ulong modId);
/// <summary>
/// List over mods that will be injected into client world downloads.
/// </summary>
IReadOnlyCollection<MyObjectBuilder_Checkpoint.ModItem> OverrideMods { get; }
/// <summary>
/// Event raised when injected mod list changes.
/// </summary>
event Action<CollectionChangeEventArgs> OverrideModsChanged;
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.API.Session
{
/// <summary>
/// Represents the state of a <see cref="ITorchSession"/>
/// </summary>
public enum TorchSessionState
{
/// <summary>
/// The session has been created, and is now loading.
/// </summary>
Loading,
/// <summary>
/// The session has loaded, and is now running.
/// </summary>
Loaded,
/// <summary>
/// The session was running, and is now unloading.
/// </summary>
Unloading,
/// <summary>
/// The session was unloading, and is now unloaded and stopped.
/// </summary>
Unloaded
}
/// <summary>
/// Callback raised when a session's state changes
/// </summary>
/// <param name="session">The session who had a state change</param>
/// <param name="newState">The session's new state</param>
public delegate void TorchSessionStateChangedDel(ITorchSession session, TorchSessionState newState);
}

View File

@@ -1,188 +1,105 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6-windows</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <AssemblyTitle>Torch API</AssemblyTitle>
<ProjectGuid>{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}</ProjectGuid> <Product>Torch</Product>
<OutputType>Library</OutputType> <Copyright>Copyright © Torch API 2017</Copyright>
<AppDesignerFolder>Properties</AppDesignerFolder> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<RootNamespace>Torch.API</RootNamespace> <OutputPath>..\bin\$(Platform)\$(Configuration)\</OutputPath>
<AssemblyName>Torch.API</AssemblyName> <UseWpf>True</UseWpf>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <Configurations>Debug;Release</Configurations>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <Platforms>AnyCPU</Platforms>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="$(Configuration) == 'Release'">
<OutputPath>bin\x64\Release\</OutputPath> <NoWarn>1591</NoWarn>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\x64\Release\Torch.API.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> -->
<ItemGroup>
<PackageReference Include="NLog" Version="5.0.0-rc2" />
<PackageReference Include="SemanticVersioning" Version="2.0.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath> <HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="Sandbox.Game, Version=0.1.6305.30774, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="Sandbox.Game, Version=0.1.6305.30774, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="Sandbox.Graphics, Version=0.1.6305.30761, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="Sandbox.Graphics, Version=0.1.6305.30761, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="SpaceEngineers.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="SpaceEngineers.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath> <HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="SpaceEngineers.ObjectBuilders, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="SpaceEngineers.ObjectBuilders, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.dll</HintPath> <HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference>
<Reference Include="SpaceEngineers.ObjectBuilders.XmlSerializers, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference> </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" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="VRage"> <Reference Include="VRage">
<HintPath>..\GameBinaries\VRage.dll</HintPath> <HintPath>..\GameBinaries\VRage.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Audio.dll</HintPath> <HintPath>..\GameBinaries\VRage.Audio.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="VRage.Dedicated"> <Reference Include="VRage.Dedicated">
<HintPath>..\GameBinaries\VRage.Dedicated.dll</HintPath> <HintPath>..\GameBinaries\VRage.Dedicated.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath> <HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference>
<Reference Include="VRage.Game.XmlSerializers, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Game.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath> <HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="VRage.Library"> <Reference Include="VRage.Library">
<HintPath>..\GameBinaries\VRage.Library.dll</HintPath> <HintPath>..\GameBinaries\VRage.Library.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Math.dll</HintPath> <HintPath>..\GameBinaries\VRage.Math.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference>
<Reference Include="VRage.Native, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Native.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.OpenVRWrapper, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.OpenVRWrapper.dll</HintPath>
<Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Render, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="VRage.Render, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Render.dll</HintPath> <HintPath>..\GameBinaries\VRage.Render.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="VRage.Render11, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="VRage.Render11, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath> <HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="VRage.Scripting, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="VRage.Scripting, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath> <HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ConnectionState.cs" /> <Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties/AssemblyVersion.cs" />
<Compile Include="IChatMessage.cs" />
<Compile Include="Managers\IManager.cs" />
<Compile Include="Managers\IMultiplayerManager.cs" />
<Compile Include="IPlayer.cs" />
<Compile Include="Managers\INetworkManager.cs" />
<Compile Include="Managers\IPluginManager.cs" />
<Compile Include="ITorchConfig.cs" />
<Compile Include="Plugins\ITorchPlugin.cs" />
<Compile Include="IServerControls.cs" />
<Compile Include="ITorchBase.cs" />
<Compile Include="Plugins\IWpfPlugin.cs" />
<Compile Include="ModAPI\Ingame\GridExtensions.cs" />
<Compile Include="Plugins\PluginAttribute.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServerState.cs" />
<Compile Include="ModAPI\TorchAPI.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project> </Project>

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox;
namespace Torch.API
{
/// <summary>
/// Represents the state of a <see cref="MySandboxGame"/>
/// </summary>
public enum TorchGameState
{
/// <summary>
/// The game is currently being created.
/// </summary>
Creating,
/// <summary>
/// The game has been created and is ready to begin loading.
/// </summary>
Created,
/// <summary>
/// The game is currently loading.
/// </summary>
Loading,
/// <summary>
/// The game is fully loaded and ready to start sessions
/// </summary>
Loaded,
/// <summary>
/// The game is beginning the unload sequence
/// </summary>
Unloading,
/// <summary>
/// The game has been shutdown and is no longer active
/// </summary>
Unloaded
}
/// <summary>
/// Callback raised when a game's state changes
/// </summary>
/// <param name="game">The game who had a state change</param>
/// <param name="newState">The game's new state</param>
public delegate void TorchGameStateChangedDel(MySandboxGame game, TorchGameState newState);
}

View File

@@ -0,0 +1,8 @@
namespace Torch.API
{
public enum UGCServiceType
{
Steam,
EOS
}
}

View File

@@ -0,0 +1,40 @@
using System.Windows.Media;
using VRage.Game;
using Color = VRageMath.Color;
namespace Torch.Utils
{
public static class ColorUtils
{
/// <summary>
/// Convert the old "font" or a RGB hex code to a Color.
/// </summary>
public static Color TranslateColor(string font)
{
if (StringUtils.IsFontEnum(font))
{
// RGB values copied from Fonts.sbc
switch (font)
{
case MyFontEnum.Blue:
return new Color(220, 244, 252);
case MyFontEnum.Red:
return new Color(227, 65, 65);
case MyFontEnum.Green:
return new Color(101, 182, 193);
case MyFontEnum.DarkBlue:
return new Color(94, 115, 127);
default:
return Color.White;
}
}
else
{
// VRage color doesn't have its own hex code parser and I don't want to write one
var conv = (System.Windows.Media.Color)(ColorConverter.ConvertFromString(font) ??
System.Windows.Media.Color.FromRgb(255, 255, 255));
return new Color(conv.R, conv.G, conv.B);
}
}
}
}

View File

@@ -0,0 +1,21 @@
#nullable enable
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Version = SemanticVersioning.Version;
namespace Torch.API.Utils;
public class SemanticVersionConverter : JsonConverter<Version>
{
public override Version? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Version.TryParse(reader.GetString(), out var ver);
return ver;
}
public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Utils
{
/// <summary>
/// Utility methods for strings
/// </summary>
public static class StringUtils
{
/// <summary>
/// Determines a common prefix for the given set of strings
/// </summary>
/// <param name="set">Set of strings</param>
/// <returns>Common prefix</returns>
public static string CommonPrefix(IEnumerable<string> set)
{
StringBuilder builder = null;
foreach (string other in set)
{
if (builder == null)
builder = new StringBuilder(other);
if (builder.Length > other.Length)
builder.Remove(other.Length, builder.Length - other.Length);
for (var i = 0; i < builder.Length; i++)
if (builder[i] != other[i])
{
builder.Remove(i, builder.Length - i);
break;
}
}
return builder?.ToString() ?? "";
}
/// <summary>
/// Determines a common suffix for the given set of strings
/// </summary>
/// <param name="set">Set of strings</param>
/// <returns>Common suffix</returns>
public static string CommonSuffix(IEnumerable<string> set)
{
StringBuilder builder = null;
foreach (string other in set)
{
if (builder == null)
builder = new StringBuilder(other);
if (builder.Length > other.Length)
builder.Remove(0, builder.Length - other.Length);
for (var i = 0; i < builder.Length; i++)
{
if (builder[builder.Length - 1 - i] != other[other.Length - 1 - i])
{
builder.Remove(0, builder.Length - i);
break;
}
}
}
return builder?.ToString() ?? "";
}
private static string[] FontEnumValues => _fontEnumValues ?? (_fontEnumValues = typeof(VRage.Game.MyFontEnum).GetFields(BindingFlags.Public | BindingFlags.Static).Where(x => x.IsLiteral && !x.IsInitOnly).Select(x => (string)x.GetValue(null)).ToArray());
private static string[] _fontEnumValues;
public static bool IsFontEnum(string str)
{
return FontEnumValues.Contains(str);
}
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using NLog;
using Torch.API.Utils;
using Version = SemanticVersioning.Version;
namespace Torch.API.WebAPI
{
public class JenkinsQuery
{
private const string BRANCH_QUERY = "http://136.243.80.164:2690/job/Torch/job/{0}/" + API_PATH;
private const string ARTIFACT_PATH = "artifact/bin/torch-server.zip";
private const string API_PATH = "api/json";
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static JenkinsQuery _instance;
public static JenkinsQuery Instance => _instance ??= new JenkinsQuery();
private HttpClient _client;
private JenkinsQuery()
{
_client = new HttpClient();
}
public async Task<Job> GetLatestVersion(string branch)
{
var h = await _client.GetAsync(string.Format(BRANCH_QUERY, branch));
if (!h.IsSuccessStatusCode)
{
Log.Error($"'{branch}' Branch query failed with code {h.StatusCode}");
if(h.StatusCode == HttpStatusCode.NotFound)
Log.Error("This likely means you're trying to update a branch that is not public on Jenkins. Sorry :(");
return null;
}
var branchResponse = await h.Content.ReadFromJsonAsync<BranchResponse>();
if (branchResponse is null)
{
Log.Error("Error reading branch response");
return null;
}
h = await _client.GetAsync($"{branchResponse.LastStableBuild.Url}{API_PATH}");
if (h.IsSuccessStatusCode)
return await h.Content.ReadFromJsonAsync<Job>();
Log.Error($"Job query failed with code {h.StatusCode}");
return null;
}
public async Task<bool> DownloadRelease(Job job, string path)
{
var h = await _client.GetAsync(job.Url + ARTIFACT_PATH);
if (!h.IsSuccessStatusCode)
{
Log.Error($"Job download failed with code {h.StatusCode}");
return false;
}
var s = await h.Content.ReadAsStreamAsync();
await using var fs = new FileStream(path, FileMode.Create);
await s.CopyToAsync(fs);
return true;
}
}
public record BranchResponse(string Name, string Url, Build LastBuild, Build LastStableBuild);
public record Build(int Number, string Url);
public record Job(int Number, bool Building, string Description, string Result, string Url,
[property: JsonConverter(typeof(SemanticVersionConverter))] Version Version);
}

View File

@@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using NLog;
namespace Torch.API.WebAPI
{
public class PluginQuery
{
private const string ALL_QUERY = "https://torchapi.com/api/plugins/";
private const string PLUGIN_QUERY = "https://torchapi.com/api/plugins/?guid={0}";
private readonly HttpClient _client;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static PluginQuery _instance;
public static PluginQuery Instance => _instance ??= new();
private PluginQuery()
{
_client = new();
}
public async Task<PluginsResponse> QueryAll()
{
return (PluginsResponse) await _client.GetFromJsonAsync(ALL_QUERY, typeof(PluginsResponse), CancellationToken.None);
}
public Task<PluginItem> QueryOne(Guid guid)
{
return QueryOne(guid.ToString());
}
public async Task<PluginItem> QueryOne(string guid)
{
using var res = await _client.GetAsync(string.Format(PLUGIN_QUERY, guid));
if (!res.IsSuccessStatusCode)
return null;
return await res.Content.ReadFromJsonAsync<PluginItem>();
}
public Task<bool> DownloadPlugin(Guid guid, string path = null)
{
return DownloadPlugin(guid.ToString(), path);
}
public async Task<bool> DownloadPlugin(string guid, string path = null)
{
var item = await QueryOne(guid);
if (item is null) return false;
return await DownloadPlugin(item, path);
}
public async Task<bool> DownloadPlugin(PluginItem item, string path = null)
{
try
{
path ??= Path.Combine(Directory.GetCurrentDirectory(), "Plugins", $"{item.Name}.zip");
if (item.Versions.Length == 0)
{
Log.Error($"Selected plugin {item.Name} does not have any versions to download!");
return false;
}
var version = item.Versions.FirstOrDefault(v => v.Version == item.LatestVersion);
if (version is null)
{
Log.Error($"Could not find latest version for selected plugin {item.Name}");
return false;
}
var s = await _client.GetStreamAsync(version.Url);
if(File.Exists(path))
File.Delete(path);
await using var f = File.Create(path);
await s.CopyToAsync(f);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to download plugin!");
}
return true;
}
}
public record PluginsResponse(PluginItem[] Plugins);
public record PluginItem(Guid Id, string Name, string Author, string Description, string LatestVersion,
VersionItem[] Versions)
{
[JsonIgnore]
public bool Installed { get; set; }
}
public record VersionItem(string Version, string Note, [property: JsonPropertyName("is_beta")] bool IsBeta,
string Url);
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.4.1" targetFramework="net461" />
</packages>

View File

@@ -1,12 +1,17 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch Server")] [assembly: AssemblyTitle("Torch Client Tests")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Torch")] [assembly: AssemblyProduct("Torch")]
[assembly: AssemblyCopyright("Copyright © Torch API 2017")] [assembly: AssemblyCopyright("Copyright © Torch API 2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif

View File

@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Client Tests</AssemblyTitle>
<Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DebugType>pdbonly</DebugType>
<DocumentationFile>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\Torch.Client.Tests.xml</DocumentationFile>
</PropertyGroup>
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
<ItemGroup>
<PackageReference Include="Mono.TextTransform" Version="1.0.0" />
<PackageReference Include="NLog" Version="4.4.12" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.1" />
<PackageReference Include="xunit.assert" Version="2.2.0" />
<PackageReference Include="xunit.core" Version="2.2.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.2.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.2.0" />
<PackageReference Include="xunit.runner.console" Version="2.2.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties\AssemblyVersion.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch.Client\Torch.Client.csproj" />
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj" />
<ProjectReference Include="..\Torch\Torch.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="obj\x64\Debug\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
<Compile Remove="obj\x64\Release\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using Torch.Client;
using Torch.Tests;
using Torch.Utils;
using Xunit;
namespace Torch.Client.Tests
{
public class TorchClientReflectionTest
{
static TorchClientReflectionTest()
{
TestUtils.Init();
}
private static ReflectionTestManager _manager;
private static ReflectionTestManager Manager()
{
if (_manager != null)
return _manager;
return _manager = new ReflectionTestManager().Init(typeof(TorchClient).Assembly);
}
public static IEnumerable<object[]> Getters => Manager().Getters;
public static IEnumerable<object[]> Setters => Manager().Setters;
public static IEnumerable<object[]> Invokers => Manager().Invokers;
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
public static IEnumerable<object[]> Events => Manager().Events;
#region Binding
[Theory]
[MemberData(nameof(Getters))]
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
[MemberData(nameof(Setters))]
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
[MemberData(nameof(Invokers))]
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
[MemberData(nameof(MemberInfo))]
public void TestBindingMemberInfo(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
[Theory]
[MemberData(nameof(Events))]
public void TestBindingEvents(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
((Func<ReflectedEventReplacer>)field.Field.GetValue(null)).Invoke();
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
namespace Torch.Client.Manager
{
public class MultiplayerManagerClient : MultiplayerManagerBase, IMultiplayerManagerClient
{
/// <inheritdoc />
public MultiplayerManagerClient(ITorchBase torch) : base(torch) { }
/// <inheritdoc />
public override void Attach()
{
base.Attach();
MyMultiplayer.Static.ClientJoined += RaiseClientJoined;
}
/// <inheritdoc />
public override void Detach()
{
MyMultiplayer.Static.ClientJoined -= RaiseClientJoined;
base.Detach();
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.World;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using VRage.Game.ModAPI;
namespace Torch.Client.Manager
{
public class MultiplayerManagerLobby : MultiplayerManagerBase, IMultiplayerManagerServer
{
/// <inheritdoc />
public IReadOnlyList<ulong> BannedPlayers => new List<ulong>();
/// <inheritdoc />
public MultiplayerManagerLobby(ITorchBase torch) : base(torch) { }
/// <inheritdoc />
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
/// <inheritdoc />
public void BanPlayer(ulong steamId, bool banned = true) => Torch.Invoke(() => MyMultiplayer.Static.BanClient(steamId, banned));
/// <inheritdoc />
public void PromoteUser(ulong steamId)
{
Torch.Invoke(() =>
{
var p = MySession.Static.GetUserPromoteLevel(steamId);
if (p < MyPromoteLevel.Admin) //cannot promote to owner by design
MySession.Static.SetUserPromoteLevel(steamId, p + 1);
});
}
/// <inheritdoc />
public void DemoteUser(ulong steamId)
{
Torch.Invoke(() =>
{
var p = MySession.Static.GetUserPromoteLevel(steamId);
if (p > MyPromoteLevel.None && p < MyPromoteLevel.Owner) //owner cannot be demoted by design
MySession.Static.SetUserPromoteLevel(steamId, p - 1);
});
}
/// <inheritdoc />
public MyPromoteLevel GetUserPromoteLevel(ulong steamId)
{
return MySession.Static.GetUserPromoteLevel(steamId);
}
/// <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 event Action<ulong, MyPromoteLevel> PlayerPromoted
{
add => throw new NotImplementedException();
remove => throw new NotImplementedException();
}
/// <inheritdoc/>
public override void Attach()
{
base.Attach();
MyMultiplayer.Static.ClientJoined += RaiseClientJoined;
}
/// <inheritdoc/>
public override void Detach()
{
MyMultiplayer.Static.ClientJoined -= RaiseClientJoined;
base.Detach();
}
}
}

View File

@@ -1,17 +1,180 @@
using System; using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Forms;
using NLog; using NLog;
using Torch.Utils;
using MessageBox = System.Windows.MessageBox;
namespace Torch.Client namespace Torch.Client
{ {
public static class Program public static class Program
{ {
public const string SpaceEngineersBinaries = "Bin64";
private static string _spaceEngInstallAlias = null;
public static string SpaceEngineersInstallAlias
{
get
{
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
if (_spaceEngInstallAlias == null)
{
// ReSharper disable once AssignNullToNotNullAttribute
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location),
"SpaceEngineersAlias");
}
return _spaceEngInstallAlias;
}
}
private const string _steamSpaceEngineersDirectory = @"steamapps\common\SpaceEngineers\";
private const string _spaceEngineersVerifyFile = SpaceEngineersBinaries + @"\SpaceEngineers.exe";
public const string ConfigName = "Torch.cfg";
private static Logger _log = LogManager.GetLogger("Torch"); private static Logger _log = LogManager.GetLogger("Torch");
#if DEBUG
[DllImport("kernel32.dll")]
private static extern void AllocConsole();
[DllImport("kernel32.dll")]
private static extern void FreeConsole();
#endif
public static void Main(string[] args) public static void Main(string[] args)
{ {
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; #if DEBUG
try
{
AllocConsole();
#endif
if (!TorchLauncher.IsTorchWrapped())
{
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
{
FreeConsole();
}
#endif
}
private static void SetupSpaceEngInstallAlias()
{
string spaceEngineersDirectory = null;
// 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;
if (steamDir != null)
{
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
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)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog
{
Description = "Please select the SpaceEngineers installation folder"
};
do
{
if (dialog.ShowDialog() != DialogResult.OK)
{
var ex = new FileNotFoundException(
"Unable to find the Space Engineers install directory, aborting");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
spaceEngineersDirectory = dialog.SelectedPath;
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
break;
if (MessageBox.Show(
$"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.
}
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
{
var ex = new IOException(
$"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
if (!File.Exists(junctionVerify))
{
var ex = new FileNotFoundException(
$"Junction link is not working. File {junctionVerify} does not exist");
_log.Fatal(ex);
LogManager.Flush();
throw ex;
}
}
private static bool JunctionLink(string linkName, string targetDir)
{
var junctionLinkProc = new ProcessStartInfo("cmd.exe", $"/c mklink /J \"{linkName}\" \"{targetDir}\"")
{
WorkingDirectory = Directory.GetCurrentDirectory(),
UseShellExecute = false,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII
};
Process cmd = Process.Start(junctionLinkProc);
// ReSharper disable once PossibleNullReferenceException
while (!cmd.HasExited)
{
string line = cmd.StandardOutput.ReadLine();
if (!string.IsNullOrWhiteSpace(line))
_log.Info(line);
Thread.Sleep(100);
}
if (cmd.ExitCode != 0)
_log.Error("Unable to create junction link {0} => {1}", linkName, targetDir);
return cmd.ExitCode == 0;
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception) e.ExceptionObject;
_log.Error(ex);
LogManager.Flush();
MessageBox.Show(ex.StackTrace, ex.Message);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void RunClient()
{
var client = new TorchClient(); var client = new TorchClient();
try try
@@ -27,11 +190,5 @@ namespace Torch.Client
client.Start(); client.Start();
} }
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
MessageBox.Show(ex.StackTrace, ex.Message);
}
} }
} }

View File

@@ -1,4 +1,17 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.169.376")] [assembly: AssemblyTitle("Torch Client")]
[assembly: AssemblyFileVersion("1.0.169.376")] [assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Torch")]
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif

View File

@@ -1,16 +0,0 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System.Reflection;
<# var dt = DateTime.Now;
int major = 1;
int minor = 0;
int build = dt.DayOfYear;
int rev = (int)dt.TimeOfDay.TotalMinutes / 2;
#>
[assembly: AssemblyVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]

View File

@@ -1,74 +1,68 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E36DF745-260B-4956-A2E8-09F08B2E7161}</ProjectGuid>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <TargetFramework>net461</TargetFramework>
<RootNamespace>Torch.Client</RootNamespace>
<AssemblyName>Torch.Client</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile /> <AssemblyTitle>Torch Client</AssemblyTitle>
<Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutputPath>
<Prefer32Bit>true</Prefer32Bit>
<UseWPF>true</UseWPF>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget> <DocumentationFile>$(SolutionDir)\bin\$(Platform)\$(Configuration)\Torch.Client.xml</DocumentationFile>
<ErrorReport>prompt</ErrorReport> <NoWarn>1591</NoWarn>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
<DocumentationFile>bin\x64\Release\Torch.Client.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>torchicon.ico</ApplicationIcon> <ApplicationIcon>torchicon.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
<ItemGroup> <ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <PackageReference Include="Mono.TextTransform" Version="1.0.0" />
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath> <PackageReference Include="NLog" Version="4.4.12" />
<Private>True</Private> </ItemGroup>
<ItemGroup>
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
<Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="Sandbox.Game"> <Reference Include="Sandbox.Game">
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="Sandbox.Graphics, Version=0.1.6108.20417, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="Sandbox.Graphics, Version=0.1.6108.20417, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath> <HintPath>..\GameBinaries\Sandbox.Graphics.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="SpaceEngineers.Game"> <Reference Include="SpaceEngineers.Game">
<HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath> <HintPath>..\GameBinaries\SpaceEngineers.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Xaml"> <Reference Include="System.Xaml" />
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="VRage"> <Reference Include="VRage">
<HintPath>..\GameBinaries\VRage.dll</HintPath> <HintPath>..\GameBinaries\VRage.dll</HintPath>
<Private>False</Private> <Private>False</Private>
@@ -77,19 +71,23 @@
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath> <HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Game.XmlSerializers">
<HintPath>..\GameBinaries\VRage.Game.XmlSerializers.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64"> <Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath> <HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="VRage.Library"> <Reference Include="VRage.Library">
<HintPath>..\GameBinaries\VRage.Library.dll</HintPath> <HintPath>..\GameBinaries\VRage.Library.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="VRage.Math, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\GameBinaries\VRage.Math.dll</HintPath> <HintPath>..\GameBinaries\VRage.Math.dll</HintPath>
<Private>False</Private> <Private>False</Private>
<SpecificVersion>False</SpecificVersion>
</Reference> </Reference>
<Reference Include="VRage.Render"> <Reference Include="VRage.Render">
<HintPath>..\GameBinaries\VRage.Render.dll</HintPath> <HintPath>..\GameBinaries\VRage.Render.dll</HintPath>
@@ -99,76 +97,26 @@
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath> <HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="VRage.Steam">
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties\AssemblyVersion.cs" />
<DependentUpon>AssemblyInfo.tt</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="Properties\AssemblyInfo1.cs" />
<Compile Include="TorchClient.cs" />
<Compile Include="TorchConsoleScreen.cs" />
<Compile Include="TorchMainMenuScreen.cs" />
<Compile Include="TorchSettingsScreen.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj"> <ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project> <ProjectReference Include="..\Torch\Torch.csproj" />
<Name>Torch.API</Name>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\Torch\Torch.csproj">
<Project>{7E01635C-3B67-472E-BCD6-C5539564F214}</Project>
<Name>Torch</Name>
<Private>True</Private>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="torchicon.ico" /> <Resource Include="torchicon.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Properties\AssemblyInfo.tt"> <Compile Remove="obj\x64\Debug\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
<Generator>TextTemplatingFileGenerator</Generator> <Compile Remove="obj\x64\Release\.NETFramework,Version=v4.6.1.AssemblyAttributes.cs" />
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project> </Project>

View File

@@ -1,70 +1,56 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Sandbox; using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Platform; using Sandbox.Engine.Platform;
using Sandbox.Engine.Utils;
using Sandbox.Game; using Sandbox.Game;
using SpaceEngineers.Game; using SpaceEngineers.Game;
using VRage.Steam;
using Torch.API; using Torch.API;
using Torch.API.Managers;
using Torch.API.Session;
using Torch.Client.Manager;
using Torch.Client.UI;
using Torch.Session;
using VRage;
using VRage.FileSystem; using VRage.FileSystem;
using VRage.GameServices;
using VRageRender; using VRageRender;
using VRageRender.ExternalApp;
namespace Torch.Client namespace Torch.Client
{ {
public class TorchClient : TorchBase, ITorchClient public class TorchClient : TorchBase, ITorchClient
{ {
private MyCommonProgramStartup _startup; protected override uint SteamAppId => 244850;
private IMyRender _renderer; protected override string SteamAppName => "SpaceEngineers";
private const uint APP_ID = 244850;
private VRageGameServices _services; public TorchClient()
{
Config = new TorchClientConfig();
var sessionManager = Managers.GetManager<ITorchSessionManager>();
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerLobby
? new MultiplayerManagerLobby(this)
: null);
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerClientBase
? new MultiplayerManagerClient(this)
: null);
}
public override void Init() public override void Init()
{ {
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
Log.Info("Initializing Torch Client"); Log.Info("Initializing Torch Client");
Config.InstancePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
SteamAppName);
base.Init(); base.Init();
OverrideMenus();
if (!File.Exists("steam_appid.txt")) SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
{
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(VRage.FastResourceLock).Assembly.Location) + "\\..");
}
SpaceEngineersGame.SetupBasicGameInfo();
_startup = new MyCommonProgramStartup(RunArgs);
if (_startup.PerformReporting())
return;
_startup.PerformAutoconnect();
if (!_startup.CheckSingleInstance())
return;
var appDataPath = _startup.GetAppDataPath();
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
MyInitializer.InitCheckSum();
if (!_startup.Check64Bit())
return;
_startup.DetectSharpDxLeaksBeforeRun();
using (var mySteamService = new SteamService(Game.IsDedicated, APP_ID))
{
_renderer = null;
SpaceEngineersGame.SetupPerGameSettings();
OverrideMenus();
InitializeRender();
_services = new VRageGameServices(mySteamService);
if (!Game.IsDedicated)
MyFileSystem.InitUserSpecific(mySteamService.UserId.ToString());
}
_startup.DetectSharpDxLeaksAfterRun();
MyInitializer.InvokeAfterRun();
} }
private void OverrideMenus() private void OverrideMenus()
@@ -72,70 +58,35 @@ namespace Torch.Client
var credits = new MyCreditsDepartment("Torch Developed By") var credits = new MyCreditsDepartment("Torch Developed By")
{ {
Persons = new List<MyCreditsPerson> Persons = new List<MyCreditsPerson>
{ {
new MyCreditsPerson("THE TORCH TEAM"), new MyCreditsPerson("THE TORCH TEAM"),
new MyCreditsPerson("http://github.com/TorchSE"), new MyCreditsPerson("http://github.com/TorchSE"),
} }
}; };
MyPerGameSettings.Credits.Departments.Insert(0, credits); MyPerGameSettings.Credits.Departments.Insert(0, credits);
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen); MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
} }
public override void Start() private void SetRenderWindowTitle(string title)
{ {
using (var spaceEngineersGame = new SpaceEngineersGame(_services, RunArgs)) MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
{ if (renderThread == null)
Log.Info("Starting client"); return;
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded; FieldInfo renderWindowField = typeof(MyRenderThread).GetField("m_renderWindow",
spaceEngineersGame.Run(); BindingFlags.Instance | BindingFlags.NonPublic);
} if (renderWindowField == null)
return;
var window =
renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as
System.Windows.Forms.Form;
if (window != null)
renderThread.Invoke(() => { window.Text = title; });
} }
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e) public override void Restart()
{ {
} throw new NotImplementedException();
public override void Stop()
{
MySandboxGame.ExitThreadSafe();
}
private void InitializeRender()
{
try
{
if (Game.IsDedicated)
{
_renderer = new MyNullRender();
}
else
{
var graphicsRenderer = MySandboxGame.Config.GraphicsRenderer;
if (graphicsRenderer == MySandboxGame.DirectX11RendererKey)
{
_renderer = new MyDX11Render();
if (!_renderer.IsSupported)
{
MySandboxGame.Log.WriteLine("DirectX 11 renderer not supported. No renderer to revert back to.");
_renderer = null;
}
}
if (_renderer == null)
throw new MyRenderException("The current version of the game requires a Dx11 card. \\n For more information please see : http://blog.marekrosa.org/2016/02/space-engineers-news-full-source-code_26.html", MyRenderExceptionEnum.GpuNotSupported);
MySandboxGame.Config.GraphicsRenderer = graphicsRenderer;
}
MyRenderProxy.Initialize(_renderer);
MyRenderProxy.GetRenderProfiler().SetAutocommit(false);
MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Render Initialization Failed");
Environment.Exit(-1);
}
} }
} }
} }

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Client
{
public class TorchClientConfig : ITorchConfig
{
// How do we want to handle client side config? It's radically different than the server.
public bool GetPluginUpdates { get; set; } = false;
public bool GetTorchUpdates { get; set; } = false;
public string InstanceName { get; set; } = "TorchClient";
public string InstancePath { get; set; }
public bool NoUpdate { get; set; } = true;
public List<string> Plugins { get; set; }
public bool ShouldUpdatePlugins { get; } = false;
public bool ShouldUpdateTorch { get; } = false;
public int TickTimeout { get; set; }
public bool Autostart { get; set; } = false;
public bool ForceUpdate { get; set; } = false;
public bool NoGui { get; set; } = false;
public bool RestartOnCrash { get; set; } = false;
public string WaitForPID { get; set; } = null;
public string ChatName { get; set; }
public string ChatColor { get; set; }
public bool Save(string path = null)
{
return true;
}
}
}

View File

@@ -1,35 +0,0 @@
#pragma warning disable 618
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Graphics;
using Sandbox.Graphics.GUI;
using Sandbox.Gui;
using SpaceEngineers.Game.GUI;
using VRage.Game;
using VRage.Utils;
using VRageMath;
namespace Torch.Client
{
public class TorchMainMenuScreen : MyGuiScreenMainMenu
{
/// <inheritdoc />
public override void RecreateControls(bool constructor)
{
base.RecreateControls(constructor);
var buttonSize = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
Vector2 leftButtonPositionOrigin = MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_BOTTOM) + new Vector2(buttonSize.X / 2f, 0f);
var btn = MakeButton(leftButtonPositionOrigin - 9 * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA, MyStringId.GetOrCompute("Torch"), TorchButtonClicked);
Controls.Add(btn);
}
private void TorchButtonClicked(MyGuiControlButton obj)
{
MyGuiSandbox.AddScreen(new TorchSettingsScreen());
}
}
}

View File

@@ -0,0 +1,56 @@
#pragma warning disable 618
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.Gui;
using Sandbox.Graphics;
using Sandbox.Graphics.GUI;
using Sandbox.Gui;
using SpaceEngineers.Game.GUI;
using Torch.Utils;
using VRage.Game;
using VRage.Utils;
using VRageMath;
namespace Torch.Client.UI
{
public class TorchMainMenuScreen : MyGuiScreenMainMenu
{
#pragma warning disable 169
[ReflectedGetter(Name = "m_elementGroup")]
private static Func<MyGuiScreenMainMenu, MyGuiControlElementGroup> _elementsGroup;
#pragma warning restore 169
public TorchMainMenuScreen() : this(false)
{
}
public TorchMainMenuScreen(bool pauseGame)
: base(pauseGame)
{
}
/// <inheritdoc />
public override void RecreateControls(bool constructor)
{
base.RecreateControls(constructor);
Vector2 minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
Vector2 value = MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_BOTTOM, 54, 54) + new Vector2(minSizeGui.X / 2f, 0f) + new Vector2(15f, 0f) / MyGuiConstants.GUI_OPTIMAL_SIZE;
MyGuiControlButton myGuiControlButton = MakeButton(value - 9 * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA,
MyStringId.GetOrCompute("Torch"), TorchButtonClicked, MyCommonTexts.ToolTipExitToWindows);
Controls.Add(myGuiControlButton);
_elementsGroup.Invoke(this).Add(myGuiControlButton);
}
private void TorchButtonClicked(MyGuiControlButton obj)
{
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen<TorchNavScreen>());
}
}
}

View File

@@ -0,0 +1,49 @@
using Sandbox;
using Sandbox.Game.Gui;
using Sandbox.Graphics.GUI;
using VRage;
using VRage.Game;
using VRage.Utils;
using VRageMath;
namespace Torch.Client.UI
{
public class TorchNavScreen : MyGuiScreenBase
{
private MyGuiControlElementGroup _elementGroup;
public TorchNavScreen() : base(new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR, new Vector2(0.35875f, 0.558333337f))
{
EnabledBackgroundFade = true;
RecreateControls(true);
}
public override void RecreateControls(bool constructor)
{
base.RecreateControls(constructor);
_elementGroup = new MyGuiControlElementGroup();
_elementGroup.HighlightChanged += ElementGroupHighlightChanged;
AddCaption(MyCommonTexts.ScreenCaptionOptions, null, null);
var value = new Vector2(0f, -m_size.Value.Y / 2f + 0.146f);
var num = 0;
var myGuiControlButton = new MyGuiControlButton(value + num++ * MyGuiConstants.MENU_BUTTONS_POSITION_DELTA, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER, null, MyTexts.Get(MyCommonTexts.ScreenOptionsButtonGame), 0.8f, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER, MyGuiControlHighlightType.WHEN_ACTIVE, delegate(MyGuiControlButton sender)
{
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen<TorchSettingsScreen>());
}, GuiSounds.MouseClick, 1f, null);
Controls.Add(myGuiControlButton);
_elementGroup.Add(myGuiControlButton);
CloseButtonEnabled = true;
}
private void ElementGroupHighlightChanged(MyGuiControlElementGroup obj)
{
foreach (MyGuiControlBase current in _elementGroup)
if (current.HasFocus && obj.SelectedElement != current)
FocusedControl = obj.SelectedElement;
}
public override string GetFriendlyName() => "Torch";
public void OnBackClick(MyGuiControlButton sender) => CloseScreen();
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Graphics.GUI;
using VRageMath;
namespace Torch.Client.UI
{
public class TorchSettingsScreen : MyGuiScreenBase
{
public TorchSettingsScreen() : base(new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR,
new Vector2(0.35875f, 0.558333337f))
{
EnabledBackgroundFade = true;
RecreateControls(true);
}
/// <inheritdoc />
public override string GetFriendlyName() => "Torch Settings";
public void OnBackClick(MyGuiControlButton sender) => CloseScreen();
}
}

11
Torch.Client/app.config Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.4.1" targetFramework="net461" />
</packages>

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,57 @@
using System;
using System.Collections.Generic;
using System.Text;
using ProtoBuf;
using Sandbox.ModAPI;
namespace Torch.Mod.Messages
{
[ProtoContract]
public class JoinServerMessage : MessageBase
{
[ProtoMember(201)]
public int Delay;
[ProtoMember(202)]
public string Address;
private JoinServerMessage()
{
}
public JoinServerMessage(string address)
{
Address = address;
}
public JoinServerMessage(string address, int delay)
{
Address = address;
Delay = delay;
}
public override void ProcessClient()
{
if (TorchModCore.Debug)
{
MyAPIGateway.Utilities.ShowMessage("Torch", $"Joining server {Address} with delay {Delay}");
}
if (Delay <= 0)
{
MyAPIGateway.Multiplayer.JoinServer(Address);
return;
}
MyAPIGateway.Parallel.StartBackground(() =>
{
MyAPIGateway.Parallel.Sleep(Delay);
MyAPIGateway.Multiplayer.JoinServer(Address);
});
}
public override void ProcessServer()
{
}
}
}

View File

@@ -0,0 +1,52 @@
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))]
[ProtoInclude(4, typeof(JoinServerMessage))]
#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,208 @@
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.Network;
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;
#if TORCH
m.SenderId = MyEventContext.Current.Sender.Value;
#endif
_processing.Add(m);
}
public static void DoProcessing()
{
while (!_closing)
{
try
{
MessageBase m;
try
{
m = _processing.Take();
}
catch
{
continue;
}
MyLog.Default.WriteLineAndConsole($"Processing message: {m.GetType().Name}");
if (m is IncomingMessage) //process incoming messages
{
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 (TorchModCore.Debug)
MyAPIGateway.Utilities.ShowMessage("Torch", $"Received message of type {i.GetType().Name}");
if (MyAPIGateway.Multiplayer.IsServer)
i.ProcessServer();
else
i.ProcessClient();
}
else //process outgoing messages
{
if (TorchModCore.Debug)
MyAPIGateway.Utilities.ShowMessage("Torch", $"Sending message of type {m.GetType().Name}");
var b = MyAPIGateway.Utilities.SerializeToBinary(m);
m.CompressedData = MyCompression.Compress(b);
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: INFO: Communication thread shut down successfully! THIS IS NOT AN ERROR");
//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,21 @@
<?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\JoinServerMessage.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,12 @@
<?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" />
<Import Project="Torch.Mod.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

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

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.ModAPI;
using VRage.Game.Components;
namespace Torch.Mod
{
[MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
public class TorchModCore : MySessionComponentBase
{
public const ulong MOD_ID = 2722000298;
private static bool _init;
public static bool Debug;
public override void UpdateAfterSimulation()
{
if (_init)
return;
_init = true;
ModCommunication.Register();
MyAPIGateway.Utilities.MessageEntered += Utilities_MessageEntered;
}
private void Utilities_MessageEntered(string messageText, ref bool sendToOthers)
{
if (messageText == "@!debug")
{
Debug = !Debug;
MyAPIGateway.Utilities.ShowMessage("Torch", $"Debug: {Debug}");
sendToOthers = false;
}
}
protected override void UnloadData()
{
try
{
MyAPIGateway.Utilities.MessageEntered -= Utilities_MessageEntered;
ModCommunication.Unregister();
}
catch
{
//session unloading, don't care
}
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Torch Server Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Torch")]
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif

View File

@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6-windows</TargetFramework>
<NoWarn>1591,0649</NoWarn>
<AssemblyTitle>Torch Server Tests</AssemblyTitle>
<Product>Torch</Product>
<Copyright>Copyright © Torch API 2017</Copyright>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<OutputPath>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\</OutputPath>
<PlatformTarget>x64</PlatformTarget>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup Condition="$(Configuration) == 'Release'">
<DocumentationFile>$(SolutionDir)\bin-test\$(Platform)\$(Configuration)\Torch.Server.Tests.xml</DocumentationFile>
</PropertyGroup>
<!-- <Import Project="$(SolutionDir)\TransformOnBuild.targets" /> -->
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NLog" Version="5.0.0-rc2" />
<PackageReference Include="xunit" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\Versioning\AssemblyVersion.cs" Link="Properties\AssemblyVersion.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj" />
<ProjectReference Include="..\Torch.Server\Torch.Server.csproj" />
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj" />
<ProjectReference Include="..\Torch\Torch.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using Torch.Tests;
using Torch.Utils;
using Xunit;
namespace Torch.Server.Tests
{
#warning Disabled reflection tests because of seemingly random failures
public class TorchServerReflectionTest
{
static TorchServerReflectionTest()
{
TestUtils.Init();
}
private static ReflectionTestManager _manager;
private static ReflectionTestManager Manager()
{
if (_manager != null)
return _manager;
return _manager = new ReflectionTestManager().Init(typeof(TorchServer).Assembly);
}
public static IEnumerable<object[]> Getters => Manager().Getters;
public static IEnumerable<object[]> Setters => Manager().Setters;
public static IEnumerable<object[]> Invokers => Manager().Invokers;
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
public static IEnumerable<object[]> Events => Manager().Events;
#region Binding
//[Theory]
[MemberData(nameof(Getters))]
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
//[Theory]
[MemberData(nameof(Setters))]
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
//[Theory]
[MemberData(nameof(Invokers))]
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
Assert.NotNull(field.Field.GetValue(null));
}
//[Theory]
[MemberData(nameof(Events))]
public void TestBindingEvents(ReflectionTestManager.FieldRef field)
{
if (field.Field == null)
return;
Assert.True(ReflectedManager.Process(field.Field));
if (field.Field.IsStatic)
((Func<ReflectedEventReplacer>)field.Field.GetValue(null)).Invoke();
}
#endregion
}
}

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,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="protobuf-net" publicKeyToken="257b51d87d2e4d67" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.4.0.0" newVersion="2.4.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>

View File

@@ -0,0 +1,66 @@
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.Contains(steamId))
{
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,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<PropertyChanged />
</Weavers>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="TriggerDependentProperties" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EventInvokerNames" type="xs:string">
<xs:annotation>
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEquality" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

186
Torch.Server/Initializer.cs Normal file
View File

@@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
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;
namespace Torch.Server
{
public class Initializer
{
internal static Initializer Instance { get; private set; }
private static readonly Logger Log = LogManager.GetLogger(nameof(Initializer));
private bool _init;
private const string STEAMCMD_DIR = "steamcmd";
private const string STEAMCMD_ZIP = "temp.zip";
private static readonly string STEAMCMD_EXE = "steamcmd.exe";
private static readonly string RUNSCRIPT_FILE = "runscript.txt";
private const string RUNSCRIPT = @"force_install_dir ../
login anonymous
app_update 298740
quit";
private TorchServer _server;
internal Persistent<TorchConfig> ConfigPersistent { get; }
public TorchConfig Config => ConfigPersistent?.Data;
public TorchServer Server => _server;
public Initializer(string basePath, Persistent<TorchConfig> torchConfig)
{
Instance = this;
ConfigPersistent = torchConfig;
}
public bool Initialize(string[] args)
{
if (_init)
return false;
#if DEBUG
//enables logging debug messages when built in debug mode. Amazing.
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "main");
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "console");
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "wpf");
LogManager.ReconfigExistingLoggers();
Log.Debug("Debug logging enabled.");
#endif
// This is what happens when Keen is bad and puts extensions into the System namespace.
if (!Enumerable.Contains(args, "-noupdate"))
RunSteamCmd();
if (!string.IsNullOrEmpty(Config.WaitForPID))
{
try
{
var pid = int.Parse(Config.WaitForPID);
var waitProc = Process.GetProcessById(pid);
Log.Info("Continuing in 5 seconds.");
Log.Warn($"Waiting for process {pid} to close");
while (!waitProc.HasExited)
{
Console.Write(".");
Thread.Sleep(1000);
}
}
catch (Exception e)
{
Log.Warn(e);
}
}
_init = true;
return true;
}
public void Run(bool isService, string instanceName, string instancePath)
{
_server = new TorchServer(Config, instancePath, instanceName);
if (isService || Config.NoGui)
{
_server.Init();
_server.Start();
}
else
{
#if !DEBUG
if (!Config.IndependentConsole)
{
Console.SetOut(TextWriter.Null);
NativeMethods.FreeConsole();
}
#endif
var gameThread = new Thread(() =>
{
_server.Init();
if (Config.Autostart || Config.TempAutostart)
{
Config.TempAutostart = false;
_server.Start();
}
});
gameThread.Start();
var ui = new TorchUI(_server);
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
ui.ShowDialog();
}
}
public static void RunSteamCmd()
{
var log = LogManager.GetLogger("SteamCMD");
var path = Environment.GetEnvironmentVariable("TORCH_STEAMCMD") ?? Path.GetFullPath(STEAMCMD_DIR);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
var runScriptPath = Path.Combine(path, RUNSCRIPT_FILE);
if (!File.Exists(runScriptPath))
File.WriteAllText(runScriptPath, RUNSCRIPT);
var steamCmdExePath = Path.Combine(path, STEAMCMD_EXE);
if (!File.Exists(steamCmdExePath))
{
try
{
log.Info("Downloading SteamCMD.");
using (var client = new HttpClient())
using (var file = File.Create(STEAMCMD_ZIP))
client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result.CopyTo(file);
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, path);
File.Delete(STEAMCMD_ZIP);
log.Info("SteamCMD downloaded successfully!");
}
catch (Exception e)
{
log.Error(e, "Failed to download SteamCMD, unable to update the DS.");
return;
}
}
log.Info("Checking for DS updates.");
var steamCmdProc = new ProcessStartInfo(steamCmdExePath, "+runscript runscript.txt")
{
WorkingDirectory = path,
UseShellExecute = false,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII
};
var cmd = Process.Start(steamCmdProc);
// ReSharper disable once PossibleNullReferenceException
while (!cmd.HasExited)
{
log.Info(cmd.StandardOutput.ReadLine());
Thread.Sleep(100);
}
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace Torch.Server
{
public static class ListBoxExtensions
{
//https://stackoverflow.com/questions/28689125/how-to-autoscroll-listbox-to-bottom-wpf-c
public static void ScrollToItem(this ListBox listBox, int index)
{
// Find a container
UIElement container = null;
for (int i = index; i > 0; i--)
{
container = listBox.ItemContainerGenerator.ContainerFromIndex(i) as UIElement;
if (container != null)
{
break;
}
}
if (container == null)
return;
// Find the ScrollContentPresenter
ScrollContentPresenter presenter = null;
for (Visual vis = container; vis != null && vis != listBox; vis = VisualTreeHelper.GetParent(vis) as Visual)
if ((presenter = vis as ScrollContentPresenter) != null)
break;
if (presenter == null)
return;
// Find the IScrollInfo
var scrollInfo =
!presenter.CanContentScroll ? presenter :
presenter.Content as IScrollInfo ??
FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ??
presenter;
// Find the amount of items that is "Visible" in the ListBox
var height = (container as ListBoxItem).ActualHeight;
var lbHeight = listBox.ActualHeight;
var showCount = (int)Math.Floor(lbHeight / height) - 1;
//Set the scrollbar
if (scrollInfo.CanVerticallyScroll)
scrollInfo.SetVerticalOffset(index - showCount);
}
private static DependencyObject FirstVisualChild(Visual visual)
{
if (visual == null) return null;
if (VisualTreeHelper.GetChildrenCount(visual) == 0) return null;
return VisualTreeHelper.GetChild(visual, 0);
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Threading;
using System.Windows.Media;
using System.Windows.Threading;
using NLog;
using NLog.Targets;
using Torch.Server.ViewModels;
using Torch.Server.Views;
namespace Torch.Server
{
/// <summary>
/// NLog target that writes to a <see cref="LogViewerControl"/>.
/// </summary>
[Target("logViewer")]
public sealed class LogViewerTarget : TargetWithLayout
{
public IList<LogEntry> LogEntries { get; set; }
public SynchronizationContext TargetContext { get; set; }
private readonly int _maxLines = 1000;
/// <inheritdoc />
protected override void Write(LogEventInfo logEvent)
{
TargetContext?.Post(_sendOrPostCallback, logEvent);
}
private void WriteCallback(object state)
{
var logEvent = (LogEventInfo) state;
LogEntries?.Add(new(logEvent.TimeStamp, Layout.Render(logEvent), LogLevelColors[logEvent.Level]));
}
private static readonly Dictionary<LogLevel, SolidColorBrush> LogLevelColors = new()
{
[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),
};
private readonly SendOrPostCallback _sendOrPostCallback;
public LogViewerTarget()
{
_sendOrPostCallback = WriteCallback;
}
}
}

View File

@@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Utils;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
using Torch.Server.ViewModels;
using VRage.Game;
namespace Torch.Server.Managers
{
//TODO
public class ConfigManager : Manager
{
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
public TorchConfig TorchConfig { get; set; }
public ConfigManager(ITorchBase torchInstance) : base(torchInstance)
{
}
/// <inheritdoc />
public override void Init()
{
LoadInstance(Torch.Config.InstancePath);
}
public void LoadInstance(string path)
{
if (!Directory.Exists(path))
throw new FileNotFoundException($"Instance directory not found at '{path}'");
var configPath = Path.Combine(path, CONFIG_NAME);
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
config.Load();
DedicatedConfig = new ConfigDedicatedViewModel(config);
}
/// <summary>
/// Creates a skeleton of a DS instance folder at the given directory.
/// </summary>
/// <param name="path"></param>
public void CreateInstance(string path)
{
if (Directory.Exists(path))
return;
Directory.CreateDirectory(path);
var savesPath = Path.Combine(path, "Saves");
Directory.CreateDirectory(savesPath);
var modsPath = Path.Combine(path, "Mods");
Directory.CreateDirectory(modsPath);
LoadInstance(path);
}
}
}

View File

@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using NLog;
using NLog.Fluent;
using Torch.API;
using Torch.Collections;
using Torch.Managers;
using Torch.Server.ViewModels.Entities;
using Torch.Utils;
namespace Torch.Server.Managers
{
/// <summary>
/// Manager that lets users bind random view models to entities in Torch's Entity Manager
/// </summary>
public class EntityControlManager : Manager
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Creates an entity control manager for the given instance of torch
/// </summary>
/// <param name="torchInstance">Torch instance</param>
internal EntityControlManager(ITorchBase torchInstance) : base(torchInstance)
{
}
private abstract class ModelFactory
{
private readonly ConditionalWeakTable<EntityViewModel, EntityControlViewModel> _models = new ConditionalWeakTable<EntityViewModel, EntityControlViewModel>();
public abstract Delegate Delegate { get; }
protected abstract EntityControlViewModel Create(EntityViewModel evm);
internal IEnumerable<EntityViewModel> Keys => _models.Select(b => b.Key);
internal EntityControlViewModel GetOrCreate(EntityViewModel evm)
{
return _models.GetValue(evm, Create);
}
internal bool TryGet(EntityViewModel evm, out EntityControlViewModel res)
{
return _models.TryGetValue(evm, out res);
}
}
private class ModelFactory<T> : ModelFactory where T : EntityViewModel
{
private readonly Func<T, EntityControlViewModel> _factory;
public override Delegate Delegate => _factory;
internal ModelFactory(Func<T, EntityControlViewModel> factory)
{
_factory = factory;
}
protected override EntityControlViewModel Create(EntityViewModel evm)
{
if (evm is T m)
{
var result = _factory(m);
_log.Trace($"Model factory {_factory.Method} created {result} for {evm}");
return result;
}
return null;
}
}
private readonly List<ModelFactory> _modelFactories = new List<ModelFactory>();
private readonly List<Delegate> _controlFactories = new List<Delegate>();
private readonly List<WeakReference<EntityViewModel>> _boundEntityViewModels = new List<WeakReference<EntityViewModel>>();
private readonly ConditionalWeakTable<EntityViewModel, MtObservableList<EntityControlViewModel>> _boundViewModels = new ConditionalWeakTable<EntityViewModel, MtObservableList<EntityControlViewModel>>();
/// <summary>
/// This factory will be used to create component models for matching entity models.
/// </summary>
/// <typeparam name="TEntityBaseModel">entity model type to match</typeparam>
/// <param name="modelFactory">Method to create component model from entity model.</param>
public void RegisterModelFactory<TEntityBaseModel>(Func<TEntityBaseModel, EntityControlViewModel> modelFactory)
where TEntityBaseModel : EntityViewModel
{
if (!typeof(TEntityBaseModel).IsAssignableFrom(modelFactory.Method.GetParameters()[0].ParameterType))
throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory));
lock (this)
{
var factory = new ModelFactory<TEntityBaseModel>(modelFactory);
_modelFactories.Add(factory);
var i = 0;
while (i < _boundEntityViewModels.Count)
{
if (_boundEntityViewModels[i].TryGetTarget(out EntityViewModel target) &&
_boundViewModels.TryGetValue(target, out MtObservableList<EntityControlViewModel> components))
{
if (target is TEntityBaseModel tent)
UpdateBinding(target, components);
i++;
}
else
_boundEntityViewModels.RemoveAtFast(i);
}
}
}
/// <summary>
/// Unregisters a factory registered with <see cref="RegisterModelFactory{TEntityBaseModel}"/>
/// </summary>
/// <typeparam name="TEntityBaseModel">entity model type to match</typeparam>
/// <param name="modelFactory">Method to create component model from entity model.</param>
public void UnregisterModelFactory<TEntityBaseModel>(Func<TEntityBaseModel, EntityControlViewModel> modelFactory)
where TEntityBaseModel : EntityViewModel
{
if (!typeof(TEntityBaseModel).IsAssignableFrom(modelFactory.Method.GetParameters()[0].ParameterType))
throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory));
lock (this)
{
for (var i = 0; i < _modelFactories.Count; i++)
{
if (_modelFactories[i].Delegate == (Delegate)modelFactory)
{
foreach (var entry in _modelFactories[i].Keys)
if (_modelFactories[i].TryGet(entry, out EntityControlViewModel ecvm) && ecvm != null
&& _boundViewModels.TryGetValue(entry, out var binding))
binding.Remove(ecvm);
_modelFactories.RemoveAt(i);
break;
}
}
}
}
/// <summary>
/// This factory will be used to create controls for matching view models.
/// </summary>
/// <typeparam name="TEntityComponentModel">component model to match</typeparam>
/// <param name="controlFactory">Method to create control from component model</param>
public void RegisterControlFactory<TEntityComponentModel>(
Func<TEntityComponentModel, Control> controlFactory)
where TEntityComponentModel : EntityControlViewModel
{
if (!typeof(TEntityComponentModel).IsAssignableFrom(controlFactory.Method.GetParameters()[0].ParameterType))
throw new ArgumentException("Generic type must match lamda type", nameof(controlFactory));
lock (this)
{
_controlFactories.Add(controlFactory);
RefreshControls<TEntityComponentModel>();
}
}
///<summary>
/// Unregisters a factory registered with <see cref="RegisterControlFactory{TEntityComponentModel}"/>
/// </summary>
/// <typeparam name="TEntityComponentModel">component model to match</typeparam>
/// <param name="controlFactory">Method to create control from component model</param>
public void UnregisterControlFactory<TEntityComponentModel>(
Func<TEntityComponentModel, Control> controlFactory)
where TEntityComponentModel : EntityControlViewModel
{
if (!typeof(TEntityComponentModel).IsAssignableFrom(controlFactory.Method.GetParameters()[0].ParameterType))
throw new ArgumentException("Generic type must match lamda type", nameof(controlFactory));
lock (this)
{
_controlFactories.Remove(controlFactory);
RefreshControls<TEntityComponentModel>();
}
}
private void RefreshControls<TEntityComponentModel>() where TEntityComponentModel : EntityControlViewModel
{
var i = 0;
while (i < _boundEntityViewModels.Count)
{
if (_boundEntityViewModels[i].TryGetTarget(out EntityViewModel target) &&
_boundViewModels.TryGetValue(target, out MtObservableList<EntityControlViewModel> components))
{
foreach (EntityControlViewModel component in components)
if (component is TEntityComponentModel)
component.InvalidateControl();
i++;
}
else
_boundEntityViewModels.RemoveAtFast(i);
}
}
/// <summary>
/// Gets the models bound to the given entity view model.
/// </summary>
/// <param name="entity">view model to query</param>
/// <returns></returns>
public MtObservableList<EntityControlViewModel> BoundModels(EntityViewModel entity)
{
return _boundViewModels.GetValue(entity, CreateFreshBinding);
}
/// <summary>
/// Gets a control for the given view model type.
/// </summary>
/// <param name="model">model to create a control for</param>
/// <returns>control, or null if none</returns>
public Control CreateControl(EntityControlViewModel model)
{
lock (this)
foreach (Delegate factory in _controlFactories)
if (factory.Method.GetParameters()[0].ParameterType.IsInstanceOfType(model) &&
factory.DynamicInvoke(model) is Control result)
{
_log.Trace($"Control factory {factory.Method} created {result}");
return result;
}
_log.Warn($"No control created for {model}");
return null;
}
private MtObservableList<EntityControlViewModel> CreateFreshBinding(EntityViewModel key)
{
var binding = new MtObservableList<EntityControlViewModel>();
lock (this)
{
_boundEntityViewModels.Add(new WeakReference<EntityViewModel>(key));
}
binding.PropertyChanged += (x, args) =>
{
if (nameof(binding.IsObserved).Equals(args.PropertyName))
UpdateBinding(key, binding);
};
return binding;
}
private void UpdateBinding(EntityViewModel key, MtObservableList<EntityControlViewModel> binding)
{
if (!binding.IsObserved)
return;
lock (this)
{
foreach (ModelFactory factory in _modelFactories)
{
var result = factory.GetOrCreate(key);
if (result != null && !binding.Contains(result))
binding.Add(result);
}
}
}
}
}

View File

@@ -0,0 +1,360 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Havok;
using NLog;
using Sandbox;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.Gui;
using Torch.API;
using Torch.API.Managers;
using Torch.Collections;
using Torch.Managers;
using Torch.Mod;
using Torch.Server.ViewModels;
using Torch.Utils;
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, IInstanceManager
{
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]
private FilesystemManager _filesystemManager;
private new ITorchServer Torch { get; }
public InstanceManager(ITorchServer torchInstance) : base(torchInstance)
{
Torch = torchInstance;
}
public IWorld SelectedWorld => DedicatedConfig.SelectedWorld;
public void LoadInstance(string path, bool validate = true)
{
Log.Info($"Loading instance {path}");
if (validate)
ValidateInstance(path);
MyFileSystem.Reset();
MyFileSystem.Init("Content", path);
//Initializes saves path. Why this isn't in Init() we may never know.
MyFileSystem.InitUserSpecific(null);
// why?....
// var configPath = Path.Combine(path, CONFIG_NAME);
// if (!File.Exists(configPath))
// {
// Log.Error($"Failed to load dedicated config at {path}");
// return;
// }
// var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
// config.Load(configPath);
DedicatedConfig = new ConfigDedicatedViewModel((MyConfigDedicated<MyObjectBuilder_SessionSettings>) MySandboxGame.ConfigDedicated);
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.InstancePath, "Saves"));
foreach (var f in worldFolders)
{
try
{
if (!string.IsNullOrEmpty(f) && File.Exists(Path.Combine(f, "Sandbox.sbc")))
{
var worldViewModel = new WorldViewModel(f, DedicatedConfig.LoadWorld == f);
DedicatedConfig.Worlds.Add(worldViewModel);
}
}
catch (Exception ex)
{
Log.Error("Failed to load world at path: " + f);
continue;
}
}
if (DedicatedConfig.Worlds.Count == 0)
{
Log.Warn($"No worlds found in the current instance {path}.");
return;
}
SelectWorld(DedicatedConfig.LoadWorld ?? DedicatedConfig.Worlds.First().WorldPath, false);
InstanceLoaded?.Invoke(DedicatedConfig);
}
public void SelectCreatedWorld(string worldPath)
{
WorldViewModel worldViewModel;
try
{
worldViewModel = new(worldPath);
DedicatedConfig.Worlds.Add(worldViewModel);
}
catch (Exception ex)
{
Log.Error(ex, "Failed to load world at path: " + worldPath);
return;
}
SelectWorld(worldViewModel, false);
}
public void SelectWorld(string worldPath, bool modsOnly = true)
{
DedicatedConfig.LoadWorld = worldPath;
var worldInfo = DedicatedConfig.Worlds.FirstOrDefault(x => x.WorldPath == worldPath);
try
{
if (worldInfo?.Checkpoint == null)
{
worldInfo = new WorldViewModel(worldPath);
DedicatedConfig.Worlds.Add(worldInfo);
}
}
catch (Exception ex)
{
Log.Error("Failed to load world at path: " + worldPath);
DedicatedConfig.LoadWorld = null;
return;
}
DedicatedConfig.SelectedWorld = worldInfo;
if (DedicatedConfig.SelectedWorld?.Checkpoint != null)
{
DedicatedConfig.Mods.Clear();
//remove the Torch mod to avoid running multiple copies of it
DedicatedConfig.SelectedWorld.WorldConfiguration.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID);
foreach (var m in DedicatedConfig.SelectedWorld.WorldConfiguration.Mods)
DedicatedConfig.Mods.Add(new ModItemInfo(m));
Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync());
}
}
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.WorldConfiguration.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID);
foreach (var m in DedicatedConfig.SelectedWorld.WorldConfiguration.Mods)
DedicatedConfig.Mods.Add(new ModItemInfo(m));
Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync());
}
}
public void ImportSelectedWorldConfig()
{
ImportWorldConfig(DedicatedConfig.SelectedWorld, false);
}
private void ImportWorldConfig(WorldViewModel world, bool modsOnly = true)
{
var mods = new MtObservableList<ModItemInfo>();
foreach (var mod in world.WorldConfiguration.Mods)
mods.Add(new ModItemInfo(mod));
DedicatedConfig.Mods = mods;
Log.Debug("Loaded mod list from world");
if (!modsOnly)
DedicatedConfig.SessionSettings = world.WorldConfiguration.Settings;
}
private void ImportWorldConfig(bool modsOnly = true)
{
if (string.IsNullOrEmpty(DedicatedConfig.LoadWorld))
return;
var sandboxPath = Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc");
if (!File.Exists(sandboxPath))
return;
try
{
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 {Torch.Config.InstancePath})");
return;
}
var mods = new MtObservableList<ModItemInfo>();
foreach (var mod in checkpoint.Mods)
mods.Add(new ModItemInfo(mod));
DedicatedConfig.Mods = mods;
Log.Debug("Loaded mod list from world");
if (!modsOnly)
DedicatedConfig.SessionSettings = new SessionSettingsViewModel(checkpoint.Settings);
}
catch (Exception e)
{
Log.Error($"Error loading mod list from world, verify that your mod list is accurate. '{DedicatedConfig.LoadWorld}'.");
Log.Error(e);
}
}
public void SaveConfig()
{
if (!((TorchServer)Torch).HasRun)
{
DedicatedConfig.Save(Path.Combine(Torch.InstancePath, CONFIG_NAME));
Log.Info("Saved dedicated config.");
}
try
{
var world = DedicatedConfig.Worlds.FirstOrDefault(x => x.WorldPath == DedicatedConfig.LoadWorld) ?? new WorldViewModel(DedicatedConfig.LoadWorld);
world.Checkpoint.SessionName = DedicatedConfig.WorldName;
world.WorldConfiguration.Settings = DedicatedConfig.SessionSettings;
world.WorldConfiguration.Mods.Clear();
foreach (var mod in DedicatedConfig.Mods)
{
var savedMod = ModItemUtils.Create(mod.PublishedFileId);
savedMod.IsDependency = mod.IsDependency;
savedMod.Name = mod.Name;
savedMod.FriendlyName = mod.FriendlyName;
world.WorldConfiguration.Mods.Add(savedMod);
}
Task.Run(() => DedicatedConfig.UpdateAllModInfosAsync());
world.SaveSandbox();
Log.Info("Saved world config.");
}
catch (Exception e)
{
Log.Error("Failed to write sandbox config");
Log.Error(e);
}
}
/// <summary>
/// Ensures that the given path is a valid server instance.
/// </summary>
private void ValidateInstance(string path)
{
Directory.CreateDirectory(Path.Combine(path, "Saves"));
Directory.CreateDirectory(Path.Combine(path, "Mods"));
var configPath = Path.Combine(path, CONFIG_NAME);
if (File.Exists(configPath))
return;
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
config.Save(configPath);
}
}
public class WorldViewModel : ViewModel, IWorld
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public string FolderName { get; set; }
public string WorldPath { get; }
public MyObjectBuilder_SessionSettings KeenSessionSettings => WorldConfiguration.Settings;
public MyObjectBuilder_Checkpoint KeenCheckpoint => Checkpoint;
public long WorldSizeKB { get; }
private string _checkpointPath;
private string _worldConfigPath;
private CheckpointViewModel _checkpoint;
public CheckpointViewModel Checkpoint
{
get
{
if (_checkpoint is null) LoadSandbox();
return _checkpoint;
}
private set => _checkpoint = value;
}
public WorldConfigurationViewModel WorldConfiguration { get; private set; }
public WorldViewModel(string worldPath, bool loadFiles = true)
{
try
{
WorldPath = worldPath;
WorldSizeKB = new DirectoryInfo(worldPath).GetFiles().Sum(x => x.Length) / 1024;
_checkpointPath = Path.Combine(WorldPath, "Sandbox.sbc");
_worldConfigPath = Path.Combine(WorldPath, "Sandbox_config.sbc");
FolderName = Path.GetFileName(worldPath);
if (loadFiles)
LoadSandbox();
}
catch (ArgumentException ex)
{
Log.Error($"World view model failed to load the path: {worldPath} Please ensure this is a valid path.");
throw; //rethrow to be handled further up the stack
}
}
public void SaveSandbox()
{
using (var f = File.Open(_checkpointPath, FileMode.Create))
MyObjectBuilderSerializer.SerializeXML(f, Checkpoint);
using (var f = File.Open(_worldConfigPath, FileMode.Create))
MyObjectBuilderSerializer.SerializeXML(f, WorldConfiguration);
}
public void LoadSandbox()
{
if (!MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint))
throw new SerializationException("Error reading checkpoint, see keen log for details");
Checkpoint = new CheckpointViewModel(checkpoint);
// migrate old saves
if (File.Exists(_worldConfigPath))
{
if (!MyObjectBuilderSerializer.DeserializeXML(_worldConfigPath, out MyObjectBuilder_WorldConfiguration worldConfig))
throw new SerializationException("Error reading settings, see keen log for details");
WorldConfiguration = new WorldConfigurationViewModel(worldConfig);
}
else
{
WorldConfiguration = new WorldConfigurationViewModel(new MyObjectBuilder_WorldConfiguration
{
Mods = checkpoint.Mods,
Settings = checkpoint.Settings
});
checkpoint.Mods = null;
checkpoint.Settings = null;
}
}
}
}

View File

@@ -0,0 +1,373 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using NLog;
using NLog.Fluent;
using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Game.Gui;
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;
namespace Torch.Server.Managers
{
public class MultiplayerManagerDedicated : MultiplayerManagerBase, IMultiplayerManagerServer
{
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_waitingForGroup")]
private static Func<MyDedicatedServerBase, HashSet<ulong>> _waitingForGroup;
#pragma warning restore 649
/// <inheritdoc />
public IReadOnlyList<ulong> BannedPlayers => MySandboxGame.ConfigDedicated.Banned;
private Dictionary<ulong, ulong> _gameOwnerIds = new Dictionary<ulong, ulong>();
[Dependency]
private InstanceManager _instanceManager;
/// <inheritdoc />
public MultiplayerManagerDedicated(ITorchBase torch) : base(torch)
{
}
/// <inheritdoc />
public void KickPlayer(ulong steamId) => Torch.Invoke(() => MyMultiplayer.Static.KickClient(steamId));
/// <inheritdoc />
public void BanPlayer(ulong steamId, bool banned = true)
{
Torch.Invoke(() =>
{
MyMultiplayer.Static.BanClient(steamId, banned);
});
}
/// <inheritdoc />
public void PromoteUser(ulong steamId)
{
Torch.Invoke(() =>
{
var p = MySession.Static.GetUserPromoteLevel(steamId);
if (p < MyPromoteLevel.Admin) //cannot promote to owner by design
//MySession.Static.SetUserPromoteLevel(steamId, p + 1);
MyGuiScreenPlayers.PromoteImplementation(steamId, true);
});
}
/// <inheritdoc />
public void DemoteUser(ulong steamId)
{
Torch.Invoke(() =>
{
var p = MySession.Static.GetUserPromoteLevel(steamId);
if (p > MyPromoteLevel.None && p < MyPromoteLevel.Owner) //owner cannot be demoted by design
//MySession.Static.SetUserPromoteLevel(steamId, p - 1);
MyGuiScreenPlayers.PromoteImplementation(steamId, false);
});
}
/// <inheritdoc />
public MyPromoteLevel GetUserPromoteLevel(ulong steamId)
{
return MySession.Static.GetUserPromoteLevel(steamId);
}
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);
public bool IsProfiling(ulong steamId) => _Profiling.Invoke((MyDedicatedServerBase)MyMultiplayer.Static, steamId);
/// <inheritdoc />
public event Action<ulong> PlayerKicked;
/// <inheritdoc />
public event Action<ulong, bool> PlayerBanned;
/// <inheritdoc />
public event Action<ulong, MyPromoteLevel> PlayerPromoted;
internal void RaisePromoteChanged(ulong steamId, MyPromoteLevel level)
{
PlayerPromoted?.Invoke(steamId, level);
}
/// <inheritdoc/>
public override void Attach()
{
base.Attach();
if (Torch.Config.UgcServiceType == UGCServiceType.Steam)
{
_gameServerValidateAuthTicketReplacer = _gameServerValidateAuthTicketFactory.Invoke();
_gameServerUserGroupStatusReplacer = _gameServerUserGroupStatusFactory.Invoke();
}
else
{
_gameServerValidateAuthTicketReplacer = _eosServerValidateAuthTicketFactory.Invoke();
_gameServerUserGroupStatusReplacer = _eosServerUserGroupStatusFactory.Invoke();
}
_gameServerValidateAuthTicketReplacer.Replace(
new Action<ulong, JoinResult, ulong, string>(ValidateAuthTicketResponse), MyGameService.GameServer);
_gameServerUserGroupStatusReplacer.Replace(new Action<ulong, ulong, bool, bool>(UserGroupStatusResponse),
MyGameService.GameServer);
_log.Info("Inserted authentication intercept");
}
/// <inheritdoc/>
public override void Detach()
{
if (_gameServerValidateAuthTicketReplacer != null && _gameServerValidateAuthTicketReplacer.Replaced)
_gameServerValidateAuthTicketReplacer.Restore(MyGameService.GameServer);
if (_gameServerUserGroupStatusReplacer != null && _gameServerUserGroupStatusReplacer.Replaced)
_gameServerUserGroupStatusReplacer.Restore(MyGameService.GameServer);
_log.Info("Removed authentication intercept");
base.Detach();
}
#pragma warning disable 649
[ReflectedEventReplace("VRage.Steam.MySteamGameServer, VRage.Steam", "ValidateAuthTicketResponse",
typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
private static Func<ReflectedEventReplacer> _gameServerValidateAuthTicketFactory;
[ReflectedEventReplace("VRage.Steam.MySteamGameServer, VRage.Steam", "UserGroupStatusResponse",
typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
private static Func<ReflectedEventReplacer> _gameServerUserGroupStatusFactory;
[ReflectedEventReplace("VRage.EOS.MyEOSGameServer, VRage.EOS", "ValidateAuthTicketResponse",
typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
private static Func<ReflectedEventReplacer> _eosServerValidateAuthTicketFactory;
[ReflectedEventReplace("VRage.EOS.MyEOSGameServer, VRage.EOS", "UserGroupStatusResponse",
typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
private static Func<ReflectedEventReplacer> _eosServerUserGroupStatusFactory;
private ReflectedEventReplacer _gameServerValidateAuthTicketReplacer;
private ReflectedEventReplacer _gameServerUserGroupStatusReplacer;
#pragma warning restore 649
#region CustomAuth
#pragma warning disable 649
[ReflectedStaticMethod(Type = typeof(MyDedicatedServerBase), Name = "ConvertSteamIDFrom64")]
private static Func<ulong, string> _convertSteamIDFrom64;
[ReflectedStaticMethod(Type = typeof(MyGameService), Name = "GetServerAccountType")]
private static Func<ulong, MyGameServiceAccountType> _getServerAccountType;
[ReflectedMethod(Name = "ClientIsProfiling")]
private static Func<MyDedicatedServerBase, ulong, bool> _Profiling;
[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 = "RaiseClientKicked")]
private static Action<MyMultiplayerBase, ulong> _raiseClientKicked;
#pragma warning restore 649
private const int _waitListSize = 32;
private readonly List<WaitingForGroup> _waitingForGroupLocal = new List<WaitingForGroup>(_waitListSize);
private struct WaitingForGroup
{
public readonly ulong SteamId;
public readonly JoinResult Response;
public readonly ulong SteamOwner;
public WaitingForGroup(ulong id, JoinResult response, ulong owner)
{
SteamId = id;
Response = response;
SteamOwner = owner;
}
}
//Largely copied from SE
private void ValidateAuthTicketResponse(ulong steamId, JoinResult response, ulong steamOwner, string serviceName)
{
var state = new MyP2PSessionState();
Sandbox.Engine.Networking.MyGameService.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 (IsProfiling(steamId))
{
_log.Warn($"Rejecting user {steamId} for using Profiler/ModSDK!");
UserRejected(steamId, JoinResult.ProfilingNotAllowed);
}
else 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))
lock (_waitingForGroupLocal)
{
if (_waitingForGroupLocal.Count >= _waitListSize)
_waitingForGroupLocal.RemoveAt(0);
_waitingForGroupLocal.Add(new WaitingForGroup(steamId, response, steamOwner));
}
else
UserRejected(steamId, JoinResult.SteamServersOffline);
}
private void RunEvent(ValidateAuthTicketEvent info)
{
JoinResult internalAuth;
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)
{
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)))
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
{
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) =>
{
JoinResult verdict;
if (task.IsFaulted)
{
_log.Error(task.Exception, $"Future validation verdict faulted");
verdict = JoinResult.TicketCanceled;
}
else if (Players.ContainsKey(info.SteamID))
{
_log.Warn($"Player {info.SteamID} has already joined!");
verdict = JoinResult.AlreadyJoined;
}
else
verdict = task.Result;
Torch.Invoke(() => { CommitVerdict(info.SteamID, verdict); });
});
}
private void CommitVerdict(ulong steamId, JoinResult verdict)
{
if (verdict == JoinResult.OK)
UserAccepted(steamId);
else
UserRejected(steamId, verdict);
}
private void UserGroupStatusResponse(ulong userId, ulong groupId, bool member, bool officer)
{
lock (_waitingForGroupLocal)
for (var j = 0; j < _waitingForGroupLocal.Count; j++)
{
var wait = _waitingForGroupLocal[j];
if (wait.SteamId == userId)
{
RunEvent(new ValidateAuthTicketEvent(wait.SteamId, wait.SteamOwner, wait.Response, groupId,
member, officer));
_waitingForGroupLocal.RemoveAt(j);
break;
}
}
}
private void UserRejected(ulong steamId, JoinResult reason)
{
_userRejected.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId, reason);
}
private void UserAccepted(ulong steamId)
{
_userAcceptedImpl.Invoke((MyDedicatedServerBase) MyMultiplayer.Static, steamId);
base.RaiseClientJoined(steamId);
}
#endregion
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;
using Sandbox;
using Torch.API.Event;
using Torch.Event;
using VRage.Network;
namespace Torch.Server.Managers
{
[EventShim]
internal static class MultiplayerManagerDedicatedEventShim
{
private static readonly EventList<ValidateAuthTicketEvent> _eventValidateAuthTicket =
new EventList<ValidateAuthTicketEvent>();
internal static void RaiseValidateAuthTicket(ref ValidateAuthTicketEvent info)
{
_eventValidateAuthTicket?.RaiseEvent(ref info);
}
}
/// <summary>
/// Event that occurs when a player tries to connect to a dedicated server.
/// Use these values to choose a <see cref="ValidateAuthTicketEvent.FutureVerdict"/>,
/// or leave it unset to allow the default logic to handle the request.
/// </summary>
public struct ValidateAuthTicketEvent : IEvent
{
/// <summary>
/// SteamID of the player
/// </summary>
public readonly ulong SteamID;
/// <summary>
/// SteamID of the game owner
/// </summary>
public readonly ulong SteamOwner;
/// <summary>
/// The response from steam
/// </summary>
public readonly JoinResult SteamResponse;
/// <summary>
/// ID of the queried group, or <c>0</c> if no group.
/// </summary>
public readonly ulong Group;
/// <summary>
/// Is this person a member of <see cref="Group"/>. If no group this is true.
/// </summary>
public readonly bool Member;
/// <summary>
/// Is this person an officer of <see cref="Group"/>. If no group this is false.
/// </summary>
public readonly bool Officer;
/// <summary>
/// A future verdict on this authorization request. If null, let the default logic choose. If not async use <see cref="Task.FromResult{TResult}(TResult)"/>
/// </summary>
public Task<JoinResult> FutureVerdict;
internal ValidateAuthTicketEvent(ulong steamId, ulong steamOwner, JoinResult steamResponse,
ulong serverGroup, bool member, bool officer)
{
SteamID = steamId;
SteamOwner = steamOwner;
SteamResponse = steamResponse;
Group = serverGroup;
Member = member;
Officer = officer;
FutureVerdict = null;
}
/// <inheritdoc/>
public bool Cancelled => FutureVerdict != null;
}
}

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,40 @@
using NLog;
using Sandbox;
using Torch.API;
using Torch.Managers;
using VRage.Dedicated.RemoteAPI;
namespace Torch.Server.Managers
{
public class RemoteAPIManager : Manager
{
/// <inheritdoc />
public RemoteAPIManager(ITorchBase torchInstance) : base(torchInstance)
{
}
/// <inheritdoc />
public override void Attach()
{
Torch.GameStateChanged += TorchOnGameStateChanged;
base.Attach();
}
/// <inheritdoc />
public override void Detach()
{
Torch.GameStateChanged -= TorchOnGameStateChanged;
base.Detach();
}
private void TorchOnGameStateChanged(MySandboxGame game, TorchGameState newstate)
{
if (newstate == TorchGameState.Loading && MySandboxGame.ConfigDedicated.RemoteApiEnabled && !string.IsNullOrEmpty(MySandboxGame.ConfigDedicated.RemoteSecurityKey))
{
var myRemoteServer = new MyRemoteServer(MySandboxGame.ConfigDedicated.RemoteApiPort, MySandboxGame.ConfigDedicated.RemoteSecurityKey);
LogManager.GetCurrentClassLogger().Info($"Remote API started on port {myRemoteServer.Port}");
}
}
}
}

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

@@ -0,0 +1,39 @@
using System.Reflection;
using NLog;
using Sandbox.Engine.Networking;
using Torch.API.Managers;
using Torch.Managers.PatchManager;
using Torch.Server.Managers;
using Torch.Utils;
using VRage.Game;
namespace Torch.Patches;
[PatchShim]
public static class CheckpointLoadPatch
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
[ReflectedMethodInfo(typeof(MyLocalCache), "LoadCheckpoint")]
private static MethodInfo LoadCheckpointMethod = null!;
public static void Patch(PatchContext context)
{
context.GetPattern(LoadCheckpointMethod).AddPrefix();
}
private static bool Prefix(ref MyObjectBuilder_Checkpoint __result)
{
#pragma warning disable CS0618
var world = TorchBase.Instance.Managers.GetManager<InstanceManager>().DedicatedConfig.SelectedWorld;
#pragma warning restore CS0618
if (world is null)
{
Log.Error("Selected world is null");
return false;
}
__result = world.Checkpoint;
return false;
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using NLog;
using Sandbox.Game.World;
using Torch.Managers.PatchManager;
using VRage.Game.ModAPI;
using Torch.API.Managers;
using Torch.Server.Managers;
namespace Torch.Patches
{
[PatchShim]
internal static class PromotePatch
{
private static Logger _log = LogManager.GetCurrentClassLogger();
private static IMultiplayerManagerServer _backing;
private static IMultiplayerManagerServer ServerManager => _backing ?? (_backing = TorchBase.Instance?.CurrentSession?.Managers.GetManager<IMultiplayerManagerServer>());
public static void Patch(PatchContext ctx)
{
_log.Info("patching promote");
ctx.GetPattern(typeof(MySession).GetMethod("OnPromoteLevelSet", BindingFlags.NonPublic | BindingFlags.Static)).Prefixes.Add(typeof(PromotePatch).GetMethod(nameof(PromotePrefix)));
}
public static void PromotePrefix(ulong steamId, MyPromoteLevel level)
{
if (ServerManager is MultiplayerManagerDedicated d)
d.RaisePromoteChanged(steamId, level);
else
throw new NotSupportedException();
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using NLog;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.World;
using Torch.API.Managers;
using Torch.Managers.PatchManager;
using Torch.Managers.PatchManager.MSIL;
using Torch.Server.Managers;
using VRage.Game.ModAPI;
namespace Torch.Patches
{
[PatchShim]
public static class ServerResponsePatch
{
private static Logger _log = LogManager.GetCurrentClassLogger();
public static void Patch(PatchContext ctx)
{
var transpiler = typeof(ServerResponsePatch).GetMethod(nameof(Transpile), BindingFlags.Public | BindingFlags.Static);
ctx.GetPattern(typeof(MyDedicatedServerBase).GetMethod("Initialize", BindingFlags.NonPublic | BindingFlags.Instance))
.Transpilers.Add(transpiler);
_log.Info("Patching Steam response polling");
}
public static IEnumerable<MsilInstruction> Transpile(IEnumerable<MsilInstruction> instructions)
{
// Reduce response timeout from 100 seconds to 5 seconds.
foreach (var instruction in instructions)
{
if (instruction.OpCode == OpCodes.Ldc_I4 && instruction.Operand is MsilOperandInline.MsilOperandInt32 inlineI32 && inlineI32.Value == 1000)
{
_log.Info("Patching Steam response timeout to 5 seconds");
inlineI32.Value = 50;
}
yield return instruction;
}
}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using NLog;
using Sandbox;
using Torch.Managers.PatchManager;
using Torch.Managers.PatchManager.MSIL;
using Torch.Utils;
namespace Torch.Patches
{
/// <summary>
/// Patches MySandboxGame.InitQuickLaunch to rethrow exceptions caught during session load.
/// </summary>
[PatchShim]
public static class WorldLoadExceptionPatch
{
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
[ReflectedMethodInfo(typeof(MySandboxGame), "InitQuickLaunch")]
private static MethodInfo _quickLaunchMethod = null!;
public static void Patch(PatchContext ctx)
{
ctx.GetPattern(_quickLaunchMethod).AddTranspiler(nameof(Transpile));
}
private static IEnumerable<MsilInstruction> Transpile(IEnumerable<MsilInstruction> method)
{
var msil = method.ToList();
for (var i = 0; i < msil.Count; i++)
{
var instruction = msil[i];
if (instruction.IsLocalStore() && instruction.Operand is MsilOperandInline.MsilOperandLocal {Value.Index: 19} operand)
{
msil.InsertRange(i + 1, new []
{
operand.Instruction.CopyWith(OpCodes.Ldloc_S),
new MsilInstruction(OpCodes.Call).InlineValue(new Action<Exception>(LogFatal).Method)
});
}
}
return msil;
}
private static void LogFatal(Exception e) => Log.Fatal(e.ToStringDemystified());
}
}

View File

@@ -1,312 +1,92 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
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 NLog;
using Sandbox.Game.World; using NLog.Config;
using Sandbox.ModAPI; using NLog.Targets;
using Torch; using Torch.Utils;
using Torch.API;
using Torch.Server.Views;
using VRage.Game.ModAPI;
using System.IO.Compression;
using System.Net;
using Torch.Server.Managers;
using VRage.FileSystem;
using VRageRender;
namespace Torch.Server namespace Torch.Server
{ {
internal static class Program internal static class Program
{ {
private static ITorchServer _server;
private static Logger _log = LogManager.GetLogger("Torch");
private static bool _restartOnCrash;
public static bool IsManualInstall;
private static TorchCli _cli;
/// <summary>
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
/// </summary>
[STAThread] [STAThread]
public static void Main(string[] args) public static void Main(string[] args)
{ {
var isService = Environment.GetEnvironmentVariable("TORCH_SERVICE")
?.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase) ?? false;
//Ensures that all the files are downloaded in the Torch directory. //Ensures that all the files are downloaded in the Torch directory.
Directory.SetCurrentDirectory(new FileInfo(typeof(Program).Assembly.Location).Directory.ToString()); var workingDir = AppContext.BaseDirectory;
var binDir = Path.Combine(Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir, "DedicatedServer64");
IsManualInstall = File.Exists("SpaceEngineersDedicated.exe"); Directory.SetCurrentDirectory(Environment.GetEnvironmentVariable("TORCH_GAME_PATH") ?? workingDir);
if (!IsManualInstall)
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; if (!isService && Directory.Exists(binDir))
foreach (var file in Directory.GetFiles(binDir, "System.*.dll"))
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
if (!Environment.UserInteractive)
{
using (var service = new TorchService())
{ {
ServiceBase.Run(service); File.Delete(file);
} }
// Breaks on Windows Server 2019
#if TORCH_SERVICE
if (!new ComputerInfo().OSFullName.Contains("Server 2019") && !Environment.UserInteractive)
{
using (var service = new TorchService(args))
ServiceBase.Run(service);
return; return;
} }
#endif
var configName = "TorchConfig.xml"; var instanceName = Environment.GetEnvironmentVariable("TORCH_INSTANCE") ?? "Instance";
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName); string instancePath;
TorchConfig options;
if (File.Exists(configName)) if (Path.IsPathRooted(instanceName))
{ {
_log.Info($"Loading config {configPath}"); instancePath = instanceName;
options = TorchConfig.LoadFrom(configPath); instanceName = Path.GetDirectoryName(instanceName);
} }
else else
{ {
_log.Info($"Generating default config at {configPath}"); instancePath = Directory.CreateDirectory(instanceName).FullName;
options = new TorchConfig(); }
var oldNlog = Path.Combine(workingDir, "NLog.config");
var newNlog = Path.Combine(instancePath, "NLog.config");
if (File.Exists(oldNlog))
File.Move(oldNlog, newNlog, true);
else if (!File.Exists(newNlog))
using (var f = File.Create(newNlog))
typeof(Program).Assembly.GetManifestResourceStream("Torch.Server.NLog.config")!.CopyTo(f);
if (!IsManualInstall) var oldTorchCfg = Path.Combine(workingDir, "Torch.cfg");
{ var torchCfg = Path.Combine(instancePath, "Torch.cfg");
//new ConfigManager().CreateInstance("Instance");
options.InstancePath = Path.GetFullPath("Instance"); if (File.Exists(oldTorchCfg))
File.Move(oldTorchCfg, torchCfg, true);
_log.Warn("Would you like to enable automatic updates? (Y/n):"); var config = Persistent<TorchConfig>.Load(torchCfg);
config.Data.InstanceName = instanceName;
var input = Console.ReadLine() ?? ""; config.Data.InstancePath = instancePath;
var autoUpdate = !input.Equals("n", StringComparison.InvariantCultureIgnoreCase); if (!config.Data.Parse(args))
options.AutomaticUpdates = autoUpdate; {
if (autoUpdate) Console.WriteLine("Invalid arguments");
{ Environment.Exit(1);
_log.Info("Automatic updates enabled, updating server.");
RunSteamCmd();
}
}
//var setupDialog = new FirstTimeSetup { DataContext = options };
//setupDialog.ShowDialog();
options.Save(configPath);
} }
_cli = new TorchCli { Config = options }; var handler = new UnhandledExceptionHandler(config.Data, isService);
if (!_cli.Parse(args)) AppDomain.CurrentDomain.UnhandledException += handler.OnUnhandledException;
return;
Target.Register<LogViewerTarget>(nameof(LogViewerTarget));
TorchLogManager.RegisterTargets(Environment.GetEnvironmentVariable("TORCH_LOG_EXTENSIONS_PATH") ??
Path.Combine(instancePath, "LoggingExtensions"));
TorchLogManager.SetConfiguration(new XmlLoggingConfiguration(newNlog));
_log.Debug(_cli.ToString()); var initializer = new Initializer(workingDir, config);
if (!initializer.Initialize(args))
Environment.Exit(1);
if (!string.IsNullOrEmpty(_cli.WaitForPID)) TorchLauncher.Launch(workingDir, binDir);
{
try initializer.Run(isService, instanceName, instancePath);
{
var pid = int.Parse(_cli.WaitForPID);
var waitProc = Process.GetProcessById(pid);
_log.Warn($"Waiting for process {pid} to exit.");
waitProc.WaitForExit();
}
catch
{
// ignored
}
}
_restartOnCrash = _cli.RestartOnCrash;
if (options.AutomaticUpdates || _cli.Update)
{
if (IsManualInstall)
_log.Warn("Detected manual install, won't attempt to update DS");
else
{
RunSteamCmd();
}
}
RunServer(options, _cli);
}
private const string STEAMCMD_DIR = "steamcmd";
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
quit";
public static void RunSteamCmd()
{
var log = LogManager.GetLogger("SteamCMD");
if (!Directory.Exists(STEAMCMD_DIR))
{
Directory.CreateDirectory(STEAMCMD_DIR);
}
if (!File.Exists(RUNSCRIPT_PATH))
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
if (!File.Exists(STEAMCMD_PATH))
{
try
{
log.Info("Downloading SteamCMD.");
using (var client = new WebClient())
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
File.Delete(STEAMCMD_ZIP);
log.Info("SteamCMD downloaded successfully!");
}
catch
{
log.Error("Failed to download SteamCMD, unable to update the DS.");
return;
}
}
log.Info("Checking for DS updates.");
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
{
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
UseShellExecute = false,
RedirectStandardOutput = true,
StandardOutputEncoding = Encoding.ASCII
};
var cmd = Process.Start(steamCmdProc);
// ReSharper disable once PossibleNullReferenceException
while (!cmd.HasExited)
{
log.Info(cmd.StandardOutput.ReadLine());
Thread.Sleep(100);
}
}
public static void RunServer(TorchConfig options, TorchCli cli)
{
/*
if (!parser.ParseArguments(args, options))
{
_log.Error($"Parsing arguments failed: {string.Join(" ", args)}");
return;
}
if (!string.IsNullOrEmpty(options.Config) && File.Exists(options.Config))
{
options = ServerConfig.LoadFrom(options.Config);
parser.ParseArguments(args, options);
}*/
//RestartOnCrash autostart autosave=15
//gamepath ="C:\Program Files\Space Engineers DS" instance="Hydro Survival" instancepath="C:\ProgramData\SpaceEngineersDedicated\Hydro Survival"
/*
if (options.InstallService)
{
var serviceName = $"\"Torch - {options.InstanceName}\"";
// Working on installing the service properly instead of with sc.exe
_log.Info($"Installing service '{serviceName}");
var exePath = $"\"{Assembly.GetExecutingAssembly().Location}\"";
var createInfo = new ServiceCreateInfo
{
Name = options.InstanceName,
BinaryPath = exePath,
};
_log.Info("Service Installed");
var runArgs = string.Join(" ", args.Skip(1));
_log.Info($"Installing Torch as a service with arguments '{runArgs}'");
var startInfo = new ProcessStartInfo
{
FileName = "sc.exe",
Arguments = $"create Torch binPath=\"{Assembly.GetExecutingAssembly().Location} {runArgs}\"",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas"
};
Process.Start(startInfo).WaitForExit();
_log.Info("Torch service installed");
return;
}
if (options.UninstallService)
{
_log.Info("Uninstalling Torch service");
var startInfo = new ProcessStartInfo
{
FileName = "sc.exe",
Arguments = "delete Torch",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas"
};
Process.Start(startInfo).WaitForExit();
_log.Info("Torch service uninstalled");
return;
}*/
_server = new TorchServer(options);
_server.Init();
if (cli.NoGui || cli.Autostart)
{
new Thread(() => _server.Start()).Start();
}
if (!cli.NoGui)
{
var ui = new TorchUI((TorchServer)_server);
ui.LoadConfig(options);
ui.ShowDialog();
}
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
var basePath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "DedicatedServer64");
string asmPath = Path.Combine(basePath, new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(asmPath))
return Assembly.LoadFrom(asmPath);
}
catch
{
// ignored
}
return null;
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
_log.Fatal(ex);
if (_restartOnCrash)
{
/* Throws an exception somehow and I'm too lazy to debug it.
try
{
if (MySession.Static != null && MySession.Static.AutoSaveInMinutes > 0)
MySession.Static.Save();
}
catch { }*/
var exe = typeof(Program).Assembly.Location;
_cli.WaitForPID = Process.GetCurrentProcess().Id.ToString();
Process.Start(exe, _cli.ToString());
}
//1627 = Function failed during execution.
Environment.Exit(1627);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,17 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.169.376")] [assembly: AssemblyTitle("Torch Server")]
[assembly: AssemblyFileVersion("1.0.169.376")] [assembly: AssemblyDescription("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Torch")]
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif

View File

@@ -1,16 +0,0 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System.Reflection;
<# var dt = DateTime.Now;
int major = 1;
int minor = 0;
int build = dt.DayOfYear;
int rev = (int)dt.TimeOfDay.TotalMinutes / 2;
#>
[assembly: AssemblyVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]

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