Compare commits
302 Commits
v1.0.0-alp
...
v1.3.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
814a9def7f | ||
![]() |
276a4522d6 | ||
![]() |
8b7a07ffe7 | ||
![]() |
f990d27851 | ||
![]() |
3d8bf78213 | ||
![]() |
90479dfea2 | ||
![]() |
844d4be96a | ||
![]() |
9f610d5ae8 | ||
![]() |
c9adb2a212 | ||
![]() |
7fd814d595 | ||
![]() |
e7065a7159 | ||
![]() |
8625db7ae4 | ||
![]() |
68e6774e26 | ||
![]() |
a7c6ae7382 | ||
![]() |
9c06049628 | ||
![]() |
34e5f4df49 | ||
![]() |
b7b58f5870 | ||
![]() |
f9d75856d1 | ||
![]() |
66b7adf485 | ||
![]() |
76637b130c | ||
![]() |
9a1a31c424 | ||
![]() |
faef000245 | ||
![]() |
c2035668cd | ||
![]() |
f1201c6259 | ||
![]() |
bdaa674662 | ||
![]() |
3b17eb4750 | ||
![]() |
9221d412ca | ||
![]() |
65bb71aabf | ||
![]() |
dcd0fa86b9 | ||
![]() |
72be1b8dbf | ||
![]() |
1d7b642c50 | ||
![]() |
f7d45ca338 | ||
![]() |
831722dd84 | ||
![]() |
ee3dd0b5bd | ||
![]() |
7480847677 | ||
![]() |
0ff715af1b | ||
a318aa87cf | |||
![]() |
74b00d3ab1 | ||
![]() |
355375e9db | ||
![]() |
016203d2bc | ||
![]() |
b65efa2968 | ||
![]() |
4fc5f10bad | ||
![]() |
f8e9d68ceb | ||
![]() |
65e8d62391 | ||
![]() |
93fa82201a | ||
![]() |
71930182dd | ||
![]() |
8764540d3b | ||
![]() |
6a6676c1cb | ||
![]() |
5bf91f1891 | ||
![]() |
14e16a959f | ||
![]() |
3b72724966 | ||
![]() |
9a0e7809cd | ||
![]() |
94c25a70b3 | ||
![]() |
4901120be4 | ||
![]() |
21e45b5e45 | ||
![]() |
b829e90edb | ||
![]() |
fbf7fa6176 | ||
![]() |
2b413ef609 | ||
![]() |
0f06ee5688 | ||
![]() |
ec065ec329 | ||
![]() |
c9a5472282 | ||
![]() |
b12199c65b | ||
![]() |
b4ac097910 | ||
![]() |
aae4ec97a9 | ||
![]() |
45d931b351 | ||
![]() |
f53c9660fe | ||
![]() |
c889854818 | ||
![]() |
b8b0a0fcce | ||
![]() |
b3f9d7e5c7 | ||
![]() |
94b457d9c0 | ||
![]() |
0c58655708 | ||
![]() |
ba98e0a15a | ||
![]() |
5496ad1198 | ||
![]() |
7bad6149b5 | ||
![]() |
378905268d | ||
![]() |
f56a700fea | ||
![]() |
736176ce27 | ||
![]() |
96d749c512 | ||
![]() |
17514c89ad | ||
![]() |
8b08f2b747 | ||
![]() |
07bb0fc4cf | ||
![]() |
dbea9d83f4 | ||
![]() |
8989ae94a7 | ||
![]() |
4db83e6f65 | ||
![]() |
9286f2e559 | ||
![]() |
045a572058 | ||
![]() |
f68be8e4c9 | ||
![]() |
ec4572c390 | ||
![]() |
b89b61496b | ||
![]() |
63f504feb7 | ||
![]() |
06eca83ff9 | ||
![]() |
ebc8b7a7fd | ||
![]() |
4c34a653bd | ||
![]() |
38d2f1b62e | ||
![]() |
d8915d1893 | ||
![]() |
873acfcb4f | ||
![]() |
030df5029b | ||
![]() |
7404b6bd2d | ||
![]() |
b9e9be227a | ||
![]() |
f2537706e7 | ||
![]() |
a8251d9385 | ||
![]() |
b1edd62c0b | ||
![]() |
c1e315fa40 | ||
![]() |
bfcf96f1ad | ||
![]() |
d257e9e1e8 | ||
![]() |
85e307f8db | ||
![]() |
03fa0a73b6 | ||
![]() |
2f157a6438 | ||
![]() |
cdde72cbe0 | ||
![]() |
b18420ad55 | ||
![]() |
d92daccdbf | ||
![]() |
6b3cc6c421 | ||
![]() |
18af85c4d7 | ||
![]() |
6c6ff18ec3 | ||
![]() |
67e663f023 | ||
![]() |
3f717b304a | ||
![]() |
300af03012 | ||
![]() |
3d8d333f10 | ||
![]() |
d87cc7f1e7 | ||
![]() |
d59ef20f72 | ||
![]() |
b2bf0229ed | ||
![]() |
f03bfd2d7a | ||
![]() |
3dd646d6e9 | ||
![]() |
58ad553b39 | ||
![]() |
83056bacf4 | ||
![]() |
2751eaf399 | ||
![]() |
869ba0d33c | ||
![]() |
aeb29d9a69 | ||
![]() |
979d5914a9 | ||
![]() |
e72f5b7c37 | ||
![]() |
42d3324fc1 | ||
![]() |
e242ed6f1f | ||
![]() |
a2acb9c11c | ||
![]() |
444da941c9 | ||
![]() |
f19fd84f1d | ||
![]() |
b5793d36a8 | ||
![]() |
a71c03124b | ||
![]() |
66c484796d | ||
![]() |
a4927030d7 | ||
![]() |
c32badb750 | ||
![]() |
356eb849f2 | ||
![]() |
04e949ed0c | ||
![]() |
13dc8622c9 | ||
![]() |
a2b9c4724d | ||
![]() |
f326e569a1 | ||
![]() |
c1961dee5f | ||
![]() |
e42a231553 | ||
![]() |
b3d9a64632 | ||
![]() |
47c7c37fa9 | ||
![]() |
17413f81ff | ||
![]() |
725e555733 | ||
![]() |
6e7456605d | ||
![]() |
b652181dda | ||
![]() |
6764d80534 | ||
![]() |
6fbc06081e | ||
![]() |
0328876d50 | ||
![]() |
c5e1dd7c3a | ||
![]() |
714824df97 | ||
![]() |
2cb921087f | ||
![]() |
1ed3144428 | ||
![]() |
ba8fa01ce5 | ||
![]() |
3f6f077833 | ||
![]() |
74d9999202 | ||
![]() |
1be1c938cc | ||
![]() |
930f1d43e0 | ||
![]() |
1e04053026 | ||
![]() |
1e6b3faff8 | ||
![]() |
e6928b6ab1 | ||
![]() |
eb97d0d479 | ||
![]() |
0a75d57cf9 | ||
![]() |
383c9b9a33 | ||
![]() |
d2adbecc44 | ||
![]() |
834395bdc3 | ||
![]() |
18dad5bedf | ||
![]() |
c188367749 | ||
![]() |
5b098c68aa | ||
![]() |
22bd56652d | ||
![]() |
d07caea0f6 | ||
![]() |
897f75c069 | ||
![]() |
8167e04383 | ||
![]() |
72b6d0e7bb | ||
![]() |
039c5d9244 | ||
![]() |
7ea982c903 | ||
![]() |
f0adeddb66 | ||
![]() |
e709b6c321 | ||
![]() |
1b0dcc9808 | ||
![]() |
fe5dfa0ea7 | ||
![]() |
25e6f27854 | ||
![]() |
c07a01a427 | ||
![]() |
11bc7cb60c | ||
![]() |
6e3b7e7a04 | ||
![]() |
ff58cf5b19 | ||
![]() |
25a708a3d4 | ||
![]() |
004dcc19dc | ||
![]() |
ac95f5f89c | ||
![]() |
0fc9b49fba | ||
![]() |
600e73ad43 | ||
![]() |
0bc0b0dc77 | ||
![]() |
86f62e1f37 | ||
![]() |
7850b8368a | ||
![]() |
496bde733f | ||
![]() |
8b98deafca | ||
![]() |
b3ab0cbd74 | ||
![]() |
462eb77e0d | ||
![]() |
d5702d3065 | ||
![]() |
c8377b318e | ||
![]() |
6814a833be | ||
![]() |
98aae10126 | ||
![]() |
0558675132 | ||
![]() |
c8f42e8a48 | ||
![]() |
d30d16b855 | ||
![]() |
178957642c | ||
![]() |
cd77fe74d5 | ||
![]() |
90c91c3ebc | ||
![]() |
11dbf83faf | ||
![]() |
b7fa57c9b7 | ||
![]() |
7a63527d8f | ||
![]() |
4b33bedccd | ||
![]() |
473637ceaf | ||
![]() |
6f5142393b | ||
![]() |
fdc20d4e9d | ||
![]() |
4d0dcede41 | ||
![]() |
4ed262a330 | ||
![]() |
48f0f81f12 | ||
![]() |
7b9f2d680a | ||
![]() |
c69537b173 | ||
![]() |
794a4a23d3 | ||
![]() |
998ff6a13a | ||
![]() |
4ff4a60106 | ||
![]() |
eaaca5b003 | ||
![]() |
388b4731c7 | ||
![]() |
f285d67c87 | ||
![]() |
bc1a612a20 | ||
![]() |
b67879577d | ||
![]() |
2b5b9d44e6 | ||
![]() |
3e48638d8c | ||
![]() |
3307d2d23d | ||
![]() |
62d73cbf96 | ||
![]() |
2004f71290 | ||
![]() |
013dc43c2f | ||
![]() |
716e6cbc04 | ||
![]() |
9e81b6316f | ||
![]() |
d709bf68dd | ||
![]() |
c14b8ed23a | ||
![]() |
7ba6fb5a2e | ||
![]() |
4f1a03811a | ||
![]() |
0946d5a138 | ||
![]() |
6f650c8bbd | ||
![]() |
ad1502e998 | ||
![]() |
bb42dd026c | ||
![]() |
95b6c9dfe5 | ||
![]() |
9b1754a431 | ||
![]() |
0574d59e12 | ||
![]() |
4f7c35dfcf | ||
![]() |
967384ccfe | ||
![]() |
b906a32e23 | ||
![]() |
9b9a4c5ee1 | ||
![]() |
13f3e7ee11 | ||
![]() |
b7f2a62b3c | ||
![]() |
1f4197ce67 | ||
![]() |
f377d044d6 | ||
![]() |
205dd1a201 | ||
![]() |
9c505c4f5d | ||
![]() |
eb7f7f4244 | ||
![]() |
f1fc49d276 | ||
![]() |
d8e2072493 | ||
![]() |
96f813a17b | ||
![]() |
a97542e649 | ||
![]() |
5eceb21ec7 | ||
![]() |
a61b646295 | ||
![]() |
373c476d2d | ||
![]() |
b1145c8926 | ||
![]() |
0810e76474 | ||
![]() |
57acb274c6 | ||
![]() |
e57f885d3b | ||
![]() |
0073e101dd | ||
![]() |
b42d43c0e1 | ||
![]() |
9d8988a2ec | ||
![]() |
aa784c121b | ||
![]() |
a36e8a4065 | ||
![]() |
3fd7b66905 | ||
![]() |
44ebcc5d25 | ||
![]() |
9a68ed6bd0 | ||
![]() |
4f84cd8963 | ||
![]() |
837b56462f | ||
![]() |
9c3a22c556 | ||
![]() |
9471d83a45 | ||
![]() |
52b225d944 | ||
![]() |
c8f0a61209 | ||
![]() |
59c3e9eb54 | ||
![]() |
d20d68b831 | ||
![]() |
cfda1f8eef | ||
![]() |
2fb222125a | ||
![]() |
c7c3c00783 | ||
![]() |
dcc130e2cf | ||
![]() |
491f3d3af4 | ||
![]() |
140000df55 | ||
![]() |
4b2fee7614 | ||
![]() |
2c7b522378 | ||
![]() |
d9ef60d4e8 | ||
![]() |
178fcb8164 | ||
![]() |
64f123abe9 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,9 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
#Rider directory
|
||||
.idea/
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# Making a Pull Request
|
||||
* Fork this repository and make sure your local **master** branch is up to date with the main repository.
|
||||
* Create a new branch for your addition with an appropriate name, e.g. **add-restart-command**
|
||||
* 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.
|
||||
* Submit your branch as a PR to be reviewed, with Torch's **staging** branch as the base.
|
||||
|
||||
## Naming Conventions
|
||||
* Types: **PascalCase**
|
||||
|
@@ -16,7 +16,7 @@ try
|
||||
tag_name=$tagName
|
||||
name="Generated $tagName"
|
||||
body=""
|
||||
draft=$FALSE
|
||||
draft=$TRUE
|
||||
prerelease=$tagName.Contains("alpha") -or $tagName.Contains("beta")
|
||||
}
|
||||
Write-Output("Creating new release " + $tagName + "...")
|
||||
|
68
Jenkinsfile
vendored
68
Jenkinsfile
vendored
@@ -1,3 +1,18 @@
|
||||
def packageAndArchive(buildMode, packageName, exclude) {
|
||||
zipFile = "bin\\${packageName}.zip"
|
||||
packageDir = "bin\\${packageName}\\"
|
||||
|
||||
bat "IF EXIST ${zipFile} DEL ${zipFile}"
|
||||
bat "IF EXIST ${packageDir} RMDIR /S /Q ${packageDir}"
|
||||
|
||||
bat "xcopy bin\\x64\\${buildMode} ${packageDir}"
|
||||
if (exclude.length() > 0) {
|
||||
bat "del ${packageDir}${exclude}"
|
||||
}
|
||||
powershell "Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\"\$PWD\\${packageDir}\", \"\$PWD\\${zipFile}\")"
|
||||
archiveArtifacts artifacts: zipFile, caseSensitive: false, onlyIfSuccessful: true
|
||||
}
|
||||
|
||||
node {
|
||||
stage('Checkout') {
|
||||
checkout scm
|
||||
@@ -16,12 +31,30 @@ node {
|
||||
|
||||
stage('Build') {
|
||||
currentBuild.description = bat(returnStdout: true, script: '@powershell -File Versioning/version.ps1').trim()
|
||||
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=Release /p:Platform=x64"
|
||||
if (env.BRANCH_NAME == "master") {
|
||||
buildMode = "Release"
|
||||
} else {
|
||||
buildMode = "Debug"
|
||||
}
|
||||
bat "IF EXIST \"bin\" rmdir /Q /S \"bin\""
|
||||
bat "IF EXIST \"bin-test\" rmdir /Q /S \"bin-test\""
|
||||
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=${buildMode} /p:Platform=x64 /t:Clean"
|
||||
bat "\"${tool 'MSBuild'}msbuild\" Torch.sln /p:Configuration=${buildMode} /p:Platform=x64"
|
||||
}
|
||||
|
||||
stage('Archive') {
|
||||
archiveArtifacts artifacts: "bin/x64/${buildMode}/Torch*", caseSensitive: false, fingerprint: true, onlyIfSuccessful: true
|
||||
|
||||
packageAndArchive(buildMode, "torch-server", "Torch.Client*")
|
||||
|
||||
/*packageAndArchive(buildMode, "torch-client", "Torch.Server*")*/
|
||||
}
|
||||
|
||||
/* Disabled because they fail builds more often than they detect actual problems
|
||||
stage('Test') {
|
||||
bat 'IF NOT EXIST reports MKDIR reports'
|
||||
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/Release/Torch.Tests.dll\" \"bin-test/x64/Release/Torch.Server.Tests.dll\" \"bin-test/x64/Release/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
|
||||
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/${buildMode}/Torch.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Server.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
|
||||
|
||||
step([
|
||||
$class: 'XUnitBuilder',
|
||||
thresholdMode: 1,
|
||||
@@ -36,32 +69,5 @@ node {
|
||||
]]
|
||||
])
|
||||
}
|
||||
|
||||
stage('Archive') {
|
||||
bat '''IF EXIST bin\\torch-server.zip DEL bin\\torch-server.zip
|
||||
IF EXIST bin\\package-server RMDIR /S /Q bin\\package-server
|
||||
xcopy bin\\x64\\Release bin\\package-server\\
|
||||
del bin\\package-server\\Torch.Client*'''
|
||||
bat "powershell -Command \"Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\\\"\$PWD\\bin\\package-server\\\", \\\"\$PWD\\bin\\torch-server.zip\\\")\""
|
||||
archiveArtifacts artifacts: 'bin/torch-server.zip', caseSensitive: false, onlyIfSuccessful: true
|
||||
|
||||
bat '''IF EXIST bin\\torch-client.zip DEL bin\\torch-client.zip
|
||||
IF EXIST bin\\package-client RMDIR /S /Q bin\\package-client
|
||||
xcopy bin\\x64\\Release bin\\package-client\\
|
||||
del bin\\package-client\\Torch.Server*'''
|
||||
bat "powershell -Command \"Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\\\"\$PWD\\bin\\package-client\\\", \\\"\$PWD\\bin\\torch-client.zip\\\")\""
|
||||
archiveArtifacts artifacts: 'bin/torch-client.zip', caseSensitive: false, onlyIfSuccessful: true
|
||||
|
||||
archiveArtifacts artifacts: 'bin/x64/Release/Torch*', caseSensitive: false, fingerprint: true, onlyIfSuccessful: true
|
||||
}
|
||||
|
||||
gitVersion = bat(returnStdout: true, script: "@git describe --tags").trim()
|
||||
gitSimpleVersion = bat(returnStdout: true, script: "@git describe --tags --abbrev=0").trim()
|
||||
if (gitVersion == gitSimpleVersion) {
|
||||
stage('Release') {
|
||||
withCredentials([usernamePassword(credentialsId: 'e771beac-b3ee-4bc9-82b7-40a6d426d508', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
|
||||
powershell "./Jenkins/release.ps1 \"https://api.github.com/repos/TorchAPI/Torch/\" \"$gitSimpleVersion\" \"$USERNAME:$PASSWORD\" @(\"bin/torch-server.zip\", \"bin/torch-client.zip\")"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
20
NLog.config
20
NLog.config
@@ -1,15 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
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>
|
||||
<target xsi:type="File" name="main" layout="${time} [${level:uppercase=true}] ${logger}: ${message}" fileName="Logs\Torch-${shortdate}.log" />
|
||||
<targets async="true">
|
||||
<target xsi:type="Null" name="null" formatMessage="false" />
|
||||
<target xsi:type="File" name="keen" layout="${var:logStamp} ${logger}: ${var:logContent}" fileName="Logs\Keen-${shortdate}.log" />
|
||||
<target xsi:type="File" name="main" layout="${var:logStamp} ${logger}: ${var:logContent}" fileName="Logs\Torch-${shortdate}.log" />
|
||||
<target xsi:type="File" name="chat" layout="${longdate} ${message}" fileName="Logs\Chat.log" />
|
||||
<target xsi:type="ColoredConsole" name="console" layout="${time} [${level:uppercase=true}] ${logger}: ${message}" />
|
||||
<target xsi:type="ColoredConsole" name="console" layout="${var:logStamp} ${logger:shortName=true}: ${var:logContent}" />
|
||||
<target xsi:type="File" name="patch" layout="${var:logContent}" fileName="Logs\patch.log"/>
|
||||
<target xsi:type="FlowDocument" name="wpf" layout="${var:logStamp} ${logger:shortName=true}: ${var:logContent}" />
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Info" writeTo="main, 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>
|
||||
</nlog>
|
15
README.md
15
README.md
@@ -1,18 +1,20 @@
|
||||
[](https://discord.gg/8uHZykr) [](http://server.torchapi.net:8080/job/Torch/job/Torch/job/master/)
|
||||
[](https://discord.gg/8uHZykr) [](http://build.torchapi.net/job/Torch/job/Torch/job/master/)
|
||||
|
||||
# What is Torch?
|
||||
Torch is the successor to SE Server Extender and gives server admins the tools they need to keep their Space Engineers servers running smoothly. It features a user interface with live management tools and a plugin system so you can run your server exactly how you'd like. Torch is still in early development so there may be bugs and incomplete features.
|
||||
|
||||
# Features
|
||||
## Torch.Server
|
||||
|
||||
### Features
|
||||
* WPF-based user interface
|
||||
* 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
|
||||
* Organized, easy to use configuration editor
|
||||
* Extensible using the Torch plugin system
|
||||
|
||||
# Installation
|
||||
### Installation
|
||||
|
||||
* Get the latest Torch release here: https://github.com/TorchAPI/Torch/releases
|
||||
* Get the latest Torch release here: https://torchapi.net/download
|
||||
* Unzip the Torch release into its own directory and run the executable. It will automatically download the SE DS and generate the other necessary files.
|
||||
- If you already have a DS installed you can unzip the Torch files into the folder that contains the DedicatedServer64 folder.
|
||||
|
||||
@@ -21,10 +23,5 @@ To build Torch you must first have a complete SE Dedicated installation somewher
|
||||
|
||||
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)!
|
||||
|
28
Torch.API/Event/EventHandlerAttribute.cs
Normal file
28
Torch.API/Event/EventHandlerAttribute.cs
Normal 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
11
Torch.API/Event/IEvent.cs
Normal 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; }
|
||||
}
|
||||
}
|
9
Torch.API/Event/IEventHandler.cs
Normal file
9
Torch.API/Event/IEventHandler.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
28
Torch.API/Event/IEventManager.cs
Normal file
28
Torch.API/Event/IEventManager.cs
Normal 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);
|
||||
}
|
||||
}
|
@@ -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; }
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Torch.API.Managers;
|
||||
@@ -17,21 +18,25 @@ namespace Torch.API
|
||||
/// <summary>
|
||||
/// Fired when the session begins loading.
|
||||
/// </summary>
|
||||
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||
event Action SessionLoading;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the session finishes loading.
|
||||
/// </summary>
|
||||
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||
event Action SessionLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when the session begins unloading.
|
||||
/// </summary>
|
||||
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||
event Action SessionUnloading;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the session finishes unloading.
|
||||
/// </summary>
|
||||
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||
event Action SessionUnloaded;
|
||||
|
||||
/// <summary>
|
||||
@@ -44,10 +49,6 @@ namespace Torch.API
|
||||
/// </summary>
|
||||
ITorchConfig Config { get; }
|
||||
|
||||
/// <inheritdoc cref="IMultiplayerManager"/>
|
||||
[Obsolete]
|
||||
IMultiplayerManager Multiplayer { get; }
|
||||
|
||||
/// <inheritdoc cref="IPluginManager"/>
|
||||
[Obsolete]
|
||||
IPluginManager Plugins { get; }
|
||||
@@ -55,52 +56,83 @@ namespace Torch.API
|
||||
/// <inheritdoc cref="IDependencyManager"/>
|
||||
IDependencyManager Managers { get; }
|
||||
|
||||
[Obsolete("Prefer using Managers.GetManager for global managers")]
|
||||
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; }
|
||||
InformationalVersion TorchVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoke an action on the game thread.
|
||||
/// </summary>
|
||||
void Invoke(Action action);
|
||||
void Invoke(Action action, [CallerMemberName] string caller = "");
|
||||
|
||||
/// <summary>
|
||||
/// Invoke an action on the game thread and block until it has completed.
|
||||
/// If this is called on the game thread the action will execute immediately.
|
||||
/// </summary>
|
||||
void InvokeBlocking(Action action);
|
||||
/// <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);
|
||||
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
|
||||
|
||||
/// <summary>
|
||||
/// Start the Torch instance.
|
||||
/// 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>
|
||||
/// Stop the Torch instance.
|
||||
/// Signals the torch instance to stop, then blocks until it's stopped.
|
||||
/// </summary>
|
||||
void Stop();
|
||||
|
||||
/// <summary>
|
||||
/// Restart the Torch instance.
|
||||
/// Restart the Torch instance, blocking until the restart has been performed.
|
||||
/// </summary>
|
||||
void Restart();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a save of the game.
|
||||
/// </summary>
|
||||
/// <param name="callerId">Id of the player who initiated the save.</param>
|
||||
Task Save(long callerId);
|
||||
/// <param name="timeoutMs">timeout before the save is treated as failed, or -1 for no timeout</param>
|
||||
/// <param name="exclusive">Only start saving if we aren't already saving</param>
|
||||
/// <returns>Future result of the save, or null if one is in progress and in exclusive mode</returns>
|
||||
Task<GameSaveResult> Save(int timeoutMs = -1, bool exclusive = false);
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the Torch instance.
|
||||
/// 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>
|
||||
@@ -108,10 +140,20 @@ namespace Torch.API
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the server's Init() method has completed.
|
||||
/// </summary>
|
||||
event Action<ITorchServer> Initialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,6 +161,6 @@ namespace Torch.API
|
||||
/// </summary>
|
||||
public interface ITorchClient : ITorchBase
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
52
Torch.API/InformationalVersion.cs
Normal file
52
Torch.API/InformationalVersion.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Version in the form v#.#.#.#-info
|
||||
/// </summary>
|
||||
public class InformationalVersion
|
||||
{
|
||||
public Version Version { get; set; }
|
||||
public string[] Information { get; set; }
|
||||
|
||||
public static bool TryParse(string input, out InformationalVersion version)
|
||||
{
|
||||
version = default(InformationalVersion);
|
||||
var trim = input.TrimStart('v');
|
||||
var info = trim.Split('-');
|
||||
if (!Version.TryParse(info[0], out Version result))
|
||||
return false;
|
||||
|
||||
version = new InformationalVersion { Version = result };
|
||||
|
||||
if (info.Length > 1)
|
||||
version.Information = info.Skip(1).ToArray();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (Information == null || Information.Length == 0)
|
||||
return $"v{Version}";
|
||||
|
||||
return $"v{Version}-{string.Join("-", Information)}";
|
||||
}
|
||||
|
||||
public static explicit operator InformationalVersion(Version v)
|
||||
{
|
||||
return new InformationalVersion { Version = v };
|
||||
}
|
||||
|
||||
public static implicit operator Version(InformationalVersion v)
|
||||
{
|
||||
return v.Version;
|
||||
}
|
||||
}
|
||||
}
|
128
Torch.API/Managers/IChatManagerClient.cs
Normal file
128
Torch.API/Managers/IChatManagerClient.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using VRage.Game;
|
||||
using VRage.Network;
|
||||
using VRage.Replication;
|
||||
|
||||
namespace Torch.API.Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a scripted or user chat message.
|
||||
/// </summary>
|
||||
public struct TorchChatMessage
|
||||
{
|
||||
/// <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, string font = MyFontEnum.Blue)
|
||||
{
|
||||
Timestamp = DateTime.Now;
|
||||
AuthorSteamId = null;
|
||||
Author = author;
|
||||
Message = message;
|
||||
Font = font;
|
||||
}
|
||||
|
||||
/// <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, string font = MyFontEnum.Blue)
|
||||
{
|
||||
Timestamp = DateTime.Now;
|
||||
AuthorSteamId = authorSteamId;
|
||||
Author = author;
|
||||
Message = message;
|
||||
Font = font;
|
||||
}
|
||||
|
||||
/// <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, string font = MyFontEnum.Blue)
|
||||
{
|
||||
Timestamp = DateTime.Now;
|
||||
AuthorSteamId = authorSteamId;
|
||||
Author = MyMultiplayer.Static?.GetMemberName(authorSteamId) ?? "Player";
|
||||
Message = message;
|
||||
Font = font;
|
||||
}
|
||||
|
||||
/// <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 font, or null if default.
|
||||
/// </summary>
|
||||
public readonly string Font;
|
||||
}
|
||||
|
||||
/// <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" );
|
||||
}
|
||||
}
|
45
Torch.API/Managers/IChatManagerServer.cs
Normal file
45
Torch.API/Managers/IChatManagerServer.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Network;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
/// <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="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, string font, ulong targetSteamId = 0);
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ModAPI;
|
||||
|
||||
namespace Torch.API.Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for received messages.
|
||||
/// </summary>
|
||||
/// <param name="message">Message data.</param>
|
||||
/// <param name="sendToOthers">Flag to broadcast message to other players.</param>
|
||||
public delegate void MessageReceivedDel(IChatMessage message, ref bool sendToOthers);
|
||||
|
||||
/// <summary>
|
||||
/// API for multiplayer related functions.
|
||||
/// </summary>
|
||||
public interface IMultiplayerManager : IManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Fired when a player joins.
|
||||
/// </summary>
|
||||
event Action<IPlayer> PlayerJoined;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a player disconnects.
|
||||
/// </summary>
|
||||
event Action<IPlayer> PlayerLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a chat message is received.
|
||||
/// </summary>
|
||||
event MessageReceivedDel MessageReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message to all or one specific player.
|
||||
/// </summary>
|
||||
void SendMessage(string message, string author = "Server", long playerId = 0, string font = MyFontEnum.Blue);
|
||||
|
||||
/// <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>
|
||||
/// 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);
|
||||
}
|
||||
}
|
41
Torch.API/Managers/IMultiplayerManagerBase.cs
Normal file
41
Torch.API/Managers/IMultiplayerManagerBase.cs
Normal 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);
|
||||
}
|
||||
}
|
12
Torch.API/Managers/IMultiplayerManagerClient.cs
Normal file
12
Torch.API/Managers/IMultiplayerManagerClient.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
46
Torch.API/Managers/IMultiplayerManagerServer.cs
Normal file
46
Torch.API/Managers/IMultiplayerManagerServer.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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>
|
||||
/// 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;
|
||||
}
|
||||
}
|
@@ -18,6 +18,12 @@ namespace Torch.API.Managers
|
||||
/// Register a network handler.
|
||||
/// </summary>
|
||||
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>
|
||||
@@ -33,6 +39,7 @@ namespace Torch.API.Managers
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
@@ -14,12 +14,12 @@ namespace Torch.API.Managers
|
||||
/// <summary>
|
||||
/// Fired when plugins are loaded.
|
||||
/// </summary>
|
||||
event Action<IList<ITorchPlugin>> PluginsLoaded;
|
||||
event Action<IReadOnlyCollection<ITorchPlugin>> PluginsLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of loaded plugins.
|
||||
/// </summary>
|
||||
IList<ITorchPlugin> Plugins { get; }
|
||||
IReadOnlyDictionary<Guid, ITorchPlugin> Plugins { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates all loaded plugins.
|
||||
|
@@ -17,7 +17,7 @@ namespace Torch.API.Plugins
|
||||
/// <summary>
|
||||
/// The version of the plugin.
|
||||
/// </summary>
|
||||
Version Version { get; }
|
||||
string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the plugin.
|
||||
|
@@ -10,6 +10,7 @@ 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
|
||||
{
|
||||
|
44
Torch.API/Session/GameSaveResult.cs
Normal file
44
Torch.API/Session/GameSaveResult.cs
Normal 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
|
||||
}
|
||||
}
|
@@ -25,5 +25,15 @@ namespace Torch.API.Session
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,11 @@ namespace Torch.API.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>
|
||||
|
38
Torch.API/Session/TorchSessionState.cs
Normal file
38
Torch.API/Session/TorchSessionState.cs
Normal 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);
|
||||
}
|
@@ -74,11 +74,6 @@
|
||||
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="SteamSDK, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\GameBinaries\SteamSDK.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -160,15 +155,23 @@
|
||||
<Link>Properties\AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="ConnectionState.cs" />
|
||||
<Compile Include="IChatMessage.cs" />
|
||||
<Compile Include="InformationalVersion.cs" />
|
||||
<Compile Include="ITorchConfig.cs" />
|
||||
<Compile Include="Managers\DependencyManagerExtensions.cs" />
|
||||
<Compile Include="Managers\DependencyProviderExtensions.cs" />
|
||||
<Compile Include="Event\EventHandlerAttribute.cs" />
|
||||
<Compile Include="Event\IEvent.cs" />
|
||||
<Compile Include="Event\IEventHandler.cs" />
|
||||
<Compile Include="Managers\IChatManagerClient.cs" />
|
||||
<Compile Include="Managers\IChatManagerServer.cs" />
|
||||
<Compile Include="Managers\IDependencyManager.cs" />
|
||||
<Compile Include="Managers\IDependencyProvider.cs" />
|
||||
<Compile Include="Event\IEventManager.cs" />
|
||||
<Compile Include="Managers\IManager.cs" />
|
||||
<Compile Include="Managers\IMultiplayerManager.cs" />
|
||||
<Compile Include="Managers\IMultiplayerManagerClient.cs" />
|
||||
<Compile Include="Managers\IMultiplayerManagerBase.cs" />
|
||||
<Compile Include="IPlayer.cs" />
|
||||
<Compile Include="Managers\IMultiplayerManagerServer.cs" />
|
||||
<Compile Include="Managers\INetworkManager.cs" />
|
||||
<Compile Include="Managers\IPluginManager.cs" />
|
||||
<Compile Include="Plugins\ITorchPlugin.cs" />
|
||||
@@ -180,8 +183,11 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServerState.cs" />
|
||||
<Compile Include="ModAPI\TorchAPI.cs" />
|
||||
<Compile Include="Session\GameSaveResult.cs" />
|
||||
<Compile Include="Session\ITorchSession.cs" />
|
||||
<Compile Include="Session\ITorchSessionManager.cs" />
|
||||
<Compile Include="Session\TorchSessionState.cs" />
|
||||
<Compile Include="TorchGameState.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
@@ -189,6 +195,7 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||
</Project>
|
47
Torch.API/TorchGameState.cs
Normal file
47
Torch.API/TorchGameState.cs
Normal 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);
|
||||
}
|
@@ -90,6 +90,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Torch.Client;
|
||||
using Torch.Tests;
|
||||
using Torch.Utils;
|
||||
@@ -29,6 +30,10 @@ namespace Torch.Client.Tests
|
||||
|
||||
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))]
|
||||
@@ -62,6 +67,28 @@ namespace Torch.Client.Tests
|
||||
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
|
||||
}
|
||||
}
|
||||
|
32
Torch.Client/Manager/MultiplayerManagerClient.cs
Normal file
32
Torch.Client/Manager/MultiplayerManagerClient.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
58
Torch.Client/Manager/MultiplayerManagerLobby.cs
Normal file
58
Torch.Client/Manager/MultiplayerManagerLobby.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
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 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 bool IsBanned(ulong steamId) => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<ulong> PlayerKicked
|
||||
{
|
||||
add => throw new NotImplementedException();
|
||||
remove => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<ulong, bool> PlayerBanned
|
||||
{
|
||||
add => throw new NotImplementedException();
|
||||
remove => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Attach()
|
||||
{
|
||||
base.Attach();
|
||||
MyMultiplayer.Static.ClientJoined += RaiseClientJoined;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Detach()
|
||||
{
|
||||
MyMultiplayer.Static.ClientJoined -= RaiseClientJoined;
|
||||
base.Detach();
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,6 +18,7 @@ namespace Torch.Client
|
||||
{
|
||||
public const string SpaceEngineersBinaries = "Bin64";
|
||||
private static string _spaceEngInstallAlias = null;
|
||||
|
||||
public static string SpaceEngineersInstallAlias
|
||||
{
|
||||
get
|
||||
@@ -26,7 +27,8 @@ namespace Torch.Client
|
||||
if (_spaceEngInstallAlias == null)
|
||||
{
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "SpaceEngineersAlias");
|
||||
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location),
|
||||
"SpaceEngineersAlias");
|
||||
}
|
||||
return _spaceEngInstallAlias;
|
||||
}
|
||||
@@ -52,16 +54,20 @@ namespace Torch.Client
|
||||
{
|
||||
AllocConsole();
|
||||
#endif
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
// Early config: Resolve SE install directory.
|
||||
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
|
||||
SetupSpaceEngInstallAlias();
|
||||
|
||||
using (new TorchAssemblyResolver(Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries)))
|
||||
if (!TorchLauncher.IsTorchWrapped())
|
||||
{
|
||||
RunClient();
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
// Early config: Resolve SE install directory.
|
||||
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
|
||||
SetupSpaceEngInstallAlias();
|
||||
|
||||
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName, args,
|
||||
Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries));
|
||||
return;
|
||||
}
|
||||
|
||||
RunClient();
|
||||
#if DEBUG
|
||||
}
|
||||
finally
|
||||
@@ -77,7 +83,8 @@ namespace Torch.Client
|
||||
|
||||
// TODO look at Steam/config/Config.VDF? Has alternate directories.
|
||||
var steamDir =
|
||||
Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Valve\\Steam", "SteamPath", null) as string;
|
||||
Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Valve\\Steam", "SteamPath",
|
||||
null) as string;
|
||||
if (steamDir != null)
|
||||
{
|
||||
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
|
||||
@@ -85,7 +92,10 @@ namespace Torch.Client
|
||||
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
|
||||
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
|
||||
else
|
||||
{
|
||||
_log.Debug("Couldn't find Space Engineers in {0}", spaceEngineersDirectory);
|
||||
spaceEngineersDirectory = null;
|
||||
}
|
||||
}
|
||||
if (spaceEngineersDirectory == null)
|
||||
{
|
||||
@@ -97,7 +107,8 @@ namespace Torch.Client
|
||||
{
|
||||
if (dialog.ShowDialog() != DialogResult.OK)
|
||||
{
|
||||
var ex = new FileNotFoundException("Unable to find the Space Engineers install directory, aborting");
|
||||
var ex = new FileNotFoundException(
|
||||
"Unable to find the Space Engineers install directory, aborting");
|
||||
_log.Fatal(ex);
|
||||
LogManager.Flush();
|
||||
throw ex;
|
||||
@@ -109,11 +120,12 @@ namespace Torch.Client
|
||||
$"Unable to find {0} in {1}. Are you sure it's the Space Engineers install directory?",
|
||||
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
break;
|
||||
} while (true); // Repeat until they confirm.
|
||||
} while (true); // Repeat until they confirm.
|
||||
}
|
||||
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
|
||||
{
|
||||
var ex = new IOException($"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
|
||||
var ex = new IOException(
|
||||
$"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
|
||||
_log.Fatal(ex);
|
||||
LogManager.Flush();
|
||||
throw ex;
|
||||
@@ -121,7 +133,8 @@ namespace Torch.Client
|
||||
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
|
||||
if (!File.Exists(junctionVerify))
|
||||
{
|
||||
var ex = new FileNotFoundException($"Junction link is not working. File {junctionVerify} does not exist");
|
||||
var ex = new FileNotFoundException(
|
||||
$"Junction link is not working. File {junctionVerify} does not exist");
|
||||
_log.Fatal(ex);
|
||||
LogManager.Flush();
|
||||
throw ex;
|
||||
@@ -153,7 +166,7 @@ namespace Torch.Client
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var ex = (Exception)e.ExceptionObject;
|
||||
var ex = (Exception) e.ExceptionObject;
|
||||
_log.Error(ex);
|
||||
LogManager.Flush();
|
||||
MessageBox.Show(ex.StackTrace, ex.Message);
|
||||
|
@@ -121,11 +121,12 @@
|
||||
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||
<Link>Properties\AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Manager\MultiplayerManagerClient.cs" />
|
||||
<Compile Include="Manager\MultiplayerManagerLobby.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TorchClient.cs" />
|
||||
<Compile Include="TorchClientConfig.cs" />
|
||||
<Compile Include="TorchConsoleScreen.cs" />
|
||||
<Compile Include="TorchMainMenuScreen.cs" />
|
||||
<Compile Include="TorchSettingsScreen.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
@@ -138,6 +139,9 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<Compile Include="UI\TorchMainMenuScreen.cs" />
|
||||
<Compile Include="UI\TorchNavScreen.cs" />
|
||||
<Compile Include="UI\TorchSettingsScreen.cs" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
@@ -167,6 +171,7 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||
<PropertyGroup>
|
||||
|
@@ -4,12 +4,18 @@ using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Platform;
|
||||
using Sandbox.Game;
|
||||
using SpaceEngineers.Game;
|
||||
using VRage.Steam;
|
||||
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.GameServices;
|
||||
@@ -20,13 +26,19 @@ namespace Torch.Client
|
||||
{
|
||||
public class TorchClient : TorchBase, ITorchClient
|
||||
{
|
||||
private MyCommonProgramStartup _startup;
|
||||
private IMyRender _renderer;
|
||||
private const uint APP_ID = 244850;
|
||||
protected override uint SteamAppId => 244850;
|
||||
protected override string SteamAppName => "SpaceEngineers";
|
||||
|
||||
public TorchClient()
|
||||
{
|
||||
Config = new TorchClientConfig();
|
||||
var sessionManager = Managers.GetManager<ITorchSessionManager>();
|
||||
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerLobby
|
||||
? new MultiplayerManagerLobby(this)
|
||||
: null);
|
||||
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerClientBase
|
||||
? new MultiplayerManagerClient(this)
|
||||
: null);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
@@ -34,39 +46,11 @@ namespace Torch.Client
|
||||
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
|
||||
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
|
||||
Log.Info("Initializing Torch Client");
|
||||
Config.InstancePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
SteamAppName);
|
||||
base.Init();
|
||||
|
||||
SpaceEngineersGame.SetupBasicGameInfo();
|
||||
_startup = new MyCommonProgramStartup(RunArgs);
|
||||
if (_startup.PerformReporting())
|
||||
throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
|
||||
|
||||
_startup.PerformAutoconnect();
|
||||
if (!_startup.CheckSingleInstance())
|
||||
throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
|
||||
|
||||
var appDataPath = _startup.GetAppDataPath();
|
||||
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
|
||||
MyInitializer.InitCheckSum();
|
||||
_startup.InitSplashScreen();
|
||||
if (!_startup.Check64Bit())
|
||||
throw new InvalidOperationException("Torch requires a 64bit operating system");
|
||||
|
||||
_startup.DetectSharpDxLeaksBeforeRun();
|
||||
var steamService = new SteamService(Game.IsDedicated, APP_ID);
|
||||
MyServiceManager.Instance.AddService<IMyGameService>(steamService);
|
||||
_renderer = null;
|
||||
SpaceEngineersGame.SetupPerGameSettings();
|
||||
// I'm sorry, but it's what Keen does in SpaceEngineers.MyProgram
|
||||
#pragma warning disable 612
|
||||
SpaceEngineersGame.SetupRender();
|
||||
#pragma warning restore 612
|
||||
InitializeRender();
|
||||
if (!_startup.CheckSteamRunning())
|
||||
throw new InvalidOperationException("Space Engineers requires steam to be running");
|
||||
|
||||
if (!Game.IsDedicated)
|
||||
MyFileSystem.InitUserSpecific(MyGameService.UserId.ToString());
|
||||
OverrideMenus();
|
||||
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
||||
}
|
||||
|
||||
private void OverrideMenus()
|
||||
@@ -74,27 +58,15 @@ namespace Torch.Client
|
||||
var credits = new MyCreditsDepartment("Torch Developed By")
|
||||
{
|
||||
Persons = new List<MyCreditsPerson>
|
||||
{
|
||||
new MyCreditsPerson("THE TORCH TEAM"),
|
||||
new MyCreditsPerson("http://github.com/TorchSE"),
|
||||
}
|
||||
{
|
||||
new MyCreditsPerson("THE TORCH TEAM"),
|
||||
new MyCreditsPerson("http://github.com/TorchSE"),
|
||||
}
|
||||
};
|
||||
MyPerGameSettings.Credits.Departments.Insert(0, credits);
|
||||
|
||||
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
using (var spaceEngineersGame = new SpaceEngineersGame(RunArgs))
|
||||
{
|
||||
Log.Info("Starting client");
|
||||
OverrideMenus();
|
||||
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded;
|
||||
spaceEngineersGame.OnGameExit += Dispose;
|
||||
spaceEngineersGame.Run(false, _startup.DisposeSplashScreen);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetRenderWindowTitle(string title)
|
||||
{
|
||||
@@ -105,66 +77,16 @@ namespace Torch.Client
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (renderWindowField == null)
|
||||
return;
|
||||
var window = renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as System.Windows.Forms.Form;
|
||||
var window =
|
||||
renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as
|
||||
System.Windows.Forms.Form;
|
||||
if (window != null)
|
||||
renderThread.Invoke(() =>
|
||||
{
|
||||
window.Text = title;
|
||||
});
|
||||
renderThread.Invoke(() => { window.Text = title; });
|
||||
}
|
||||
|
||||
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e)
|
||||
public override void Restart()
|
||||
{
|
||||
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
MyGameService.ShutDown();
|
||||
_startup.DetectSharpDxLeaksAfterRun();
|
||||
MyInitializer.InvokeAfterRun();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
56
Torch.Client/UI/TorchMainMenuScreen.cs
Normal file
56
Torch.Client/UI/TorchMainMenuScreen.cs
Normal 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>());
|
||||
}
|
||||
}
|
||||
}
|
49
Torch.Client/UI/TorchNavScreen.cs
Normal file
49
Torch.Client/UI/TorchNavScreen.cs
Normal 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();
|
||||
}
|
||||
}
|
25
Torch.Client/UI/TorchSettingsScreen.cs
Normal file
25
Torch.Client/UI/TorchSettingsScreen.cs
Normal 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();
|
||||
}
|
||||
}
|
72
Torch.Mod/Messages/DialogMessage.cs
Normal file
72
Torch.Mod/Messages/DialogMessage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
26
Torch.Mod/Messages/IncomingMessage.cs
Normal file
26
Torch.Mod/Messages/IncomingMessage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
51
Torch.Mod/Messages/MessageBase.cs
Normal file
51
Torch.Mod/Messages/MessageBase.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace Torch.Mod.Messages
|
||||
{
|
||||
#region Includes
|
||||
[ProtoInclude(1, typeof(DialogMessage))]
|
||||
[ProtoInclude(2, typeof(NotificationMessage))]
|
||||
[ProtoInclude(3, typeof(VoxelResetMessage))]
|
||||
#endregion
|
||||
|
||||
[ProtoContract]
|
||||
public abstract class MessageBase
|
||||
{
|
||||
[ProtoMember(101)]
|
||||
public ulong SenderId;
|
||||
|
||||
public abstract void ProcessClient();
|
||||
public abstract void ProcessServer();
|
||||
|
||||
//members below not serialized, they're just metadata about the intended target(s) of this message
|
||||
internal MessageTarget TargetType;
|
||||
internal ulong Target;
|
||||
internal ulong[] Ignore;
|
||||
internal byte[] CompressedData;
|
||||
}
|
||||
|
||||
public enum MessageTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Send to Target
|
||||
/// </summary>
|
||||
Single,
|
||||
/// <summary>
|
||||
/// Send to Server
|
||||
/// </summary>
|
||||
Server,
|
||||
/// <summary>
|
||||
/// Send to all Clients (only valid from server)
|
||||
/// </summary>
|
||||
AllClients,
|
||||
/// <summary>
|
||||
/// Send to all except those steam ID listed in Ignore
|
||||
/// </summary>
|
||||
AllExcept,
|
||||
}
|
||||
}
|
39
Torch.Mod/Messages/NotificationMessage.cs
Normal file
39
Torch.Mod/Messages/NotificationMessage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
44
Torch.Mod/Messages/VoxelResetMessage.cs
Normal file
44
Torch.Mod/Messages/VoxelResetMessage.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
189
Torch.Mod/ModCommunication.cs
Normal file
189
Torch.Mod/ModCommunication.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch.Mod.Messages;
|
||||
using VRage;
|
||||
using VRage.Collections;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.Utils;
|
||||
using Task = ParallelTasks.Task;
|
||||
|
||||
namespace Torch.Mod
|
||||
{
|
||||
public static class ModCommunication
|
||||
{
|
||||
public const ushort NET_ID = 4352;
|
||||
private static bool _closing = false;
|
||||
private static BlockingCollection<MessageBase> _processing;
|
||||
private static MyConcurrentPool<IncomingMessage> _messagePool;
|
||||
private static List<IMyPlayer> _playerCache;
|
||||
|
||||
public static void Register()
|
||||
{
|
||||
MyLog.Default.WriteLineAndConsole("TORCH MOD: Registering mod communication.");
|
||||
_processing = new BlockingCollection<MessageBase>(new ConcurrentQueue<MessageBase>());
|
||||
_playerCache = new List<IMyPlayer>();
|
||||
_messagePool = new MyConcurrentPool<IncomingMessage>(8);
|
||||
|
||||
MyAPIGateway.Multiplayer.RegisterMessageHandler(NET_ID, MessageHandler);
|
||||
//background thread to handle de/compression and processing
|
||||
_closing = false;
|
||||
MyAPIGateway.Parallel.StartBackground(DoProcessing);
|
||||
MyLog.Default.WriteLineAndConsole("TORCH MOD: Mod communication registered successfully.");
|
||||
}
|
||||
|
||||
public static void Unregister()
|
||||
{
|
||||
MyLog.Default.WriteLineAndConsole("TORCH MOD: Unregistering mod communication.");
|
||||
MyAPIGateway.Multiplayer?.UnregisterMessageHandler(NET_ID, MessageHandler);
|
||||
_processing?.CompleteAdding();
|
||||
_closing = true;
|
||||
//_task.Wait();
|
||||
}
|
||||
|
||||
private static void MessageHandler(byte[] bytes)
|
||||
{
|
||||
var m = _messagePool.Get();
|
||||
m.CompressedData = bytes;
|
||||
_processing.Add(m);
|
||||
}
|
||||
|
||||
public static void DoProcessing()
|
||||
{
|
||||
while (!_closing)
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = _processing.Take();
|
||||
MyLog.Default.WriteLineAndConsole($"Processing message: {m.GetType().Name}");
|
||||
|
||||
if (m is IncomingMessage)
|
||||
{
|
||||
MessageBase i;
|
||||
try
|
||||
{
|
||||
var o = MyCompression.Decompress(m.CompressedData);
|
||||
m.CompressedData = null;
|
||||
_messagePool.Return((IncomingMessage)m);
|
||||
i = MyAPIGateway.Utilities.SerializeFromBinary<MessageBase>(o);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Failed to deserialize message! {ex}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MyAPIGateway.Multiplayer.IsServer)
|
||||
i.ProcessServer();
|
||||
else
|
||||
i.ProcessClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
var b = MyAPIGateway.Utilities.SerializeToBinary(m);
|
||||
m.CompressedData = MyCompression.Compress(b);
|
||||
|
||||
MyAPIGateway.Utilities.InvokeOnGameThread(() =>
|
||||
{
|
||||
|
||||
switch (m.TargetType)
|
||||
{
|
||||
case MessageTarget.Single:
|
||||
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, m.Target);
|
||||
break;
|
||||
case MessageTarget.Server:
|
||||
MyAPIGateway.Multiplayer.SendMessageToServer(NET_ID, m.CompressedData);
|
||||
break;
|
||||
case MessageTarget.AllClients:
|
||||
MyAPIGateway.Players.GetPlayers(_playerCache);
|
||||
foreach (var p in _playerCache)
|
||||
{
|
||||
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId)
|
||||
continue;
|
||||
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
|
||||
}
|
||||
break;
|
||||
case MessageTarget.AllExcept:
|
||||
MyAPIGateway.Players.GetPlayers(_playerCache);
|
||||
foreach (var p in _playerCache)
|
||||
{
|
||||
if (p.SteamUserId == MyAPIGateway.Multiplayer.MyId || m.Ignore.Contains(p.SteamUserId))
|
||||
continue;
|
||||
MyAPIGateway.Multiplayer.SendMessageTo(NET_ID, m.CompressedData, p.SteamUserId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception();
|
||||
}
|
||||
_playerCache.Clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MyLog.Default.WriteLineAndConsole($"TORCH MOD: Exception occurred in communication thread! {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
MyLog.Default.WriteLineAndConsole("TORCH MOD: COMMUNICATION THREAD: EXIT SIGNAL RECEIVED!");
|
||||
//exit signal received. Clean everything and GTFO
|
||||
_processing?.Dispose();
|
||||
_processing = null;
|
||||
_messagePool?.Clean();
|
||||
_messagePool = null;
|
||||
_playerCache = null;
|
||||
}
|
||||
|
||||
public static void SendMessageTo(MessageBase message, ulong target)
|
||||
{
|
||||
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||
throw new Exception("Only server can send targeted messages");
|
||||
|
||||
if (_closing)
|
||||
return;
|
||||
|
||||
message.Target = target;
|
||||
message.TargetType = MessageTarget.Single;
|
||||
_processing.Add(message);
|
||||
}
|
||||
|
||||
public static void SendMessageToClients(MessageBase message)
|
||||
{
|
||||
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||
throw new Exception("Only server can send targeted messages");
|
||||
|
||||
if (_closing)
|
||||
return;
|
||||
|
||||
message.TargetType = MessageTarget.AllClients;
|
||||
_processing.Add(message);
|
||||
}
|
||||
|
||||
public static void SendMessageExcept(MessageBase message, params ulong[] ignoredUsers)
|
||||
{
|
||||
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||
throw new Exception("Only server can send targeted messages");
|
||||
|
||||
if (_closing)
|
||||
return;
|
||||
|
||||
message.TargetType = MessageTarget.AllExcept;
|
||||
message.Ignore = ignoredUsers;
|
||||
_processing.Add(message);
|
||||
}
|
||||
|
||||
public static void SendMessageToServer(MessageBase message)
|
||||
{
|
||||
if (_closing)
|
||||
return;
|
||||
|
||||
message.TargetType = MessageTarget.Server;
|
||||
_processing.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
20
Torch.Mod/Torch.Mod.projitems
Normal file
20
Torch.Mod/Torch.Mod.projitems
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<SharedGUID>3ce4d2e9-b461-4f19-8233-f87e0dfddd74</SharedGUID>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Import_RootNamespace>Torch.Mod</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Messages\IncomingMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Messages\NotificationMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Messages\DialogMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Messages\MessageBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Messages\VoxelResetMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ModCommunication.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)TorchModCore.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
13
Torch.Mod/Torch.Mod.shproj
Normal file
13
Torch.Mod/Torch.Mod.shproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>3ce4d2e9-b461-4f19-8233-f87e0dfddd74</ProjectGuid>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
|
||||
<PropertyGroup />
|
||||
<Import Project="Torch.Mod.projitems" Label="Shared" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
|
||||
</Project>
|
37
Torch.Mod/TorchModCore.cs
Normal file
37
Torch.Mod/TorchModCore.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Game.Components;
|
||||
|
||||
namespace Torch.Mod
|
||||
{
|
||||
[MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation)]
|
||||
public class TorchModCore : MySessionComponentBase
|
||||
{
|
||||
public const ulong MOD_ID = 1406994352;
|
||||
private static bool _init;
|
||||
|
||||
public override void UpdateAfterSimulation()
|
||||
{
|
||||
if (_init)
|
||||
return;
|
||||
|
||||
_init = true;
|
||||
ModCommunication.Register();
|
||||
}
|
||||
|
||||
protected override void UnloadData()
|
||||
{
|
||||
try
|
||||
{
|
||||
ModCommunication.Unregister();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//session unloading, don't care
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -39,6 +39,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@@ -46,6 +47,10 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="VRage.Game, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -65,6 +70,7 @@
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TorchServerReflectionTest.cs" />
|
||||
<Compile Include="TorchServerSessionSettingsTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||
@@ -88,6 +94,9 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||
</Project>
|
@@ -1,10 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
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()
|
||||
@@ -28,8 +30,12 @@ namespace Torch.Server.Tests
|
||||
|
||||
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||
|
||||
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
|
||||
|
||||
public static IEnumerable<object[]> Events => Manager().Events;
|
||||
|
||||
#region Binding
|
||||
[Theory]
|
||||
//[Theory]
|
||||
[MemberData(nameof(Getters))]
|
||||
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||
{
|
||||
@@ -40,7 +46,7 @@ namespace Torch.Server.Tests
|
||||
Assert.NotNull(field.Field.GetValue(null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
//[Theory]
|
||||
[MemberData(nameof(Setters))]
|
||||
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||
{
|
||||
@@ -51,7 +57,7 @@ namespace Torch.Server.Tests
|
||||
Assert.NotNull(field.Field.GetValue(null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
//[Theory]
|
||||
[MemberData(nameof(Invokers))]
|
||||
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||
{
|
||||
@@ -61,6 +67,17 @@ namespace Torch.Server.Tests
|
||||
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
|
||||
}
|
||||
}
|
||||
|
34
Torch.Server.Tests/TorchServerSessionSettingsTest.cs
Normal file
34
Torch.Server.Tests/TorchServerSessionSettingsTest.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
65
Torch.Server/Commands/WhitelistCommands.cs
Normal file
65
Torch.Server/Commands/WhitelistCommands.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Torch.Commands;
|
||||
|
||||
namespace Torch.Server.Commands
|
||||
{
|
||||
[Category("whitelist")]
|
||||
public class WhitelistCommands : CommandModule
|
||||
{
|
||||
private TorchConfig Config => (TorchConfig)Context.Torch.Config;
|
||||
|
||||
[Command("on", "Enables the whitelist.")]
|
||||
public void On()
|
||||
{
|
||||
if (!Config.EnableWhitelist)
|
||||
{
|
||||
Config.EnableWhitelist = true;
|
||||
Context.Respond("Whitelist enabled.");
|
||||
Config.Save();
|
||||
}
|
||||
else
|
||||
Context.Respond("Whitelist is already enabled.");
|
||||
}
|
||||
|
||||
[Command("off", "Disables the whitelist")]
|
||||
public void Off()
|
||||
{
|
||||
if (Config.EnableWhitelist)
|
||||
{
|
||||
Config.EnableWhitelist = false;
|
||||
Context.Respond("Whitelist disabled.");
|
||||
Config.Save();
|
||||
}
|
||||
else
|
||||
Context.Respond("Whitelist is already disabled.");
|
||||
}
|
||||
|
||||
[Command("add", "Add a Steam ID to the whitelist.")]
|
||||
public void Add(ulong steamId)
|
||||
{
|
||||
if (Config.Whitelist.Add(steamId))
|
||||
{
|
||||
Context.Respond($"Added {steamId} to the whitelist.");
|
||||
Config.Save();
|
||||
}
|
||||
else
|
||||
Context.Respond($"{steamId} is already whitelisted.");
|
||||
}
|
||||
|
||||
[Command("remove", "Remove a Steam ID from the whitelist.")]
|
||||
public void Remove(ulong steamId)
|
||||
{
|
||||
if (Config.Whitelist.Remove(steamId))
|
||||
{
|
||||
Context.Respond($"Removed {steamId} from the whitelist.");
|
||||
Config.Save();
|
||||
}
|
||||
else
|
||||
Context.Respond($"{steamId} is not whitelisted.");
|
||||
}
|
||||
}
|
||||
}
|
54
Torch.Server/FlowDocumentTarget.cs
Normal file
54
Torch.Server/FlowDocumentTarget.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using NLog;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// NLog target that writes to a <see cref="FlowDocument"/>.
|
||||
/// </summary>
|
||||
[Target("flowDocument")]
|
||||
public sealed class FlowDocumentTarget : TargetWithLayout
|
||||
{
|
||||
private FlowDocument _document = new FlowDocument { Background = new SolidColorBrush(Colors.Black) };
|
||||
private readonly Paragraph _paragraph = new Paragraph();
|
||||
private readonly int _maxLines = 500;
|
||||
|
||||
public FlowDocument Document => _document;
|
||||
|
||||
public FlowDocumentTarget()
|
||||
{
|
||||
_document.Blocks.Add(_paragraph);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
_document.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
var message = $"{Layout.Render(logEvent)}\n";
|
||||
_paragraph.Inlines.Add(new Run(message) {Foreground = LogLevelColors[logEvent.Level]});
|
||||
|
||||
// A massive paragraph slows the UI down
|
||||
if (_paragraph.Inlines.Count > _maxLines)
|
||||
_paragraph.Inlines.Remove(_paragraph.Inlines.FirstInline);
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly Dictionary<LogLevel, SolidColorBrush> LogLevelColors = new Dictionary<LogLevel, SolidColorBrush>
|
||||
{
|
||||
[LogLevel.Trace] = new SolidColorBrush(Colors.DimGray),
|
||||
[LogLevel.Debug] = new SolidColorBrush(Colors.DarkGray),
|
||||
[LogLevel.Info] = new SolidColorBrush(Colors.White),
|
||||
[LogLevel.Warn] = new SolidColorBrush(Colors.Magenta),
|
||||
[LogLevel.Error] = new SolidColorBrush(Colors.Yellow),
|
||||
[LogLevel.Fatal] = new SolidColorBrush(Colors.Red),
|
||||
};
|
||||
}
|
||||
}
|
@@ -5,11 +5,18 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using NLog;
|
||||
using NLog.Targets;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Torch.Utils;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Library.Exceptions;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
@@ -21,12 +28,12 @@ namespace Torch.Server
|
||||
private const string STEAMCMD_ZIP = "temp.zip";
|
||||
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
||||
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
||||
|
||||
private const string RUNSCRIPT = @"force_install_dir ../
|
||||
login anonymous
|
||||
app_update 298740
|
||||
quit";
|
||||
|
||||
private TorchAssemblyResolver _resolver;
|
||||
private TorchConfig _config;
|
||||
private TorchServer _server;
|
||||
private string _basePath;
|
||||
@@ -44,12 +51,21 @@ quit";
|
||||
if (_init)
|
||||
return false;
|
||||
|
||||
#if !DEBUG
|
||||
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
||||
#endif
|
||||
|
||||
if (!args.Contains("-noupdate"))
|
||||
// This is what happens when Keen is bad and puts extensions into the System namespace.
|
||||
if (!Enumerable.Contains(args, "-noupdate"))
|
||||
RunSteamCmd();
|
||||
|
||||
_resolver = new TorchAssemblyResolver(Path.Combine(_basePath, "DedicatedServer64"));
|
||||
var basePath = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
|
||||
var apiSource = Path.Combine(basePath, "DedicatedServer64", "steam_api64.dll");
|
||||
var apiTarget = Path.Combine(basePath, "steam_api64.dll");
|
||||
|
||||
if (!File.Exists(apiTarget))
|
||||
File.Copy(apiSource, apiTarget);
|
||||
|
||||
_config = InitConfig();
|
||||
if (!_config.Parse(args))
|
||||
return false;
|
||||
@@ -61,13 +77,12 @@ quit";
|
||||
var pid = int.Parse(_config.WaitForPID);
|
||||
var waitProc = Process.GetProcessById(pid);
|
||||
Log.Info("Continuing in 5 seconds.");
|
||||
Thread.Sleep(5000);
|
||||
if (!waitProc.HasExited)
|
||||
Log.Warn($"Waiting for process {pid} to close");
|
||||
while (!waitProc.HasExited)
|
||||
{
|
||||
Log.Warn($"Killing old process {pid}.");
|
||||
waitProc.Kill();
|
||||
Console.Write(".");
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -82,19 +97,29 @@ quit";
|
||||
public void Run()
|
||||
{
|
||||
_server = new TorchServer(_config);
|
||||
_server.Init();
|
||||
|
||||
if (_config.NoGui || _config.Autostart)
|
||||
var init = Task.Run(() => _server.Init()).ContinueWith(x =>
|
||||
{
|
||||
new Thread(_server.Start).Start();
|
||||
}
|
||||
if (!x.IsFaulted)
|
||||
return;
|
||||
|
||||
Log.Error("Error initializing server.");
|
||||
LogException(x.Exception);
|
||||
});
|
||||
if (!_config.NoGui)
|
||||
{
|
||||
if (_config.Autostart)
|
||||
init.ContinueWith(x => _server.Start());
|
||||
|
||||
Log.Info("Showing UI");
|
||||
Console.SetOut(TextWriter.Null);
|
||||
NativeMethods.FreeConsole();
|
||||
new TorchUI(_server).ShowDialog();
|
||||
}
|
||||
|
||||
_resolver?.Dispose();
|
||||
else
|
||||
{
|
||||
init.Wait();
|
||||
_server.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private TorchConfig InitConfig()
|
||||
@@ -109,13 +134,13 @@ quit";
|
||||
else
|
||||
{
|
||||
Log.Info($"Generating default config at {configPath}");
|
||||
var config = new TorchConfig { InstancePath = Path.GetFullPath("Instance") };
|
||||
var config = new TorchConfig {InstancePath = Path.GetFullPath("Instance")};
|
||||
config.Save(configPath);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunSteamCmd()
|
||||
public static void RunSteamCmd()
|
||||
{
|
||||
var log = LogManager.GetLogger("SteamCMD");
|
||||
|
||||
@@ -164,21 +189,50 @@ quit";
|
||||
}
|
||||
}
|
||||
|
||||
private void LogException(Exception ex)
|
||||
{
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
LogException(ex.InnerException);
|
||||
}
|
||||
|
||||
Log.Fatal(ex);
|
||||
|
||||
if (ex is ReflectionTypeLoadException exti)
|
||||
foreach (Exception exl in exti.LoaderExceptions)
|
||||
LogException(exl);
|
||||
|
||||
if (ex is AggregateException ag)
|
||||
foreach (Exception e in ag.InnerExceptions)
|
||||
LogException(e);
|
||||
}
|
||||
|
||||
private void HandleException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var ex = (Exception)e.ExceptionObject;
|
||||
Log.Fatal(ex);
|
||||
Console.WriteLine("Exiting in 5 seconds.");
|
||||
Thread.Sleep(5000);
|
||||
LogException(ex);
|
||||
if (MyFakes.ENABLE_MINIDUMP_SENDING)
|
||||
{
|
||||
string path = Path.Combine(MyFileSystem.UserDataPath, "Minidump.dmp");
|
||||
Log.Info($"Generating minidump at {path}");
|
||||
MyMiniDump.Options options = MyMiniDump.Options.WithProcessThreadData | MyMiniDump.Options.WithThreadInfo;
|
||||
MyMiniDump.Write(path, options, MyMiniDump.ExceptionInfo.Present);
|
||||
}
|
||||
LogManager.Flush();
|
||||
if (_config.RestartOnCrash)
|
||||
{
|
||||
Console.WriteLine("Restarting in 5 seconds.");
|
||||
Thread.Sleep(5000);
|
||||
var exe = typeof(Program).Assembly.Location;
|
||||
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||
Process.Start(exe, _config.ToString());
|
||||
}
|
||||
//1627 = Function failed during execution.
|
||||
Environment.Exit(1627);
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Torch encountered a fatal error and needs to close. Please check the logs for details.");
|
||||
}
|
||||
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
265
Torch.Server/Managers/EntityControlManager.cs
Normal file
265
Torch.Server/Managers/EntityControlManager.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
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);
|
||||
|
||||
#pragma warning disable 649
|
||||
[ReflectedGetter(Name = "Keys")]
|
||||
private static readonly Func<ConditionalWeakTable<EntityViewModel, EntityControlViewModel>, ICollection<EntityViewModel>> _weakTableKeys;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// Warning: Creates a giant list, avoid if possible.
|
||||
/// </summary>
|
||||
internal ICollection<EntityViewModel> Keys => _weakTableKeys(_models);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,19 +10,27 @@ using Havok;
|
||||
using NLog;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Sandbox.Game;
|
||||
using Sandbox.Game.Gui;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Managers;
|
||||
using Torch.Mod;
|
||||
using Torch.Server.ViewModels;
|
||||
using VRage;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ObjectBuilder;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.Plugins;
|
||||
|
||||
namespace Torch.Server.Managers
|
||||
{
|
||||
public class InstanceManager : Manager
|
||||
{
|
||||
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
||||
|
||||
public event Action<ConfigDedicatedViewModel> InstanceLoaded;
|
||||
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
||||
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
|
||||
[Dependency]
|
||||
@@ -32,23 +40,15 @@ namespace Torch.Server.Managers
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Attach()
|
||||
{
|
||||
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
|
||||
MyFileSystem.Init("Content", Torch.Config.InstancePath);
|
||||
//Initializes saves path. Why this isn't in Init() we may never know.
|
||||
MyFileSystem.InitUserSpecific(null);
|
||||
}
|
||||
|
||||
|
||||
public void LoadInstance(string path, bool validate = true)
|
||||
{
|
||||
Log.Info($"Loading instance {path}");
|
||||
|
||||
if (validate)
|
||||
ValidateInstance(path);
|
||||
|
||||
MyFileSystem.Reset();
|
||||
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
|
||||
MyFileSystem.Init("Content", path);
|
||||
//Initializes saves path. Why this isn't in Init() we may never know.
|
||||
MyFileSystem.InitUserSpecific(null);
|
||||
@@ -64,33 +64,73 @@ namespace Torch.Server.Managers
|
||||
config.Load(configPath);
|
||||
|
||||
DedicatedConfig = new ConfigDedicatedViewModel(config);
|
||||
|
||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
|
||||
|
||||
foreach (var f in worldFolders)
|
||||
DedicatedConfig.WorldPaths.Add(f);
|
||||
{
|
||||
if (!string.IsNullOrEmpty(f) && File.Exists(Path.Combine(f, "Sandbox.sbc")))
|
||||
DedicatedConfig.Worlds.Add(new WorldViewModel(f));
|
||||
}
|
||||
|
||||
if (DedicatedConfig.WorldPaths.Count == 0)
|
||||
if (DedicatedConfig.Worlds.Count == 0)
|
||||
{
|
||||
Log.Warn($"No worlds found in the current instance {path}.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImportWorldConfig();
|
||||
SelectWorld(DedicatedConfig.LoadWorld ?? DedicatedConfig.Worlds.First().WorldPath, false);
|
||||
|
||||
/*
|
||||
if (string.IsNullOrEmpty(DedicatedConfig.LoadWorld))
|
||||
{
|
||||
Log.Warn("No world specified, importing first available world.");
|
||||
SelectWorld(DedicatedConfig.WorldPaths[0], false);
|
||||
}*/
|
||||
InstanceLoaded?.Invoke(DedicatedConfig);
|
||||
}
|
||||
|
||||
public void SelectWorld(string worldPath, bool modsOnly = true)
|
||||
{
|
||||
DedicatedConfig.LoadWorld = worldPath;
|
||||
ImportWorldConfig(modsOnly);
|
||||
DedicatedConfig.SelectedWorld = DedicatedConfig.Worlds.FirstOrDefault(x => x.WorldPath == worldPath);
|
||||
if (DedicatedConfig.SelectedWorld?.Checkpoint != null)
|
||||
{
|
||||
DedicatedConfig.Mods.Clear();
|
||||
//remove the Torch mod to avoid running multiple copies of it
|
||||
DedicatedConfig.SelectedWorld.Checkpoint.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID);
|
||||
foreach (var m in DedicatedConfig.SelectedWorld.Checkpoint.Mods)
|
||||
DedicatedConfig.Mods.Add(m.PublishedFileId);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectWorld(WorldViewModel world, bool modsOnly = true)
|
||||
{
|
||||
DedicatedConfig.LoadWorld = world.WorldPath;
|
||||
DedicatedConfig.SelectedWorld = world;
|
||||
if (DedicatedConfig.SelectedWorld?.Checkpoint != null)
|
||||
{
|
||||
DedicatedConfig.Mods.Clear();
|
||||
//remove the Torch mod to avoid running multiple copies of it
|
||||
DedicatedConfig.SelectedWorld.Checkpoint.Mods.RemoveAll(m => m.PublishedFileId == TorchModCore.MOD_ID);
|
||||
foreach (var m in DedicatedConfig.SelectedWorld.Checkpoint.Mods)
|
||||
DedicatedConfig.Mods.Add(m.PublishedFileId);
|
||||
}
|
||||
}
|
||||
|
||||
public void ImportSelectedWorldConfig()
|
||||
{
|
||||
ImportWorldConfig(DedicatedConfig.SelectedWorld, false);
|
||||
}
|
||||
|
||||
private void ImportWorldConfig(WorldViewModel world, bool modsOnly = true)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var mod in world.Checkpoint.Mods)
|
||||
sb.AppendLine(mod.PublishedFileId.ToString());
|
||||
|
||||
DedicatedConfig.Mods = world.Checkpoint.Mods.Select(x => x.PublishedFileId).ToList();
|
||||
|
||||
|
||||
Log.Debug("Loaded mod list from world");
|
||||
|
||||
if (!modsOnly)
|
||||
DedicatedConfig.SessionSettings = world.Checkpoint.Settings;
|
||||
}
|
||||
|
||||
private void ImportWorldConfig(bool modsOnly = true)
|
||||
{
|
||||
@@ -107,15 +147,11 @@ namespace Torch.Server.Managers
|
||||
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
if (checkpoint == null)
|
||||
{
|
||||
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})");
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var mod in checkpoint.Mods)
|
||||
sb.AppendLine(mod.PublishedFileId.ToString());
|
||||
|
||||
DedicatedConfig.Mods = sb.ToString();
|
||||
DedicatedConfig.Mods = checkpoint.Mods.Select(x => x.PublishedFileId).ToList();
|
||||
|
||||
Log.Debug("Loaded mod list from world");
|
||||
|
||||
@@ -131,23 +167,39 @@ namespace Torch.Server.Managers
|
||||
|
||||
public void SaveConfig()
|
||||
{
|
||||
DedicatedConfig.Save();
|
||||
var cf = Torch.Config as TorchConfig;
|
||||
if (cf?.ReservedPlayers?.Count > 0)
|
||||
{
|
||||
foreach (var res in cf.ReservedPlayers)
|
||||
{
|
||||
if (!DedicatedConfig.Reserved.Contains(res))
|
||||
DedicatedConfig.Reserved.Add(res);
|
||||
}
|
||||
}
|
||||
|
||||
DedicatedConfig.Save(Path.Combine(Torch.Config.InstancePath, CONFIG_NAME));
|
||||
Log.Info("Saved dedicated config.");
|
||||
|
||||
try
|
||||
{
|
||||
MyObjectBuilderSerializer.DeserializeXML(Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
var sandboxPath = Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc");
|
||||
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||
if (checkpoint == null)
|
||||
{
|
||||
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {Torch.Config.InstancePath})");
|
||||
return;
|
||||
}
|
||||
|
||||
checkpoint.SessionName = DedicatedConfig.WorldName;
|
||||
checkpoint.Settings = DedicatedConfig.SessionSettings;
|
||||
checkpoint.Mods.Clear();
|
||||
foreach (var modId in DedicatedConfig.Model.Mods)
|
||||
|
||||
foreach (var modId in DedicatedConfig.Mods)
|
||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
||||
|
||||
MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld);
|
||||
MyObjectBuilderSerializer.SerializeXML(sandboxPath, false, checkpoint);
|
||||
|
||||
//MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld);
|
||||
Log.Info("Saved world config.");
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -172,4 +224,44 @@ namespace Torch.Server.Managers
|
||||
config.Save(configPath);
|
||||
}
|
||||
}
|
||||
|
||||
public class WorldViewModel : ViewModel
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public string FolderName { get; set; }
|
||||
public string WorldPath { get; }
|
||||
public long WorldSizeKB { get; }
|
||||
private string _checkpointPath;
|
||||
public CheckpointViewModel Checkpoint { get; private set; }
|
||||
|
||||
public WorldViewModel(string worldPath)
|
||||
{
|
||||
WorldPath = worldPath;
|
||||
WorldSizeKB = new DirectoryInfo(worldPath).GetFiles().Sum(x => x.Length) / 1024;
|
||||
_checkpointPath = Path.Combine(WorldPath, "Sandbox.sbc");
|
||||
FolderName = Path.GetFileName(worldPath);
|
||||
BeginLoadCheckpoint();
|
||||
}
|
||||
|
||||
public async Task SaveCheckpointAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
using (var f = File.Open(_checkpointPath, FileMode.Create))
|
||||
MyObjectBuilderSerializer.SerializeXML(f, Checkpoint);
|
||||
});
|
||||
}
|
||||
|
||||
private void BeginLoadCheckpoint()
|
||||
{
|
||||
//Task.Run(() =>
|
||||
{
|
||||
Log.Info($"Preloading checkpoint {_checkpointPath}");
|
||||
MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint);
|
||||
Checkpoint = new CheckpointViewModel(checkpoint);
|
||||
OnPropertyChanged(nameof(Checkpoint));
|
||||
}//);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
301
Torch.Server/Managers/MultiplayerManagerDedicated.cs
Normal file
301
Torch.Server/Managers/MultiplayerManagerDedicated.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
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.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);
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseClientBanned(ulong user, bool banned)
|
||||
{
|
||||
PlayerBanned?.Invoke(user, banned);
|
||||
Torch.Invoke(() =>
|
||||
{
|
||||
if(_gameOwnerIds.TryGetValue(user, out ulong owner))
|
||||
MyMultiplayer.Static.BanClient(owner, banned);
|
||||
});
|
||||
}
|
||||
|
||||
internal void RaiseClientKicked(ulong user)
|
||||
{
|
||||
PlayerKicked?.Invoke(user);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) ||
|
||||
MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<ulong> PlayerKicked;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<ulong, bool> PlayerBanned;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Attach()
|
||||
{
|
||||
base.Attach();
|
||||
_gameServerValidateAuthTicketReplacer = _gameServerValidateAuthTicketFactory.Invoke();
|
||||
_gameServerUserGroupStatusReplacer = _gameServerUserGroupStatusFactory.Invoke();
|
||||
_gameServerValidateAuthTicketReplacer.Replace(
|
||||
new Action<ulong, JoinResult, ulong>(ValidateAuthTicketResponse), MyGameService.GameServer);
|
||||
_gameServerUserGroupStatusReplacer.Replace(new Action<ulong, ulong, bool, bool>(UserGroupStatusResponse),
|
||||
MyGameService.GameServer);
|
||||
_log.Info("Inserted steam 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 steam authentication intercept");
|
||||
base.Detach();
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable 649
|
||||
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.ValidateAuthTicketResponse),
|
||||
typeof(MyDedicatedServerBase), "GameServer_ValidateAuthTicketResponse")]
|
||||
private static Func<ReflectedEventReplacer> _gameServerValidateAuthTicketFactory;
|
||||
|
||||
[ReflectedEventReplace(typeof(MySteamGameServer), nameof(MySteamGameServer.UserGroupStatusResponse),
|
||||
typeof(MyDedicatedServerBase), "GameServer_UserGroupStatus")]
|
||||
private static Func<ReflectedEventReplacer> _gameServerUserGroupStatusFactory;
|
||||
|
||||
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 = "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)
|
||||
{
|
||||
var state = new MyP2PSessionState();
|
||||
MySteamService.Static.Peer2Peer.GetSessionState(steamId, ref state);
|
||||
var ip = new IPAddress(BitConverter.GetBytes(state.RemoteIP).Reverse().ToArray());
|
||||
|
||||
Torch.CurrentSession.KeenSession.PromotedUsers.TryGetValue(steamId, out MyPromoteLevel promoteLevel);
|
||||
|
||||
_log.Debug($"ValidateAuthTicketResponse(user={steamId}, response={response}, owner={steamOwner}, permissions={promoteLevel})");
|
||||
|
||||
_log.Info($"Connection attempt by {steamId} from {ip}");
|
||||
|
||||
if (Torch.CurrentSession.KeenSession.OnlineMode == MyOnlineModeEnum.OFFLINE &&
|
||||
promoteLevel < MyPromoteLevel.Admin)
|
||||
{
|
||||
_log.Warn($"Rejecting user {steamId}, world is set to offline and user is not admin.");
|
||||
UserRejected(steamId, JoinResult.TicketCanceled);
|
||||
}
|
||||
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL)
|
||||
RunEvent(new ValidateAuthTicketEvent(steamId, steamOwner, response, 0, true, false));
|
||||
else if (_getServerAccountType(MySandboxGame.ConfigDedicated.GroupID) != MyGameServiceAccountType.Clan)
|
||||
UserRejected(steamId, JoinResult.GroupIdInvalid);
|
||||
else if (MyGameService.GameServer.RequestGroupStatus(steamId, MySandboxGame.ConfigDedicated.GroupID))
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
49
Torch.Server/MultiTextWriter.cs
Normal file
49
Torch.Server/MultiTextWriter.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,33 +1,15 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration.Install;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.ServiceProcess;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using NLog;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch;
|
||||
using Torch.API;
|
||||
using Torch.Server.Views;
|
||||
using VRage.Game.ModAPI;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Security.Policy;
|
||||
using Torch.Server.Managers;
|
||||
using NLog.Targets;
|
||||
using Torch.Utils;
|
||||
using VRage.FileSystem;
|
||||
using VRageRender;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
@@ -39,18 +21,22 @@ namespace Torch.Server
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Target.Register<FlowDocumentTarget>("FlowDocument");
|
||||
//Ensures that all the files are downloaded in the Torch directory.
|
||||
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
|
||||
var binDir = Path.Combine(workingDir, "DedicatedServer64");
|
||||
Directory.SetCurrentDirectory(workingDir);
|
||||
|
||||
if (!TorchLauncher.IsTorchWrapped())
|
||||
{
|
||||
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName, args, binDir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Environment.UserInteractive)
|
||||
{
|
||||
using (var service = new TorchService())
|
||||
using (new TorchAssemblyResolver(binDir))
|
||||
{
|
||||
ServiceBase.Run(service);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,4 +47,4 @@ namespace Torch.Server
|
||||
initializer.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1065
Torch.Server/Properties/Annotations.cs
Normal file
1065
Torch.Server/Properties/Annotations.cs
Normal file
File diff suppressed because it is too large
Load Diff
89
Torch.Server/RichTextBoxWriter.cs
Normal file
89
Torch.Server/RichTextBoxWriter.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
public class RichTextBoxWriter : TextWriter
|
||||
{
|
||||
private RichTextBox textbox;
|
||||
private StringBuilder _cache = new StringBuilder();
|
||||
public RichTextBoxWriter(RichTextBox textbox)
|
||||
{
|
||||
this.textbox = textbox;
|
||||
textbox.Document.Background = new SolidColorBrush(UnpackColor(Console.BackgroundColor));
|
||||
textbox.Document.Blocks.Clear();
|
||||
textbox.Document.Blocks.Add(new Paragraph {LineHeight = 12});
|
||||
}
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
if (value == '\r')
|
||||
return;
|
||||
|
||||
_cache.Append(value);
|
||||
if (value == '\n')
|
||||
{
|
||||
var str = _cache.ToString();
|
||||
_cache.Clear();
|
||||
|
||||
var brush = _brushes[Console.ForegroundColor];
|
||||
textbox.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
var p = (Paragraph)textbox.Document.Blocks.FirstBlock;
|
||||
p.Inlines.Add(new Run(str) { Foreground = brush });
|
||||
textbox.ScrollToEnd();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Write(string value)
|
||||
{
|
||||
var brush = _brushes[Console.ForegroundColor];
|
||||
textbox.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
var p = (Paragraph)textbox.Document.Blocks.FirstBlock;
|
||||
p.Inlines.Add(new Run(value) { Foreground = brush });
|
||||
textbox.ScrollToEnd();
|
||||
});
|
||||
}
|
||||
|
||||
public override Encoding Encoding
|
||||
{
|
||||
get { return Encoding.ASCII; }
|
||||
}
|
||||
|
||||
static RichTextBoxWriter()
|
||||
{
|
||||
foreach (var value in (ConsoleColor[])Enum.GetValues(typeof(ConsoleColor)))
|
||||
{
|
||||
_brushes.Add(value, new SolidColorBrush(UnpackColor(value)));
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<ConsoleColor, SolidColorBrush> _brushes = new Dictionary<ConsoleColor, SolidColorBrush>();
|
||||
|
||||
private static Color UnpackColor(ConsoleColor color)
|
||||
{
|
||||
var colorByte = (byte)color;
|
||||
var isBright = (colorByte & 0b1000) >> 3 > 0;
|
||||
var brightness = isBright ? (byte)255 : (byte)128;
|
||||
var red = (colorByte & 0b0100) >> 2;
|
||||
var green = (colorByte & 0b0010) >> 1;
|
||||
var blue = (colorByte & 0b0001);
|
||||
|
||||
return new Color
|
||||
{
|
||||
R = (byte)(brightness * red),
|
||||
G = (byte)(brightness * green),
|
||||
B = (byte)(brightness * blue),
|
||||
A = 255
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Torch.Collections;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
public class ServerStatistics
|
||||
{
|
||||
public RollingAverage SimSpeed { get; } = new RollingAverage(30);
|
||||
}
|
||||
}
|
17
Torch.Server/Themes/Dark Theme Animated.xaml
Normal file
17
Torch.Server/Themes/Dark Theme Animated.xaml
Normal file
@@ -0,0 +1,17 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
|
||||
<!-- Accent and AppTheme setting -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedSingleRowTabControl.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
|
||||
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
85
Torch.Server/Themes/Dark Theme.xaml
Normal file
85
Torch.Server/Themes/Dark Theme.xaml
Normal file
@@ -0,0 +1,85 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
|
||||
<!-- Accent and AppTheme setting -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabControl">
|
||||
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="ColumnDefinition0"/>
|
||||
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
|
||||
<RowDefinition x:Name="RowDefinition1" Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" Orientation="Horizontal"/>
|
||||
<Border x:Name="ContentPanel" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
|
||||
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
</Border>
|
||||
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
|
||||
<TabPanel x:Name="HeaderPanel2"
|
||||
Panel.ZIndex ="1"
|
||||
KeyboardNavigation.TabIndex="1"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Margin="2,2,2,0"
|
||||
IsItemsHost="true"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="TabStripPlacement" Value="Bottom">
|
||||
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
|
||||
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
|
||||
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,0,2,2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="TabStripPlacement" Value="Left">
|
||||
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
|
||||
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
|
||||
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
|
||||
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,2,0,2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="TabStripPlacement" Value="Right">
|
||||
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
|
||||
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
|
||||
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
|
||||
<Setter Property="Margin" TargetName="HeaderPanel" Value="0,2,2,2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="false">
|
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
|
||||
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
19
Torch.Server/Themes/Light Theme Animated.xaml
Normal file
19
Torch.Server/Themes/Light Theme Animated.xaml
Normal file
@@ -0,0 +1,19 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
|
||||
<!-- Accent and AppTheme setting -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedSingleRowTabControl.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
|
||||
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
85
Torch.Server/Themes/Light Theme.xaml
Normal file
85
Torch.Server/Themes/Light Theme.xaml
Normal file
@@ -0,0 +1,85 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
|
||||
<!-- Accent and AppTheme setting -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="TabControl">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TabControl">
|
||||
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="ColumnDefinition0"/>
|
||||
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
|
||||
<RowDefinition x:Name="RowDefinition1" Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" Orientation="Horizontal"/>
|
||||
<Border x:Name="ContentPanel" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
|
||||
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
</Border>
|
||||
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
|
||||
<TabPanel x:Name="HeaderPanel2"
|
||||
Panel.ZIndex ="1"
|
||||
KeyboardNavigation.TabIndex="1"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Margin="2,2,2,0"
|
||||
IsItemsHost="true"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="TabStripPlacement" Value="Bottom">
|
||||
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
|
||||
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
|
||||
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,0,2,2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="TabStripPlacement" Value="Left">
|
||||
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
|
||||
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
|
||||
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
|
||||
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,2,0,2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="TabStripPlacement" Value="Right">
|
||||
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
|
||||
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
|
||||
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
|
||||
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
|
||||
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
|
||||
<Setter Property="Margin" TargetName="HeaderPanel" Value="0,2,2,2"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="false">
|
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Hyperlink" x:Name="LegalshtuffLink">
|
||||
<Setter Property="NavigateUri" Value="https://github.com/MahApps/MahApps.Metro/blob/develop/LICENSE" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
@@ -15,6 +15,21 @@
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -44,11 +59,17 @@
|
||||
<ApplicationIcon>torchicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ControlzEx, Version=3.0.2.4, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ControlzEx.3.0.2.4\lib\net45\ControlzEx.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HavokWrapper, Version=1.0.6051.28726, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MahApps.Metro, Version=1.6.1.4, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MahApps.Metro.1.6.1\lib\net45\MahApps.Metro.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CodeAnalysis, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.dll</HintPath>
|
||||
@@ -96,18 +117,20 @@
|
||||
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="SteamSDK, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\GameBinaries\SteamSDK.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
<Reference Include="Steamworks.NET">
|
||||
<HintPath>..\GameBinaries\Steamworks.NET.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -182,6 +205,10 @@
|
||||
<HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Steam">
|
||||
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
@@ -190,12 +217,18 @@
|
||||
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||
<Link>Properties\AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Commands\WhitelistCommands.cs" />
|
||||
<Compile Include="FlowDocumentTarget.cs" />
|
||||
<Compile Include="ListBoxExtensions.cs" />
|
||||
<Compile Include="Managers\EntityControlManager.cs" />
|
||||
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
||||
<Compile Include="Managers\InstanceManager.cs" />
|
||||
<Compile Include="Managers\MultiplayerManagerDedicatedEventShim.cs" />
|
||||
<Compile Include="Managers\MultiplayerManagerDedicatedPatchShim.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Initializer.cs" />
|
||||
<Compile Include="Properties\Annotations.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServerStatistics.cs" />
|
||||
<Compile Include="TorchConfig.cs" />
|
||||
<Compile Include="TorchService.cs">
|
||||
<SubType>Component</SubType>
|
||||
@@ -204,11 +237,32 @@
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
|
||||
<Compile Include="ViewModels\CheckpointViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
|
||||
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\CharacterViewModel.cs" />
|
||||
<Compile Include="ViewModels\ConfigDedicatedViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\EntityControlViewModel.cs" />
|
||||
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
|
||||
<Compile Include="Views\Converters\DefinitionToIdConverter.cs" />
|
||||
<Compile Include="Views\Converters\BooleanAndConverter.cs" />
|
||||
<Compile Include="Views\Converters\ListConverter.cs" />
|
||||
<Compile Include="MultiTextWriter.cs" />
|
||||
<Compile Include="RichTextBoxWriter.cs" />
|
||||
<Compile Include="Views\Entities\CharacterView.xaml.cs">
|
||||
<DependentUpon>CharacterView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\ThemeControl.xaml.cs">
|
||||
<DependentUpon>ThemeControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\ValidationRules\ListConverterValidationRule.cs" />
|
||||
<Compile Include="Views\Entities\EntityControlHost.xaml.cs">
|
||||
<DependentUpon>EntityControlHost.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Entities\EntityControlsView.xaml.cs">
|
||||
<DependentUpon>EntityControlsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ViewModels\EntityTreeViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\EntityViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\FloatingObjectViewModel.cs" />
|
||||
@@ -216,7 +270,6 @@
|
||||
<Compile Include="ViewModels\ILazyLoad.cs" />
|
||||
<Compile Include="ViewModels\PluginManagerViewModel.cs" />
|
||||
<Compile Include="ViewModels\PluginViewModel.cs" />
|
||||
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
|
||||
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
|
||||
<Compile Include="ViewModels\SteamUserViewModel.cs" />
|
||||
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
||||
@@ -247,15 +300,15 @@
|
||||
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
|
||||
<DependentUpon>VoxelMapView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\FirstTimeSetup.xaml.cs">
|
||||
<DependentUpon>FirstTimeSetup.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\ModsControl.xaml.cs">
|
||||
<DependentUpon>ModsControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\PluginsControl.xaml.cs">
|
||||
<DependentUpon>PluginsControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SessionSettingsView.xaml.cs">
|
||||
<DependentUpon>SessionSettingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\TorchUI.xaml.cs">
|
||||
<DependentUpon>TorchUI.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -264,6 +317,10 @@
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="TorchServer.cs" />
|
||||
<Compile Include="Views\ValidationRules\NumberValidationRule.cs" />
|
||||
<Compile Include="Views\WorldGeneratorDialog.xaml.cs">
|
||||
<DependentUpon>WorldGeneratorDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
@@ -300,6 +357,30 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="Themes\Dark Theme Animated.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Themes\Dark Theme.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Themes\Light Theme Animated.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Themes\Light Theme.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Entities\EntityControlHost.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Entities\EntityControlsView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\AddWorkshopItemsDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -324,26 +405,38 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Entities\CharacterView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Entities\GridView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\PluginsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Entities\VoxelMapView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\FirstTimeSetup.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\ModsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\PluginsControl.xaml">
|
||||
<Page Include="Views\Resources.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SessionSettingsView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\ThemeControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\TorchUI.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
@@ -352,6 +445,10 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\WorldGeneratorDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="torchicon.ico" />
|
||||
@@ -359,7 +456,18 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||
<PropertyGroup>
|
||||
|
@@ -60,8 +60,22 @@ namespace Torch.Server
|
||||
/// <inheritdoc />
|
||||
public List<string> Plugins { get; set; } = new List<string>();
|
||||
|
||||
public bool EnableWhitelist { get; set; } = false;
|
||||
public HashSet<ulong> Whitelist { get; set; } = new HashSet<ulong>();
|
||||
|
||||
internal Point WindowSize { get; set; } = new Point(800, 600);
|
||||
internal Point WindowPosition { get; set; } = new Point();
|
||||
|
||||
public string LastUsedTheme { get; set; } = "Torch Theme";
|
||||
|
||||
//TODO: REMOVE ME BY JULY 2019
|
||||
[Obsolete("Use vanilla reserved slot config")]
|
||||
public HashSet<ulong> ReservedPlayers { get; set; } = new HashSet<ulong>();
|
||||
|
||||
//Prevent reserved players being written to disk, but allow it to bre read
|
||||
//remove this when ReservedPlayers is removed
|
||||
private bool ShouldSerializeReservedPlayers() => false;
|
||||
|
||||
[XmlIgnore]
|
||||
private string _path;
|
||||
|
||||
|
@@ -1,35 +1,35 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Sandbox.Game;
|
||||
using Sandbox.Game.World;
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Xml.Serialization.GeneratedAssembly;
|
||||
using Sandbox.Engine.Analytics;
|
||||
using System.Windows.Forms;
|
||||
using NLog;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.ModAPI;
|
||||
using SteamSDK;
|
||||
using Sandbox.Game.World;
|
||||
using Torch.API;
|
||||
using Torch.Managers;
|
||||
using Torch.API.Managers;
|
||||
using Torch.API.Session;
|
||||
using Torch.Commands;
|
||||
using Torch.Mod;
|
||||
using Torch.Server.Commands;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Utils;
|
||||
using VRage;
|
||||
using VRage.Dedicated;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.Game.ObjectBuilder;
|
||||
using VRage.Game.SessionComponents;
|
||||
using VRage.Library;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.Plugins;
|
||||
using VRage.Utils;
|
||||
using VRage.Dedicated.RemoteAPI;
|
||||
using VRage.GameServices;
|
||||
using VRage.Steam;
|
||||
using Timer = System.Threading.Timer;
|
||||
|
||||
#endregion
|
||||
|
||||
#pragma warning disable 618
|
||||
|
||||
@@ -37,167 +37,100 @@ namespace Torch.Server
|
||||
{
|
||||
public class TorchServer : TorchBase, ITorchServer
|
||||
{
|
||||
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
||||
public float SimulationRatio { get => _simRatio; set { _simRatio = value; OnPropertyChanged(); } }
|
||||
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } }
|
||||
public Thread GameThread { get; private set; }
|
||||
public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
|
||||
public bool IsRunning { get => _isRunning; set { _isRunning = value; OnPropertyChanged(); } }
|
||||
public InstanceManager DedicatedInstance { get; }
|
||||
/// <inheritdoc />
|
||||
public string InstanceName => Config?.InstanceName;
|
||||
/// <inheritdoc />
|
||||
public string InstancePath => Config?.InstancePath;
|
||||
|
||||
private bool _isRunning;
|
||||
private ServerState _state;
|
||||
private bool _canRun;
|
||||
private TimeSpan _elapsedPlayTime;
|
||||
private bool _hasRun;
|
||||
private bool _isRunning;
|
||||
private float _simRatio;
|
||||
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
||||
private Timer _watchdog;
|
||||
private ServerState _state;
|
||||
private Stopwatch _uptime;
|
||||
private Timer _watchdog;
|
||||
|
||||
public TorchServer(TorchConfig config = null)
|
||||
/// <inheritdoc />
|
||||
public TorchServer(TorchConfig config = null)
|
||||
{
|
||||
DedicatedInstance = new InstanceManager(this);
|
||||
AddManager(DedicatedInstance);
|
||||
AddManager(new EntityControlManager(this));
|
||||
Config = config ?? new TorchConfig();
|
||||
MyFakes.ENABLE_INFINARIO = false;
|
||||
|
||||
var sessionManager = Managers.GetManager<ITorchSessionManager>();
|
||||
sessionManager.AddFactory(x => new MultiplayerManagerDedicated(this));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public float SimulationRatio { get => _simRatio; set => SetValue(ref _simRatio, value); }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set => SetValue(ref _elapsedPlayTime, value); }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Thread GameThread { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsRunning { get => _isRunning; set => SetValue(ref _isRunning, value); }
|
||||
|
||||
public bool CanRun { get => _canRun; set => SetValue(ref _canRun, value); }
|
||||
|
||||
/// <inheritdoc />
|
||||
public InstanceManager DedicatedInstance { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string InstanceName => Config?.InstanceName;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override uint SteamAppId => 244850;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string SteamAppName => "SpaceEngineersDedicated";
|
||||
|
||||
/// <inheritdoc />
|
||||
public ServerState State { get => _state; private set => SetValue(ref _state, value); }
|
||||
|
||||
public event Action<ITorchServer> Initialized;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string InstancePath => Config?.InstancePath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
||||
Log.Info("Initializing server");
|
||||
MySandboxGame.IsDedicated = true;
|
||||
base.Init();
|
||||
|
||||
MyPerGameSettings.SendLogToKeen = false;
|
||||
MyPerServerSettings.GameName = MyPerGameSettings.GameName;
|
||||
MyPerServerSettings.GameNameSafe = MyPerGameSettings.GameNameSafe;
|
||||
MyPerServerSettings.GameDSName = MyPerServerSettings.GameNameSafe + "Dedicated";
|
||||
MyPerServerSettings.GameDSDescription = "Your place for space engineering, destruction and exploring.";
|
||||
MySessionComponentExtDebug.ForceDisable = true;
|
||||
MyPerServerSettings.AppId = 244850;
|
||||
MyFinalBuildConstants.APP_VERSION = MyPerGameSettings.BasicGameInfo.GameVersion;
|
||||
InvokeBeforeRun();
|
||||
|
||||
//MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
|
||||
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
||||
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
|
||||
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
|
||||
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
|
||||
MyPlugins.Load();
|
||||
MyGlobalTypeMetadata.Static.Init();
|
||||
|
||||
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
|
||||
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
||||
Plugins.LoadPlugins();
|
||||
CanRun = true;
|
||||
Initialized?.Invoke(this);
|
||||
Log.Info($"Initialized server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
||||
}
|
||||
|
||||
private void InvokeBeforeRun()
|
||||
{
|
||||
MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING);
|
||||
MySandboxGame.Log.WriteLine("Steam build: Always true");
|
||||
MySandboxGame.Log.WriteLine("Environment.ProcessorCount: " + MyEnvironment.ProcessorCount);
|
||||
//MySandboxGame.Log.WriteLine("Environment.OSVersion: " + GetOsName());
|
||||
MySandboxGame.Log.WriteLine("Environment.CommandLine: " + Environment.CommandLine);
|
||||
MySandboxGame.Log.WriteLine("Environment.Is64BitProcess: " + MyEnvironment.Is64BitProcess);
|
||||
MySandboxGame.Log.WriteLine("Environment.Is64BitOperatingSystem: " + Environment.Is64BitOperatingSystem);
|
||||
//MySandboxGame.Log.WriteLine("Environment.Version: " + GetNETFromRegistry());
|
||||
MySandboxGame.Log.WriteLine("Environment.CurrentDirectory: " + Environment.CurrentDirectory);
|
||||
MySandboxGame.Log.WriteLine("MainAssembly.ProcessorArchitecture: " + Assembly.GetExecutingAssembly().GetArchitecture());
|
||||
MySandboxGame.Log.WriteLine("ExecutingAssembly.ProcessorArchitecture: " + MyFileSystem.MainAssembly.GetArchitecture());
|
||||
MySandboxGame.Log.WriteLine("IntPtr.Size: " + IntPtr.Size);
|
||||
MySandboxGame.Log.WriteLine("Default Culture: " + CultureInfo.CurrentCulture.Name);
|
||||
MySandboxGame.Log.WriteLine("Default UI Culture: " + CultureInfo.CurrentUICulture.Name);
|
||||
MySandboxGame.Log.WriteLine("IsAdmin: " + new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator));
|
||||
|
||||
MyLog.Default = MySandboxGame.Log;
|
||||
|
||||
Thread.CurrentThread.Name = "Main thread";
|
||||
|
||||
//Because we want exceptions from users to be in english
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
|
||||
|
||||
MySandboxGame.Config = new MyConfig("SpaceEngineers-Dedicated.cfg");
|
||||
MySandboxGame.Config.Load();
|
||||
}
|
||||
|
||||
[ReflectedStaticMethod(Type = typeof(DedicatedServer), Name = "RunInternal")]
|
||||
private static Action _dsRunInternal;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Start()
|
||||
{
|
||||
if (State != ServerState.Stopped)
|
||||
return;
|
||||
|
||||
DedicatedInstance.SaveConfig();
|
||||
_uptime = Stopwatch.StartNew();
|
||||
IsRunning = true;
|
||||
GameThread = Thread.CurrentThread;
|
||||
if (_hasRun)
|
||||
{
|
||||
Restart();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ServerState.Starting;
|
||||
IsRunning = true;
|
||||
CanRun = false;
|
||||
_hasRun = true;
|
||||
Log.Info("Starting server.");
|
||||
|
||||
MySandboxGame.IsDedicated = true;
|
||||
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
|
||||
|
||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
||||
|
||||
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
|
||||
if (MySandboxGame.ConfigDedicated.RemoteApiEnabled && !string.IsNullOrEmpty(MySandboxGame.ConfigDedicated.RemoteSecurityKey))
|
||||
{
|
||||
var myRemoteServer = new MyRemoteServer(MySandboxGame.ConfigDedicated.RemoteApiPort, MySandboxGame.ConfigDedicated.RemoteSecurityKey);
|
||||
Log.Info($"Remote API started on port {myRemoteServer.Port}");
|
||||
}
|
||||
|
||||
_uptime = Stopwatch.StartNew();
|
||||
base.Start();
|
||||
// Stops RunInternal from calling MyFileSystem.InitUserSpecific(null), we call it in InstanceManager.
|
||||
MySandboxGame.IsReloading = true;
|
||||
try
|
||||
{
|
||||
_dsRunInternal.Invoke();
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
// Makes log formatting a little nicer.
|
||||
throw e.InnerException ?? e;
|
||||
}
|
||||
|
||||
MySandboxGame.Log.Close();
|
||||
State = ServerState.Stopped;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init(object gameInstance)
|
||||
{
|
||||
base.Init(gameInstance);
|
||||
State = ServerState.Running;
|
||||
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
SimulationRatio = Sync.ServerSimulationRatio;
|
||||
var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds));
|
||||
ElapsedPlayTime = elapsed;
|
||||
|
||||
if (_watchdog == null && Config.TickTimeout > 0)
|
||||
{
|
||||
Log.Info("Starting server watchdog.");
|
||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(Config.TickTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckServerResponding(object state)
|
||||
{
|
||||
var mre = new ManualResetEvent(false);
|
||||
((TorchServer)state).Invoke(() => mre.Set());
|
||||
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
||||
{
|
||||
var mainThread = MySandboxGame.Static.UpdateThread;
|
||||
if (mainThread.IsAlive)
|
||||
mainThread.Suspend();
|
||||
var stackTrace = new StackTrace(mainThread, true);
|
||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.\n{stackTrace}");
|
||||
}
|
||||
|
||||
Log.Debug("Server watchdog responded");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -205,71 +138,161 @@ namespace Torch.Server
|
||||
{
|
||||
if (State == ServerState.Stopped)
|
||||
Log.Error("Server is already stopped");
|
||||
|
||||
if (Thread.CurrentThread != MySandboxGame.Static.UpdateThread)
|
||||
{
|
||||
Log.Debug("Invoking server stop on game thread.");
|
||||
Invoke(Stop);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Stopping server.");
|
||||
|
||||
//Unload all the static junk.
|
||||
//TODO: Finish unloading all server data so it's in a completely clean state.
|
||||
MySandboxGame.Static.Exit();
|
||||
|
||||
base.Stop();
|
||||
Log.Info("Server stopped.");
|
||||
_stopHandle.Set();
|
||||
|
||||
State = ServerState.Stopped;
|
||||
IsRunning = false;
|
||||
CanRun = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart the program. DOES NOT SAVE!
|
||||
/// Restart the program.
|
||||
/// </summary>
|
||||
public override void Restart()
|
||||
{
|
||||
var exe = Assembly.GetExecutingAssembly().Location;
|
||||
((TorchConfig)Config).WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||
Process.Start(exe, Config.ToString());
|
||||
Environment.Exit(0);
|
||||
}
|
||||
if (IsRunning)
|
||||
Save().ContinueWith(DoRestart, this, TaskContinuationOptions.RunContinuationsAsynchronously);
|
||||
else
|
||||
DoRestart(null, this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task Save(long callerId)
|
||||
{
|
||||
return SaveGameAsync(statusCode => SaveCompleted(statusCode, callerId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for when save has finished.
|
||||
/// </summary>
|
||||
/// <param name="statusCode">Return code of the save operation</param>
|
||||
/// <param name="callerId">Caller of the save operation</param>
|
||||
private void SaveCompleted(SaveGameStatus statusCode, long callerId = 0)
|
||||
{
|
||||
switch (statusCode)
|
||||
void DoRestart(Task<GameSaveResult> task, object torch0)
|
||||
{
|
||||
case SaveGameStatus.Success:
|
||||
Log.Info("Save completed.");
|
||||
Multiplayer.SendMessage("Saved game.", playerId: callerId);
|
||||
break;
|
||||
case SaveGameStatus.SaveInProgress:
|
||||
Log.Error("Save failed, a save is already in progress.");
|
||||
Multiplayer.SendMessage("Save failed, a save is already in progress.", playerId: callerId, font: MyFontEnum.Red);
|
||||
break;
|
||||
case SaveGameStatus.GameNotReady:
|
||||
Log.Error("Save failed, game was not ready.");
|
||||
Multiplayer.SendMessage("Save failed, game was not ready.", playerId: callerId, font: MyFontEnum.Red);
|
||||
break;
|
||||
case SaveGameStatus.TimedOut:
|
||||
Log.Error("Save failed, save timed out.");
|
||||
Multiplayer.SendMessage("Save failed, save timed out.", playerId: callerId, font: MyFontEnum.Red);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
var torch = (TorchServer)torch0;
|
||||
torch.Stop();
|
||||
// TODO clone this
|
||||
var config = (TorchConfig)torch.Config;
|
||||
LogManager.Flush();
|
||||
|
||||
string exe = Assembly.GetExecutingAssembly().Location;
|
||||
Debug.Assert(exe != null);
|
||||
config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||
config.Autostart = true;
|
||||
Process.Start(exe, config.ToString());
|
||||
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
|
||||
{
|
||||
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded)
|
||||
{
|
||||
_watchdog?.Dispose();
|
||||
_watchdog = null;
|
||||
ModCommunication.Unregister();
|
||||
}
|
||||
|
||||
if (newState == TorchSessionState.Loaded)
|
||||
{
|
||||
CurrentSession.Managers.GetManager<CommandManager>().RegisterCommandModule(typeof(WhitelistCommands));
|
||||
ModCommunication.Register();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init(object gameInstance)
|
||||
{
|
||||
base.Init(gameInstance);
|
||||
var game = gameInstance as MySandboxGame;
|
||||
if (game != null && MySession.Static != null)
|
||||
State = ServerState.Running;
|
||||
else
|
||||
State = ServerState.Stopped;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
// Stops 1.00-1.02 flicker.
|
||||
SimulationRatio = Math.Min(Sync.ServerSimulationRatio, 1);
|
||||
var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds));
|
||||
ElapsedPlayTime = elapsed;
|
||||
|
||||
if (_watchdog == null && Config.TickTimeout > 0)
|
||||
{
|
||||
Log.Info("Starting server watchdog.");
|
||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero,
|
||||
TimeSpan.FromSeconds(Config.TickTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
#region Freeze Detection
|
||||
|
||||
private static void CheckServerResponding(object state)
|
||||
{
|
||||
var mre = new ManualResetEvent(false);
|
||||
((TorchServer)state).Invoke(() => mre.Set());
|
||||
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
||||
{
|
||||
#if DEBUG
|
||||
Log.Error(
|
||||
$"Server watchdog detected that the server was frozen for at least {((TorchServer) state).Config.TickTimeout} seconds.");
|
||||
Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread));
|
||||
#else
|
||||
Log.Error(DumpFrozenThread(MySandboxGame.Static.UpdateThread));
|
||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {((TorchServer)state).Config.TickTimeout} seconds.");
|
||||
#endif
|
||||
}
|
||||
|
||||
Log.Debug("Server watchdog responded");
|
||||
}
|
||||
|
||||
private static string DumpFrozenThread(Thread thread, int traces = 3, int pause = 5000)
|
||||
{
|
||||
var stacks = new List<string>(traces);
|
||||
var totalSize = 0;
|
||||
for (var i = 0; i < traces; i++)
|
||||
{
|
||||
string dump = DumpStack(thread).ToString();
|
||||
totalSize += dump.Length;
|
||||
stacks.Add(dump);
|
||||
Thread.Sleep(pause);
|
||||
}
|
||||
|
||||
string commonPrefix = StringUtils.CommonSuffix(stacks);
|
||||
// Advance prefix to include the line terminator.
|
||||
commonPrefix = commonPrefix.Substring(commonPrefix.IndexOf('\n') + 1);
|
||||
|
||||
var result = new StringBuilder(totalSize - (stacks.Count - 1) * commonPrefix.Length);
|
||||
result.AppendLine($"Frozen thread dump {thread.Name}");
|
||||
result.AppendLine("Common prefix:").AppendLine(commonPrefix);
|
||||
for (var i = 0; i < stacks.Count; i++)
|
||||
if (stacks[i].Length > commonPrefix.Length)
|
||||
{
|
||||
result.AppendLine($"Suffix {i}");
|
||||
result.AppendLine(stacks[i].Substring(0, stacks[i].Length - commonPrefix.Length));
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private static StackTrace DumpStack(Thread thread)
|
||||
{
|
||||
try
|
||||
{
|
||||
thread.Suspend();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
var stack = new StackTrace(thread, true);
|
||||
try
|
||||
{
|
||||
thread.Resume();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
132
Torch.Server/ViewModels/CheckpointViewModel.cs
Normal file
132
Torch.Server/ViewModels/CheckpointViewModel.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Torch.Collections;
|
||||
using VRage;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.Serialization;
|
||||
using VRageMath;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class CheckpointViewModel : ViewModel
|
||||
{
|
||||
private MyObjectBuilder_Checkpoint _checkpoint;
|
||||
private SessionSettingsViewModel _sessionSettings;
|
||||
|
||||
public CheckpointViewModel(MyObjectBuilder_Checkpoint checkpoint)
|
||||
{
|
||||
_checkpoint = checkpoint;
|
||||
_sessionSettings = new SessionSettingsViewModel(_checkpoint.Settings);
|
||||
}
|
||||
|
||||
public static implicit operator MyObjectBuilder_Checkpoint(CheckpointViewModel model)
|
||||
{
|
||||
return model._checkpoint;
|
||||
}
|
||||
|
||||
public Vector3I CurrentSector { get => _checkpoint.CurrentSector; set => SetValue(ref _checkpoint.CurrentSector, value); }
|
||||
|
||||
public long ElapsedGameTime { get => _checkpoint.ElapsedGameTime; set => SetValue(ref _checkpoint.ElapsedGameTime, value); }
|
||||
|
||||
public string SessionName { get => _checkpoint.SessionName; set => SetValue(ref _checkpoint.SessionName, value); }
|
||||
|
||||
public MyPositionAndOrientation SpectatorPosition { get => _checkpoint.SpectatorPosition; set => SetValue(ref _checkpoint.SpectatorPosition, value); }
|
||||
|
||||
public bool SpectatorIsLightOn { get => _checkpoint.SpectatorIsLightOn; set => SetValue(ref _checkpoint.SpectatorIsLightOn, value); }
|
||||
|
||||
public MyCameraControllerEnum CameraController { get => _checkpoint.CameraController; set => SetValue(ref _checkpoint.CameraController, value); }
|
||||
|
||||
public long CameraEntity { get => _checkpoint.CameraEntity; set => SetValue(ref _checkpoint.CameraEntity, value); }
|
||||
|
||||
public long ControlledObject { get => _checkpoint.ControlledObject; set => SetValue(ref _checkpoint.ControlledObject, value); }
|
||||
|
||||
public string Password { get => _checkpoint.Password; set => SetValue(ref _checkpoint.Password, value); }
|
||||
|
||||
public string Description { get => _checkpoint.Description; set => SetValue(ref _checkpoint.Description, value); }
|
||||
|
||||
public DateTime LastSaveTime { get => _checkpoint.LastSaveTime; set => SetValue(ref _checkpoint.LastSaveTime, value); }
|
||||
|
||||
public float SpectatorDistance { get => _checkpoint.SpectatorDistance; set => SetValue(ref _checkpoint.SpectatorDistance, value); }
|
||||
|
||||
public ulong? WorkshopId { get => _checkpoint.WorkshopId; set => SetValue(ref _checkpoint.WorkshopId, value); }
|
||||
|
||||
public MyObjectBuilder_Toolbar CharacterToolbar { get => _checkpoint.CharacterToolbar; set => SetValue(ref _checkpoint.CharacterToolbar, value); }
|
||||
|
||||
public SerializableDictionary<long, MyObjectBuilder_Checkpoint.PlayerId> ControlledEntities { get => _checkpoint.ControlledEntities; set => SetValue(ref _checkpoint.ControlledEntities, value); }
|
||||
|
||||
public SessionSettingsViewModel Settings
|
||||
{
|
||||
get => _sessionSettings;
|
||||
set
|
||||
{
|
||||
SetValue(ref _sessionSettings, value);
|
||||
_checkpoint.Settings = _sessionSettings;
|
||||
}
|
||||
}
|
||||
|
||||
public MyObjectBuilder_ScriptManager ScriptManagerData => throw new NotImplementedException();
|
||||
|
||||
public int AppVersion { get => _checkpoint.AppVersion; set => SetValue(ref _checkpoint.AppVersion, value); }
|
||||
|
||||
public MyObjectBuilder_FactionCollection Factions => throw new NotImplementedException();
|
||||
|
||||
public List<MyObjectBuilder_Checkpoint.ModItem> Mods { get => _checkpoint.Mods; set => SetValue(ref _checkpoint.Mods, value); }
|
||||
|
||||
public SerializableDictionary<ulong, MyPromoteLevel> PromotedUsers { get => _checkpoint.PromotedUsers; set => SetValue(ref _checkpoint.PromotedUsers, value); }
|
||||
|
||||
public HashSet<ulong> CreativeTools { get => _checkpoint.CreativeTools; set => SetValue(ref _checkpoint.CreativeTools, value); }
|
||||
|
||||
public SerializableDefinitionId Scenario { get => _checkpoint.Scenario; set => SetValue(ref _checkpoint.Scenario, value); }
|
||||
|
||||
public List<MyObjectBuilder_Checkpoint.RespawnCooldownItem> RespawnCooldowns { get => _checkpoint.RespawnCooldowns; set => SetValue(ref _checkpoint.RespawnCooldowns, value); }
|
||||
|
||||
public List<MyObjectBuilder_Identity> Identities { get => _checkpoint.Identities; set => SetValue(ref _checkpoint.Identities, value); }
|
||||
|
||||
public List<MyObjectBuilder_Client> Clients { get => _checkpoint.Clients; set => SetValue(ref _checkpoint.Clients, value); }
|
||||
|
||||
public MyEnvironmentHostilityEnum? PreviousEnvironmentHostility { get => _checkpoint.PreviousEnvironmentHostility; set => SetValue(ref _checkpoint.PreviousEnvironmentHostility, value); }
|
||||
|
||||
public SerializableDictionary<MyObjectBuilder_Checkpoint.PlayerId, MyObjectBuilder_Player> AllPlayersData { get => _checkpoint.AllPlayersData; set => SetValue(ref _checkpoint.AllPlayersData, value); }
|
||||
|
||||
public SerializableDictionary<MyObjectBuilder_Checkpoint.PlayerId, List<Vector3>> AllPlayersColors { get => _checkpoint.AllPlayersColors; set => SetValue(ref _checkpoint.AllPlayersColors, value); }
|
||||
|
||||
public List<MyObjectBuilder_ChatHistory> ChatHistory { get => _checkpoint.ChatHistory; set => SetValue(ref _checkpoint.ChatHistory, value); }
|
||||
|
||||
public List<MyObjectBuilder_FactionChatHistory> FactionChatHistory { get => _checkpoint.FactionChatHistory; set => SetValue(ref _checkpoint.FactionChatHistory, value); }
|
||||
|
||||
public List<long> NonPlayerIdentities { get => _checkpoint.NonPlayerIdentities; set => SetValue(ref _checkpoint.NonPlayerIdentities, value); }
|
||||
|
||||
public SerializableDictionary<long, MyObjectBuilder_Gps> Gps { get => _checkpoint.Gps; set => SetValue(ref _checkpoint.Gps, value); }
|
||||
|
||||
public SerializableBoundingBoxD? WorldBoundaries { get => _checkpoint.WorldBoundaries; set => SetValue(ref _checkpoint.WorldBoundaries, value); }
|
||||
|
||||
public List<MyObjectBuilder_SessionComponent> SessionComponents { get => _checkpoint.SessionComponents; set => SetValue(ref _checkpoint.SessionComponents, value); }
|
||||
|
||||
public SerializableDefinitionId GameDefinition { get => _checkpoint.GameDefinition; set => SetValue(ref _checkpoint.GameDefinition, value); }
|
||||
|
||||
public HashSet<string> SessionComponentEnabled { get => _checkpoint.SessionComponentEnabled; set => SetValue(ref _checkpoint.SessionComponentEnabled, value); }
|
||||
|
||||
public HashSet<string> SessionComponentDisabled { get => _checkpoint.SessionComponentDisabled; set => SetValue(ref _checkpoint.SessionComponentDisabled, value); }
|
||||
|
||||
public DateTime InGameTime { get => _checkpoint.InGameTime; set => SetValue(ref _checkpoint.InGameTime, value); }
|
||||
|
||||
public MyObjectBuilder_SessionComponentMission MissionTriggers { get => _checkpoint.MissionTriggers; set => SetValue(ref _checkpoint.MissionTriggers, value); }
|
||||
|
||||
public string Briefing { get => _checkpoint.Briefing; set => SetValue(ref _checkpoint.Briefing, value); }
|
||||
|
||||
public string BriefingVideo { get => _checkpoint.BriefingVideo; set => SetValue(ref _checkpoint.BriefingVideo, value); }
|
||||
|
||||
public string CustomLoadingScreenImage { get => _checkpoint.CustomLoadingScreenImage; set => SetValue(ref _checkpoint.BriefingVideo, value); }
|
||||
|
||||
public string CustomLoadingScreenText { get => _checkpoint.CustomLoadingScreenText; set => SetValue(ref _checkpoint.CustomLoadingScreenText, value); }
|
||||
|
||||
public string CustomSkybox { get => _checkpoint.CustomSkybox; set => SetValue(ref _checkpoint.CustomSkybox, value); }
|
||||
|
||||
public int RequiresDX { get => _checkpoint.RequiresDX; set => SetValue(ref _checkpoint.RequiresDX, value); }
|
||||
}
|
||||
}
|
@@ -6,6 +6,8 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Torch.Collections;
|
||||
using Torch.Server.Managers;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ModAPI;
|
||||
|
||||
@@ -13,7 +15,7 @@ namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class ConfigDedicatedViewModel : ViewModel
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetLogger("Config");
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Model => _config;
|
||||
|
||||
@@ -25,111 +27,110 @@ namespace Torch.Server.ViewModels
|
||||
public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated)
|
||||
{
|
||||
_config = configDedicated;
|
||||
_config.IgnoreLastSession = true;
|
||||
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
|
||||
Administrators = string.Join(Environment.NewLine, _config.Administrators);
|
||||
Banned = string.Join(Environment.NewLine, _config.Banned);
|
||||
Mods = string.Join(Environment.NewLine, _config.Mods);
|
||||
}
|
||||
|
||||
public void Save(string path = null)
|
||||
{
|
||||
var newline = new [] {Environment.NewLine};
|
||||
Validate();
|
||||
|
||||
_config.Administrators.Clear();
|
||||
foreach (var admin in Administrators.Split(newline, StringSplitOptions.RemoveEmptyEntries))
|
||||
_config.Administrators.Add(admin);
|
||||
_config.SessionSettings = _sessionSettings;
|
||||
// Never ever
|
||||
_config.IgnoreLastSession = true;
|
||||
_config.Save(path);
|
||||
}
|
||||
|
||||
_config.Banned.Clear();
|
||||
foreach (var banned in Banned.Split(newline, StringSplitOptions.RemoveEmptyEntries))
|
||||
_config.Banned.Add(ulong.Parse(banned));
|
||||
|
||||
_config.Mods.Clear();
|
||||
foreach (var mod in Mods.Split(newline, StringSplitOptions.RemoveEmptyEntries))
|
||||
public bool Validate()
|
||||
{
|
||||
if (SelectedWorld == null)
|
||||
{
|
||||
if (ulong.TryParse(mod, out ulong modId))
|
||||
_config.Mods.Add(modId);
|
||||
else
|
||||
Log.Warn($"'{mod}' is not a valid mod ID.");
|
||||
Log.Warn($"{nameof(SelectedWorld)} == null");
|
||||
return false;
|
||||
}
|
||||
|
||||
_config.Save(path);
|
||||
if (LoadWorld == null)
|
||||
{
|
||||
Log.Warn($"{nameof(LoadWorld)} == null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private SessionSettingsViewModel _sessionSettings;
|
||||
public SessionSettingsViewModel SessionSettings { get => _sessionSettings; set { _sessionSettings = value; OnPropertyChanged(); } }
|
||||
|
||||
public ObservableList<string> WorldPaths { get; } = new ObservableList<string>();
|
||||
private string _administrators;
|
||||
public string Administrators { get => _administrators; set { _administrators = value; OnPropertyChanged(); } }
|
||||
private string _banned;
|
||||
public string Banned { get => _banned; set { _banned = value; OnPropertyChanged(); } }
|
||||
private string _mods;
|
||||
public string Mods { get => _mods; set { _mods = value; OnPropertyChanged(); } }
|
||||
|
||||
public int AsteroidAmount
|
||||
public MtObservableList<WorldViewModel> Worlds { get; } = new MtObservableList<WorldViewModel>();
|
||||
private WorldViewModel _selectedWorld;
|
||||
public WorldViewModel SelectedWorld
|
||||
{
|
||||
get { return _config.AsteroidAmount; }
|
||||
set { _config.AsteroidAmount = value; OnPropertyChanged(); }
|
||||
get => _selectedWorld;
|
||||
set
|
||||
{
|
||||
SetValue(ref _selectedWorld, value);
|
||||
LoadWorld = _selectedWorld?.WorldPath;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GroupId
|
||||
{
|
||||
get { return _config.GroupID; }
|
||||
set { _config.GroupID = value; OnPropertyChanged(); }
|
||||
}
|
||||
public List<string> Administrators { get => _config.Administrators; set => SetValue(x => _config.Administrators = x, value); }
|
||||
|
||||
public bool IgnoreLastSession
|
||||
{
|
||||
get { return _config.IgnoreLastSession; }
|
||||
set { _config.IgnoreLastSession = value; OnPropertyChanged(); }
|
||||
}
|
||||
public List<ulong> Banned { get => _config.Banned; set => SetValue(x => _config.Banned = x, value); }
|
||||
|
||||
public string IP
|
||||
{
|
||||
get { return _config.IP; }
|
||||
set { _config.IP = value; OnPropertyChanged(); }
|
||||
}
|
||||
public List<ulong> Reserved { get => _config.Reserved; set => SetValue(x => _config.Reserved = x, value); }
|
||||
|
||||
public int Port
|
||||
{
|
||||
get { return _config.ServerPort; }
|
||||
set { _config.ServerPort = value; OnPropertyChanged(); }
|
||||
}
|
||||
private List<ulong> _mods = new List<ulong>();
|
||||
public List<ulong> Mods { get => _mods; set => SetValue(x => _mods = x, value); }
|
||||
|
||||
public string ServerName
|
||||
{
|
||||
get { return _config.ServerName; }
|
||||
set { _config.ServerName = value; OnPropertyChanged(); }
|
||||
}
|
||||
public int AsteroidAmount { get => _config.AsteroidAmount; set => SetValue(x => _config.AsteroidAmount = x, value); }
|
||||
|
||||
public bool PauseGameWhenEmpty
|
||||
{
|
||||
get { return _config.PauseGameWhenEmpty; }
|
||||
set { _config.PauseGameWhenEmpty = value; OnPropertyChanged(); }
|
||||
}
|
||||
public ulong GroupId { get => _config.GroupID; set => SetValue(x => _config.GroupID = x, value); }
|
||||
|
||||
public string PremadeCheckpointPath
|
||||
{
|
||||
get { return _config.PremadeCheckpointPath; }
|
||||
set { _config.PremadeCheckpointPath = value; OnPropertyChanged(); }
|
||||
}
|
||||
public string IP { get => _config.IP; set => SetValue(x => _config.IP = x, value); }
|
||||
|
||||
public string LoadWorld
|
||||
{
|
||||
get { return _config.LoadWorld; }
|
||||
set { _config.LoadWorld = value; OnPropertyChanged(); }
|
||||
}
|
||||
public int Port { get => _config.ServerPort; set => SetValue(x => _config.ServerPort = x, value); }
|
||||
|
||||
public int SteamPort
|
||||
{
|
||||
get { return _config.SteamPort; }
|
||||
set { _config.SteamPort = value; OnPropertyChanged(); }
|
||||
}
|
||||
public string ServerName { get => _config.ServerName; set => SetValue(x => _config.ServerName = x, value); }
|
||||
|
||||
public string WorldName
|
||||
public string ServerDescription { get => _config.ServerDescription; set => SetValue(x => _config.ServerDescription = x, value); }
|
||||
|
||||
public bool PauseGameWhenEmpty { get => _config.PauseGameWhenEmpty; set => SetValue(x => _config.PauseGameWhenEmpty = x, value); }
|
||||
|
||||
public bool AutodetectDependencies { get => _config.AutodetectDependencies; set => SetValue(x => _config.AutodetectDependencies = x, value); }
|
||||
|
||||
public string PremadeCheckpointPath { get => _config.PremadeCheckpointPath; set => SetValue(x => _config.PremadeCheckpointPath = x, value); }
|
||||
|
||||
public string LoadWorld { get => _config.LoadWorld; set => SetValue(x => _config.LoadWorld = x, value); }
|
||||
|
||||
public int SteamPort { get => _config.SteamPort; set => SetValue(x => _config.SteamPort = x, value); }
|
||||
|
||||
public string WorldName { get => _config.WorldName; set => SetValue(x => _config.WorldName = x, value); }
|
||||
|
||||
//this is a damn server password. I don't care if this is insecure. Bite me.
|
||||
private string _password;
|
||||
public string Password
|
||||
{
|
||||
get { return _config.WorldName; }
|
||||
set { _config.WorldName = value; OnPropertyChanged(); }
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_password))
|
||||
{
|
||||
if (string.IsNullOrEmpty(_config.ServerPasswordHash))
|
||||
return string.Empty;
|
||||
return "**********";
|
||||
}
|
||||
return _password;
|
||||
}
|
||||
set
|
||||
{
|
||||
_password = value;
|
||||
if(!string.IsNullOrEmpty(value))
|
||||
_config.SetPassword(value);
|
||||
else
|
||||
{
|
||||
_config.ServerPasswordHash = null;
|
||||
_config.ServerPasswordSalt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,16 +8,17 @@ using System.Threading.Tasks;
|
||||
using Sandbox.Game.Entities.Cube;
|
||||
using Sandbox.ModAPI;
|
||||
using Sandbox.ModAPI.Interfaces;
|
||||
using Torch.Collections;
|
||||
using Torch.Server.ViewModels.Entities;
|
||||
|
||||
namespace Torch.Server.ViewModels.Blocks
|
||||
{
|
||||
public class BlockViewModel : EntityViewModel
|
||||
{
|
||||
public IMyTerminalBlock Block { get; }
|
||||
public ObservableList<PropertyViewModel> Properties { get; } = new ObservableList<PropertyViewModel>();
|
||||
public IMyTerminalBlock Block => (IMyTerminalBlock) Entity;
|
||||
public MtObservableList<PropertyViewModel> Properties { get; } = new MtObservableList<PropertyViewModel>();
|
||||
|
||||
public string FullName => $"{Block.CubeGrid.CustomName} - {Block.CustomName}";
|
||||
public string FullName => $"{Block?.CubeGrid.CustomName} - {Block?.CustomName}";
|
||||
|
||||
public override string Name
|
||||
{
|
||||
@@ -37,7 +38,7 @@ namespace Torch.Server.ViewModels.Blocks
|
||||
|
||||
public long BuiltBy
|
||||
{
|
||||
get => ((MySlimBlock)Block.SlimBlock).BuiltBy;
|
||||
get => ((MySlimBlock)Block?.SlimBlock)?.BuiltBy ?? 0;
|
||||
set
|
||||
{
|
||||
TorchBase.Instance.Invoke(() =>
|
||||
@@ -58,7 +59,6 @@ namespace Torch.Server.ViewModels.Blocks
|
||||
|
||||
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
|
||||
{
|
||||
Block = block;
|
||||
if (Block == null)
|
||||
return;
|
||||
|
||||
|
@@ -6,7 +6,12 @@ namespace Torch.Server.ViewModels.Entities
|
||||
{
|
||||
public CharacterViewModel(MyCharacter character, EntityTreeViewModel tree) : base(character, tree)
|
||||
{
|
||||
|
||||
character.ControllerInfo.ControlAcquired += (x) => { OnPropertyChanged(nameof(Name)); };
|
||||
character.ControllerInfo.ControlReleased += (x) => { OnPropertyChanged(nameof(Name)); };
|
||||
}
|
||||
|
||||
public CharacterViewModel()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
Torch.Server/ViewModels/Entities/EntityControlViewModel.cs
Normal file
38
Torch.Server/ViewModels/Entities/EntityControlViewModel.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace Torch.Server.ViewModels.Entities
|
||||
{
|
||||
public class EntityControlViewModel : ViewModel
|
||||
{
|
||||
internal const string SignalPropertyInvalidateControl =
|
||||
"InvalidateControl-4124a476-704f-4762-8b5e-336a18e2f7e5";
|
||||
|
||||
internal void InvalidateControl()
|
||||
{
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(SignalPropertyInvalidateControl);
|
||||
}
|
||||
|
||||
private bool _hide;
|
||||
|
||||
/// <summary>
|
||||
/// Should this element be forced into the <see cref="Visibility.Collapsed"/>
|
||||
/// </summary>
|
||||
public bool Hide
|
||||
{
|
||||
get => _hide;
|
||||
protected set
|
||||
{
|
||||
if (_hide == value)
|
||||
return;
|
||||
_hide = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,10 @@
|
||||
using VRage.Game.ModAPI;
|
||||
using System.Windows.Controls;
|
||||
using Sandbox.Game.World;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Collections;
|
||||
using Torch.Server.Managers;
|
||||
using VRage.Game.Entity;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.ModAPI;
|
||||
using VRageMath;
|
||||
|
||||
@@ -7,28 +13,46 @@ namespace Torch.Server.ViewModels.Entities
|
||||
public class EntityViewModel : ViewModel
|
||||
{
|
||||
protected EntityTreeViewModel Tree { get; }
|
||||
public IMyEntity Entity { get; }
|
||||
|
||||
private IMyEntity _backing;
|
||||
public IMyEntity Entity
|
||||
{
|
||||
get => _backing;
|
||||
protected set
|
||||
{
|
||||
_backing = value;
|
||||
OnPropertyChanged();
|
||||
EntityControls = TorchBase.Instance?.Managers.GetManager<EntityControlManager>()?.BoundModels(this);
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
OnPropertyChanged(nameof(EntityControls));
|
||||
}
|
||||
}
|
||||
|
||||
public long Id => Entity.EntityId;
|
||||
|
||||
public MtObservableList<EntityControlViewModel> EntityControls { get; private set; }
|
||||
|
||||
public virtual string Name
|
||||
{
|
||||
get => Entity.DisplayName;
|
||||
get => Entity?.DisplayName ?? (Entity != null ? $"eid:{Entity.EntityId}" : "nil");
|
||||
set
|
||||
{
|
||||
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
|
||||
if (Entity!=null)
|
||||
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Position
|
||||
{
|
||||
get => Entity.GetPosition().ToString();
|
||||
get => Entity?.GetPosition().ToString();
|
||||
set
|
||||
{
|
||||
if (!Vector3D.TryParse(value, out Vector3D v))
|
||||
return;
|
||||
|
||||
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
|
||||
if (Entity != null)
|
||||
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
@@ -1,67 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Sandbox.Definitions;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Entities.Cube;
|
||||
using Sandbox.ModAPI;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Collections;
|
||||
using Torch.Server.ViewModels.Blocks;
|
||||
using VRage.Game;
|
||||
|
||||
namespace Torch.Server.ViewModels.Entities
|
||||
{
|
||||
public class GridViewModel : EntityViewModel, ILazyLoad
|
||||
{
|
||||
private MyCubeGrid Grid => (MyCubeGrid)Entity;
|
||||
public ObservableList<BlockViewModel> Blocks { get; } = new ObservableList<BlockViewModel>();
|
||||
private static readonly MyCubeBlockDefinition _fillerDefinition = new MyCubeBlockDefinition()
|
||||
{
|
||||
Id = new MyDefinitionId(typeof(MyObjectBuilder_DefinitionBase), "")
|
||||
};
|
||||
|
||||
private class CubeBlockDefinitionComparer : IComparer<MyCubeBlockDefinition>
|
||||
{
|
||||
public static readonly CubeBlockDefinitionComparer Default = new CubeBlockDefinitionComparer();
|
||||
|
||||
public int Compare(MyCubeBlockDefinition x, MyCubeBlockDefinition y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
return -1;
|
||||
if (y == null)
|
||||
return 1;
|
||||
if (ReferenceEquals(x, y))
|
||||
return 0;
|
||||
MyDefinitionId xi = x.Id;
|
||||
MyDefinitionId yi = y.Id;
|
||||
if (xi == yi)
|
||||
return 0;
|
||||
if (xi.TypeId != yi.TypeId)
|
||||
return string.CompareOrdinal(((Type) xi.TypeId).Name, ((Type) yi.TypeId).Name);
|
||||
return xi.SubtypeId == yi.SubtypeId ? 0 : string.CompareOrdinal(xi.SubtypeName, yi.SubtypeName);
|
||||
}
|
||||
}
|
||||
|
||||
private MyCubeGrid Grid => (MyCubeGrid) Entity;
|
||||
|
||||
public MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>
|
||||
Blocks { get; } =
|
||||
new MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>(
|
||||
CubeBlockDefinitionComparer.Default);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DescriptiveName { get; }
|
||||
|
||||
public GridViewModel() { }
|
||||
public GridViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
|
||||
{
|
||||
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
|
||||
Blocks.Add(new BlockViewModel(null, Tree));
|
||||
Blocks.Add(_fillerDefinition, new MtObservableSortedDictionary<long, BlockViewModel>());
|
||||
}
|
||||
|
||||
private void Grid_OnBlockRemoved(Sandbox.Game.Entities.Cube.MySlimBlock obj)
|
||||
{
|
||||
if (obj.FatBlock != null)
|
||||
Blocks.RemoveWhere(b => b.Block.EntityId == obj.FatBlock?.EntityId);
|
||||
RemoveBlock(obj.FatBlock);
|
||||
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
|
||||
private void RemoveBlock(MyCubeBlock block)
|
||||
{
|
||||
if (!Blocks.TryGetValue(block.BlockDefinition, out var group))
|
||||
return;
|
||||
if (group.Remove(block.EntityId) && group.Count == 0 && Blocks.Count > 1)
|
||||
Blocks.Remove(block.BlockDefinition);
|
||||
}
|
||||
|
||||
private void AddBlock(MyTerminalBlock block)
|
||||
{
|
||||
if (!Blocks.TryGetValue(block.BlockDefinition, out var group))
|
||||
group = Blocks[block.BlockDefinition] = new MtObservableSortedDictionary<long, BlockViewModel>();
|
||||
group.Add(block.EntityId, new BlockViewModel(block, Tree));
|
||||
}
|
||||
|
||||
private void Grid_OnBlockAdded(Sandbox.Game.Entities.Cube.MySlimBlock obj)
|
||||
{
|
||||
var block = obj.FatBlock as IMyTerminalBlock;
|
||||
var block = obj.FatBlock as MyTerminalBlock;
|
||||
if (block != null)
|
||||
Blocks.Insert(new BlockViewModel(block, Tree), b => b.Name);
|
||||
AddBlock(block);
|
||||
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
|
||||
private bool _load;
|
||||
|
||||
public void Load()
|
||||
{
|
||||
if (_load)
|
||||
return;
|
||||
|
||||
_load = true;
|
||||
Blocks.Clear();
|
||||
TorchBase.Instance.Invoke(() =>
|
||||
{
|
||||
foreach (var block in Grid.GetFatBlocks().Where(b => b is IMyTerminalBlock))
|
||||
{
|
||||
Blocks.Add(new BlockViewModel((IMyTerminalBlock)block, Tree));
|
||||
}
|
||||
Blocks.Clear();
|
||||
foreach (var block in Grid.GetFatBlocks().OfType<MyTerminalBlock>())
|
||||
AddBlock(block);
|
||||
|
||||
Grid.OnBlockAdded += Grid_OnBlockAdded;
|
||||
Grid.OnBlockRemoved += Grid_OnBlockRemoved;
|
||||
|
||||
Tree.ControlDispatcher.BeginInvoke(() =>
|
||||
{
|
||||
Blocks.Sort(b => b.Block.CustomName);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ using Sandbox.Game.Entities;
|
||||
using VRage.Game.Entity;
|
||||
using VRage.Game.ModAPI;
|
||||
using System.Threading.Tasks;
|
||||
using Torch.Collections;
|
||||
|
||||
namespace Torch.Server.ViewModels.Entities
|
||||
{
|
||||
@@ -15,7 +16,7 @@ namespace Torch.Server.ViewModels.Entities
|
||||
|
||||
public override bool CanStop => false;
|
||||
|
||||
public ObservableList<GridViewModel> AttachedGrids { get; } = new ObservableList<GridViewModel>();
|
||||
public MtObservableList<GridViewModel> AttachedGrids { get; } = new MtObservableList<GridViewModel>();
|
||||
|
||||
public async Task UpdateAttachedGrids()
|
||||
{
|
||||
@@ -28,11 +29,10 @@ namespace Torch.Server.ViewModels.Entities
|
||||
await TorchBase.Instance.InvokeAsync(() => MyEntities.GetTopMostEntitiesInBox(ref box, entities)).ConfigureAwait(false);
|
||||
foreach (var entity in entities.Where(e => e is IMyCubeGrid))
|
||||
{
|
||||
var gridModel = Tree.Grids.FirstOrDefault(g => g.Entity.EntityId == entity.EntityId);
|
||||
if (gridModel == null)
|
||||
if (Tree.Grids.TryGetValue(entity.EntityId, out var gridModel))
|
||||
{
|
||||
gridModel = new GridViewModel((MyCubeGrid)entity, Tree);
|
||||
Tree.Grids.Add(gridModel);
|
||||
Tree.Grids.Add(entity.EntityId, gridModel);
|
||||
}
|
||||
|
||||
AttachedGrids.Add(gridModel);
|
||||
|
@@ -11,16 +11,19 @@ using VRage.Game.ModAPI;
|
||||
using VRage.ModAPI;
|
||||
using System.Windows.Threading;
|
||||
using NLog;
|
||||
using Torch.Collections;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class EntityTreeViewModel : ViewModel
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
//TODO: these should be sorted sets for speed
|
||||
public ObservableList<GridViewModel> Grids { get; set; } = new ObservableList<GridViewModel>();
|
||||
public ObservableList<CharacterViewModel> Characters { get; set; } = new ObservableList<CharacterViewModel>();
|
||||
public ObservableList<EntityViewModel> FloatingObjects { get; set; } = new ObservableList<EntityViewModel>();
|
||||
public ObservableList<VoxelMapViewModel> VoxelMaps { get; set; } = new ObservableList<VoxelMapViewModel>();
|
||||
public MtObservableSortedDictionary<long, GridViewModel> Grids { get; set; } = new MtObservableSortedDictionary<long, GridViewModel>();
|
||||
public MtObservableSortedDictionary<long, CharacterViewModel> Characters { get; set; } = new MtObservableSortedDictionary<long, CharacterViewModel>();
|
||||
public MtObservableSortedDictionary<long, EntityViewModel> FloatingObjects { get; set; } = new MtObservableSortedDictionary<long, EntityViewModel>();
|
||||
public MtObservableSortedDictionary<long, VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableSortedDictionary<long, VoxelMapViewModel>();
|
||||
public Dispatcher ControlDispatcher => _control.Dispatcher;
|
||||
|
||||
private EntityViewModel _currentEntity;
|
||||
@@ -29,7 +32,12 @@ namespace Torch.Server.ViewModels
|
||||
public EntityViewModel CurrentEntity
|
||||
{
|
||||
get => _currentEntity;
|
||||
set { _currentEntity = value; OnPropertyChanged(); }
|
||||
set { _currentEntity = value; OnPropertyChanged(nameof(CurrentEntity)); }
|
||||
}
|
||||
|
||||
// I hate you today WPF
|
||||
public EntityTreeViewModel() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public EntityTreeViewModel(UserControl control)
|
||||
@@ -45,39 +53,55 @@ namespace Torch.Server.ViewModels
|
||||
|
||||
private void MyEntities_OnEntityRemove(VRage.Game.Entity.MyEntity obj)
|
||||
{
|
||||
switch (obj)
|
||||
try
|
||||
{
|
||||
case MyCubeGrid grid:
|
||||
Grids.RemoveWhere(m => m.Id == grid.EntityId);
|
||||
break;
|
||||
case MyCharacter character:
|
||||
Characters.RemoveWhere(m => m.Id == character.EntityId);
|
||||
break;
|
||||
case MyFloatingObject floating:
|
||||
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
|
||||
break;
|
||||
case MyVoxelBase voxel:
|
||||
VoxelMaps.RemoveWhere(m => m.Id == voxel.EntityId);
|
||||
break;
|
||||
switch (obj)
|
||||
{
|
||||
case MyCubeGrid grid:
|
||||
Grids.Remove(grid.EntityId);
|
||||
break;
|
||||
case MyCharacter character:
|
||||
Characters.Remove(character.EntityId);
|
||||
break;
|
||||
case MyFloatingObject floating:
|
||||
FloatingObjects.Remove(floating.EntityId);
|
||||
break;
|
||||
case MyVoxelBase voxel:
|
||||
VoxelMaps.Remove(voxel.EntityId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.Error(e);
|
||||
// ignore error "it's only UI"
|
||||
}
|
||||
}
|
||||
|
||||
private void MyEntities_OnEntityAdd(VRage.Game.Entity.MyEntity obj)
|
||||
{
|
||||
switch (obj)
|
||||
try
|
||||
{
|
||||
case MyCubeGrid grid:
|
||||
Grids.Insert(new GridViewModel(grid, this), g => g.Name);
|
||||
break;
|
||||
case MyCharacter character:
|
||||
Characters.Insert(new CharacterViewModel(character, this), c => c.Name);
|
||||
break;
|
||||
case MyFloatingObject floating:
|
||||
FloatingObjects.Insert(new FloatingObjectViewModel(floating, this), f => f.Name);
|
||||
break;
|
||||
case MyVoxelBase voxel:
|
||||
VoxelMaps.Insert(new VoxelMapViewModel(voxel, this), v => v.Name);
|
||||
break;
|
||||
switch (obj)
|
||||
{
|
||||
case MyCubeGrid grid:
|
||||
Grids.Add(obj.EntityId, new GridViewModel(grid, this));
|
||||
break;
|
||||
case MyCharacter character:
|
||||
Characters.Add(obj.EntityId, new CharacterViewModel(character, this));
|
||||
break;
|
||||
case MyFloatingObject floating:
|
||||
FloatingObjects.Add(obj.EntityId, new FloatingObjectViewModel(floating, this));
|
||||
break;
|
||||
case MyVoxelBase voxel:
|
||||
VoxelMaps.Add(obj.EntityId, new VoxelMapViewModel(voxel, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.Error(e);
|
||||
// ignore error "it's only UI"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,18 +6,19 @@ using System.Threading.Tasks;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.API.Plugins;
|
||||
using Torch.Collections;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class PluginManagerViewModel : ViewModel
|
||||
{
|
||||
public ObservableList<PluginViewModel> Plugins { get; } = new ObservableList<PluginViewModel>();
|
||||
public MtObservableList<PluginViewModel> Plugins { get; } = new MtObservableList<PluginViewModel>();
|
||||
|
||||
private PluginViewModel _selectedPlugin;
|
||||
public PluginViewModel SelectedPlugin
|
||||
{
|
||||
get => _selectedPlugin;
|
||||
set { _selectedPlugin = value; OnPropertyChanged(); }
|
||||
set { _selectedPlugin = value; OnPropertyChanged(nameof(SelectedPlugin)); }
|
||||
}
|
||||
|
||||
public PluginManagerViewModel() { }
|
||||
@@ -29,7 +30,7 @@ namespace Torch.Server.ViewModels
|
||||
pluginManager.PluginsLoaded += PluginManager_PluginsLoaded;
|
||||
}
|
||||
|
||||
private void PluginManager_PluginsLoaded(IList<ITorchPlugin> obj)
|
||||
private void PluginManager_PluginsLoaded(IReadOnlyCollection<ITorchPlugin> obj)
|
||||
{
|
||||
Plugins.Clear();
|
||||
foreach (var plugin in obj)
|
||||
|
@@ -3,32 +3,40 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Torch.API;
|
||||
using Torch.API.Plugins;
|
||||
using Torch.Server.Views;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class PluginViewModel
|
||||
{
|
||||
public UserControl Control
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Plugin is IWpfPlugin p)
|
||||
return p.GetControl();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public UserControl Control { get; }
|
||||
public string Name { get; }
|
||||
public ITorchPlugin Plugin { get; }
|
||||
|
||||
public PluginViewModel(ITorchPlugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
|
||||
if (Plugin is IWpfPlugin p)
|
||||
Control = p.GetControl();
|
||||
|
||||
Name = $"{plugin.Name} ({plugin.Version})";
|
||||
|
||||
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
|
||||
UpdateResourceDict(ThemeControl.currentTheme);
|
||||
}
|
||||
|
||||
public void UpdateResourceDict(ResourceDictionary dictionary)
|
||||
{
|
||||
if (this.Control == null)
|
||||
return;
|
||||
|
||||
this.Control.Resources.MergedDictionaries.Clear();
|
||||
this.Control.Resources.MergedDictionaries.Add(dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,373 +1,267 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpDX.Toolkit.Collections;
|
||||
using Torch;
|
||||
using Torch.Collections;
|
||||
using Torch.Views;
|
||||
using VRage.Game;
|
||||
using VRage.Library.Utils;
|
||||
using VRage.Serialization;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View model for <see cref="MyObjectBuilder_SessionSettings"/>
|
||||
/// </summary>
|
||||
public class SessionSettingsViewModel : ViewModel
|
||||
{
|
||||
private MyObjectBuilder_SessionSettings _settings;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new view model with a new <see cref="MyObjectBuilder_SessionSettings"/> object.
|
||||
/// </summary>
|
||||
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
|
||||
{
|
||||
|
||||
}
|
||||
[Torch.Views.Display(Description = "The type of the game mode.", Name = "Game Mode", GroupName = "Others")]
|
||||
public MyGameModeEnum GameMode { get => _settings.GameMode; set => SetValue(ref _settings.GameMode, value); }
|
||||
[Torch.Views.Display(Description = "The type of the game online mode.", Name = "Online Mode", GroupName = "Others")]
|
||||
public MyOnlineModeEnum OnlineMode { get => _settings.OnlineMode; set => SetValue(ref _settings.OnlineMode, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for inventory size.", Name = "Inventory Size", GroupName = "Multipliers")]
|
||||
public float InventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for assembler speed.", Name = "Assembler Speed", GroupName = "Multipliers")]
|
||||
public float AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for assembler efficiency.", Name = "Assembler Efficiency", GroupName = "Multipliers")]
|
||||
public float AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for refinery speed.", Name = "Refinery Speed", GroupName = "Multipliers")]
|
||||
public float RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The maximum number of connected players.", Name = "Max Players", GroupName = "Players")]
|
||||
public short MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The maximum number of existing floating objects.", Name = "Max Floating Objects", GroupName = "Environment")]
|
||||
public short MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The maximum number of backup saves.", Name = "Max Backup Saves", GroupName = "Others")]
|
||||
public short MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The maximum number of blocks in one grid.", Name = "Max Grid Blocks", GroupName = "Block Limits")]
|
||||
public int MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The maximum number of blocks per player.", Name = "Max Blocks per Player", GroupName = "Block Limits")]
|
||||
public int MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The total number of Performance Cost Units in the world.", Name = "World PCU", GroupName = "Block Limits")]
|
||||
public int TotalPCU { get => _settings.TotalPCU; set => SetValue(ref _settings.TotalPCU, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The maximum number of existing factions in the world.", Name = "Max Factions Count", GroupName = "Block Limits")]
|
||||
public int MaxFactionsCount { get => _settings.MaxFactionsCount; set => SetValue(ref _settings.MaxFactionsCount, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines block limits mode.", Name = "Block Limits Mode", GroupName = "Block Limits")]
|
||||
public MyBlockLimitsEnabledEnum BlockLimitsEnabled { get => _settings.BlockLimitsEnabled; set => SetValue(ref _settings.BlockLimitsEnabled, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables possibility to remove grid remotely from the world by an author.", Name = "Enable Remote Grid Removal", GroupName = "Others")]
|
||||
public bool EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines hostility of the environment.", Name = "Environment Hostility", GroupName = "Environment")]
|
||||
public MyEnvironmentHostilityEnum EnvironmentHostility { get => _settings.EnvironmentHostility; set => SetValue(ref _settings.EnvironmentHostility, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables auto healing of the character.", Name = "Auto Healing", GroupName = "Players")]
|
||||
public bool AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables copy and paste feature.", Name = "Enable Copy & Paste", GroupName = "Players")]
|
||||
public bool EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables weapons.", Name = "Enable Weapons", GroupName = "Others")]
|
||||
public bool WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "", Name = "Show Player Names on HUD", GroupName = "Players")]
|
||||
public bool ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables thruster damage.", Name = "Enable Thruster Damage", GroupName = "Others")]
|
||||
public bool ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables spawning of cargo ships.", Name = "Enable Cargo Ships", GroupName = "NPCs")]
|
||||
public bool CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables spectator camera.", Name = "Enable Spectator Camera", GroupName = "Others")]
|
||||
public bool EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a view model using an existing <see cref="MyObjectBuilder_SessionSettings"/> object.
|
||||
/// Size of the edge of the world area cube.
|
||||
/// Don't use directly, as it is error-prone (it's km instead of m and edge size instead of half-extent)
|
||||
/// Rather use MyEntities.WorldHalfExtent()
|
||||
/// </summary>
|
||||
[Torch.Views.Display(Description = "Defines the size of the world.", Name = "World Size [km]", GroupName = "Environment")]
|
||||
public int WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "When enabled respawn ship is removed after player logout.", Name = "Remove Respawn Ships on Logoff", GroupName = "Others")]
|
||||
public bool RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "", Name = "Reset Ownership", GroupName = "Players")]
|
||||
public bool ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for welder speed.", Name = "Welder Speed", GroupName = "Multipliers")]
|
||||
public float WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for grinder speed.", Name = "Grinder Speed", GroupName = "Multipliers")]
|
||||
public float GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables realistic sounds.", Name = "Enable Realistic Sound", GroupName = "Environment")]
|
||||
public bool RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for hacking speed.", Name = "Hacking Speed", GroupName = "Multipliers")]
|
||||
public float HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables permanent death.", Name = "Permanent Death", GroupName = "Players")]
|
||||
public bool? PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines autosave interval.", Name = "Autosave Interval [mins]", GroupName = "Others")]
|
||||
public uint AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables saving from the menu.", Name = "Enable Saving from Menu", GroupName = "Others")]
|
||||
public bool EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables respawn screen.", Name = "Enable Respawn Screen in the Game", GroupName = "Players")]
|
||||
public bool EnableRespawnScreen { get => _settings.EnableRespawnScreen; set => SetValue(ref _settings.EnableRespawnScreen, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables infinite ammunition in survival game mode.", Name = "Enable Infinite Ammunition in Survival", GroupName = "Others")]
|
||||
public bool InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables drop containers (unknown signals).", Name = "Enable Drop Containers", GroupName = "Others")]
|
||||
public bool EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "The multiplier for respawn ship timer.", Name = "Respawn Ship Time Multiplier", GroupName = "Players")]
|
||||
public float SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines density of the procedurally generated content.", Name = "Procedural Density", GroupName = "Environment")]
|
||||
public float ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines unique starting seed for the procedurally generated content.", Name = "Procedural Seed", GroupName = "Environment")]
|
||||
public int ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables destruction feature for the blocks.", Name = "Enable Destructible Blocks", GroupName = "Environment")]
|
||||
public bool DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables in game scripts.", Name = "Enable Ingame Scripts", GroupName = "Others")]
|
||||
public bool EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "", Name = "Flora Density Multiplier", GroupName = "Environment")]
|
||||
public float FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables tool shake feature.", Name = "Enable Tool Shake", GroupName = "Players")]
|
||||
[DefaultValue(false)]
|
||||
public bool EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "", Name = "Voxel Generator Version", GroupName = "Environment")]
|
||||
public int VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables oxygen in the world.", Name = "Enable Oxygen", GroupName = "Environment")]
|
||||
public bool EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables airtightness in the world.", Name = "Enable Airtightness", GroupName = "Environment")]
|
||||
public bool EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables 3rd person camera.", Name = "Enable 3rd Person Camera", GroupName = "Players")]
|
||||
public bool Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables random encounters in the world.", Name = "Enable Encounters", GroupName = "NPCs")]
|
||||
public bool EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables possibility of converting grid to station.", Name = "Enable Convert to Station", GroupName = "Others")]
|
||||
public bool EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables possibility of station grid inside voxel.", Name = "Enable Station Grid with Voxel", GroupName = "Environment")]
|
||||
public bool StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables sun rotation.", Name = "Enable Sun Rotation", GroupName = "Environment")]
|
||||
public bool EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables respawn ships.", Name = "Enable Respawn Ships", GroupName = "Others")]
|
||||
public bool EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "", Name = "Physics Iterations", GroupName = "Environment")]
|
||||
public int PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines interval of one rotation of the sun.", Name = "Sun Rotation Interval", GroupName = "Environment")]
|
||||
public float SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables jetpack.", Name = "Enable Jetpack", GroupName = "Players")]
|
||||
public bool EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables spawning with tools in the inventory.", Name = "Spawn with Tools", GroupName = "Players")]
|
||||
public bool SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables voxel destructions.", Name = "Enable Voxel Destruction", GroupName = "Environment")]
|
||||
public bool EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables spawning of drones in the world.", Name = "Enable Drones", GroupName = "NPCs")]
|
||||
public bool EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables spawning of wolves in the world.", Name = "Enable Wolves", GroupName = "NPCs")]
|
||||
public bool EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables spawning of spiders in the world.", Name = "Enable Spiders", GroupName = "NPCs")]
|
||||
public bool EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); }
|
||||
|
||||
[Torch.Views.Display(Name = "Block Type World Limits", GroupName = "Block Limits")]
|
||||
public Dictionary<string, short> BlockTypeLimits { get => _settings.BlockTypeLimits.Dictionary; set => SetValue(x => _settings.BlockTypeLimits.Dictionary = x, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables scripter role for administration.", Name = "Enable Scripter Role", GroupName = "Others")]
|
||||
public bool EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines minimum respawn time for drop containers.", Name = "Min Drop Container Respawn Time", GroupName = "Others")]
|
||||
public int MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines maximum respawn time for drop containers.", Name = "Max Drop Container Respawn Time", GroupName = "Others")]
|
||||
public int MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables friendly fire for turrets.", Name = "Enable Turrets Friendly Fire", GroupName = "Environment")]
|
||||
public bool EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables sub-grid damage.", Name = "Enable Sub-Grid Damage", GroupName = "Environment")]
|
||||
public bool EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines synchronization distance in multiplayer. High distance can slow down server drastically. Use with caution.", Name = "Sync Distance", GroupName = "Environment")]
|
||||
public int SyncDistance { get => _settings.SyncDistance; set => SetValue(ref _settings.SyncDistance, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines render distance for clients in multiplayer. High distance can slow down client FPS. Values larger than SyncDistance may not work as expected.", Name = "View Distance", GroupName = "Environment")]
|
||||
public int ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value);}
|
||||
|
||||
[Torch.Views.Display(Description = "Enables experimental mode.", Name = "Experimental Mode", GroupName = "Others")]
|
||||
public bool ExperimentalMode { get => _settings.ExperimentalMode; set => SetValue(ref _settings.ExperimentalMode, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables adaptive simulation quality system. This system is useful if you have a lot of voxel deformations in the world and low simulation speed.", Name = "Adaptive Simulation Quality", GroupName = "Others")]
|
||||
public bool AdaptiveSimulationQuality { get => _settings.AdaptiveSimulationQuality; set => SetValue(ref _settings.AdaptiveSimulationQuality, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables voxel hand.", Name = "Enable voxel hand", GroupName = "Others")]
|
||||
public bool EnableVoxelHand { get => _settings.EnableVoxelHand; set => SetValue(ref _settings.EnableVoxelHand, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Enables trash removal system.", Name = "Trash Removal Enabled", GroupName = "Trash Removal")]
|
||||
public bool TrashRemovalEnabled { get => _settings.TrashRemovalEnabled; set => SetValue(ref _settings.TrashRemovalEnabled, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines flags for trash removal system.", Name = "Trash Removal Flags", GroupName = "Trash Removal")]
|
||||
public MyTrashRemovalFlags TrashFlagsValue { get => (MyTrashRemovalFlags)_settings.TrashFlagsValue; set => SetValue(ref _settings.TrashFlagsValue, (int)value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines block count threshold for trash removal system.", Name = "Block Count Threshold", GroupName = "Trash Removal")]
|
||||
public int BlockCountThreshold { get => _settings.BlockCountThreshold; set => SetValue(ref _settings.BlockCountThreshold, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines player distance threshold for trash removal system.", Name = "Player Distance Threshold [m]", GroupName = "Trash Removal")]
|
||||
public float PlayerDistanceThreshold { get => _settings.PlayerDistanceThreshold; set => SetValue(ref _settings.PlayerDistanceThreshold, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "By setting this, server will keep number of grids around this value. \n !WARNING! It ignores Powered and Fixed flags, Block Count and lowers Distance from player.\n Set to 0 to disable.", Name = "Optimal Grid Count", GroupName = "Trash Removal")]
|
||||
public int OptimalGridCount { get => _settings.OptimalGridCount; set => SetValue(ref _settings.OptimalGridCount, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines player inactivity threshold for trash removal system. \n !WARNING! This will remove all grids of the player.\n Set to 0 to disable.", Name = "Player Inactivity Threshold [hours]", GroupName = "Trash Removal")]
|
||||
public float PlayerInactivityThreshold { get => _settings.PlayerInactivityThreshold; set => SetValue(ref _settings.PlayerInactivityThreshold, value); }
|
||||
|
||||
[Torch.Views.Display(Description = "Defines character removal threshold for trash removal system. If player disconnects it will remove his character after this time.\n Set to 0 to disable.", Name = "Character Removal Threshold [mins]", GroupName = "Trash Removal")]
|
||||
public int PlayerCharacterRemovalThreshold { get => _settings.PlayerCharacterRemovalThreshold; set => SetValue(ref _settings.PlayerCharacterRemovalThreshold, value); }
|
||||
|
||||
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
foreach (var limit in settings.BlockTypeLimits.Dictionary)
|
||||
BlockLimits.Add(new BlockLimitViewModel(this, limit.Key, limit.Value));
|
||||
}
|
||||
|
||||
public ObservableList<BlockLimitViewModel> BlockLimits { get; } = new ObservableList<BlockLimitViewModel>();
|
||||
|
||||
#region Multipliers
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.InventorySizeMultiplier"/>
|
||||
public float InventorySizeMultiplier
|
||||
{
|
||||
get => _settings.InventorySizeMultiplier; set { _settings.InventorySizeMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.RefinerySpeedMultiplier"/>
|
||||
public float RefinerySpeedMultiplier
|
||||
{
|
||||
get => _settings.RefinerySpeedMultiplier; set { _settings.RefinerySpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AssemblerEfficiencyMultiplier"/>
|
||||
public float AssemblerEfficiencyMultiplier
|
||||
{
|
||||
get => _settings.AssemblerEfficiencyMultiplier; set { _settings.AssemblerEfficiencyMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AssemblerSpeedMultiplier"/>
|
||||
public float AssemblerSpeedMultiplier
|
||||
{
|
||||
get => _settings.AssemblerSpeedMultiplier; set { _settings.AssemblerSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.GrinderSpeedMultiplier"/>
|
||||
public float GrinderSpeedMultiplier
|
||||
{
|
||||
get => _settings.GrinderSpeedMultiplier; set { _settings.GrinderSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.HackSpeedMultiplier"/>
|
||||
public float HackSpeedMultiplier
|
||||
{
|
||||
get => _settings.HackSpeedMultiplier; set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.WelderSpeedMultiplier"/>
|
||||
public float WelderSpeedMultiplier
|
||||
{
|
||||
get => _settings.WelderSpeedMultiplier; set { _settings.WelderSpeedMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region NPCs
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableDrones"/>
|
||||
public bool EnableDrones
|
||||
{
|
||||
get => _settings.EnableDrones; set { _settings.EnableDrones = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableEncounters"/>
|
||||
public bool EnableEncounters
|
||||
{
|
||||
get => _settings.EnableEncounters; set { _settings.EnableEncounters = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableSpiders"/>
|
||||
public bool EnableSpiders
|
||||
{
|
||||
get => _settings.EnableSpiders; set { _settings.EnableSpiders = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableWolfs"/>
|
||||
public bool EnableWolves
|
||||
{
|
||||
get => _settings.EnableWolfs; set { _settings.EnableWolfs = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.CargoShipsEnabled"/>
|
||||
public bool EnableCargoShips
|
||||
{
|
||||
get => _settings.CargoShipsEnabled; set { _settings.CargoShipsEnabled = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Environment
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableSunRotation"/>
|
||||
public bool EnableSunRotation
|
||||
{
|
||||
get => _settings.EnableSunRotation; set { _settings.EnableSunRotation = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableOxygenPressurization"/>
|
||||
public bool EnableAirtightness
|
||||
{
|
||||
get => _settings.EnableOxygenPressurization; set { _settings.EnableOxygenPressurization = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableOxygen"/>
|
||||
public bool EnableOxygen
|
||||
{
|
||||
get => _settings.EnableOxygen; set { _settings.EnableOxygen = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.DestructibleBlocks"/>
|
||||
public bool EnableDestructibleBlocks
|
||||
{
|
||||
get => _settings.DestructibleBlocks; set { _settings.DestructibleBlocks = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableToolShake"/>
|
||||
public bool EnableToolShake
|
||||
{
|
||||
get => _settings.EnableToolShake; set { _settings.EnableToolShake = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableVoxelDestruction"/>
|
||||
public bool EnableVoxelDestruction
|
||||
{
|
||||
get => _settings.EnableVoxelDestruction; set { _settings.EnableVoxelDestruction = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List used to populate the environment hostility combo box.
|
||||
/// </summary>
|
||||
public List<string> EnvironmentHostilityValues { get; } = Enum.GetNames(typeof(MyEnvironmentHostilityEnum)).ToList();
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnvironmentHostility"/>
|
||||
public string EnvironmentHostility
|
||||
{
|
||||
get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out _settings.EnvironmentHostility); OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableFlora"/>
|
||||
public bool EnableFlora
|
||||
{
|
||||
get => _settings.EnableFlora; set { _settings.EnableFlora = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// List used to populate the game mode combobox.
|
||||
/// </summary>
|
||||
public List<string> GameModeValues { get; } = Enum.GetNames(typeof(MyGameModeEnum)).ToList();
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.GameMode"/>
|
||||
public string GameMode
|
||||
{
|
||||
get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out _settings.GameMode); OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AutoHealing"/>
|
||||
public bool EnableAutoHealing
|
||||
{
|
||||
get => _settings.AutoHealing; set { _settings.AutoHealing = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableCopyPaste"/>
|
||||
public bool EnableCopyPaste
|
||||
{
|
||||
get => _settings.EnableCopyPaste; set { _settings.EnableCopyPaste = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ShowPlayerNamesOnHud"/>
|
||||
public bool ShowPlayerNamesOnHud
|
||||
{
|
||||
get => _settings.ShowPlayerNamesOnHud; set { _settings.ShowPlayerNamesOnHud = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.Enable3rdPersonView"/>
|
||||
public bool EnableThirdPerson
|
||||
{
|
||||
get => _settings.Enable3rdPersonView; set { _settings.Enable3rdPersonView = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableSpectator"/>
|
||||
public bool EnableSpectator
|
||||
{
|
||||
get => _settings.EnableSpectator; set { _settings.EnableSpectator = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.SpawnWithTools"/>
|
||||
public bool SpawnWithTools
|
||||
{
|
||||
get => _settings.SpawnWithTools; set { _settings.SpawnWithTools = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableConvertToStation"/>
|
||||
public bool EnableConvertToStation
|
||||
{
|
||||
get => _settings.EnableConvertToStation; set { _settings.EnableConvertToStation = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableJetpack"/>
|
||||
public bool EnableJetpack
|
||||
{
|
||||
get => _settings.EnableJetpack; set { _settings.EnableJetpack = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableRemoteBlockRemoval"/>
|
||||
public bool EnableRemoteOwnerRemoval
|
||||
{
|
||||
get => _settings.EnableRemoteBlockRemoval; set { _settings.EnableRemoteBlockRemoval = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableRespawnShips"/>
|
||||
public bool EnableRespawnShips
|
||||
{
|
||||
get => _settings.EnableRespawnShips; set { _settings.EnableRespawnShips = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableScripterRole"/>
|
||||
public bool EnableScripterRole
|
||||
{
|
||||
get => _settings.EnableScripterRole; set { _settings.EnableScripterRole = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.RealisticSound"/>
|
||||
public bool EnableRealisticSound
|
||||
{
|
||||
get => _settings.RealisticSound; set { _settings.RealisticSound = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ResetOwnership"/>
|
||||
public bool ResetOwnership
|
||||
{
|
||||
get => _settings.ResetOwnership; set { _settings.ResetOwnership = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.RespawnShipDelete"/>
|
||||
public bool DeleteRespawnShips
|
||||
{
|
||||
get => _settings.RespawnShipDelete; set { _settings.RespawnShipDelete = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ThrusterDamage"/>
|
||||
public bool EnableThrusterDamage
|
||||
{
|
||||
get => _settings.ThrusterDamage; set { _settings.ThrusterDamage = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.WeaponsEnabled"/>
|
||||
public bool EnableWeapons
|
||||
{
|
||||
get => _settings.WeaponsEnabled; set { _settings.WeaponsEnabled = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.EnableIngameScripts"/>
|
||||
public bool EnableIngameScripts
|
||||
{
|
||||
get => _settings.EnableIngameScripts; set { _settings.EnableIngameScripts = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.AutoSaveInMinutes"/>
|
||||
public uint AutosaveInterval
|
||||
{
|
||||
get => _settings.AutoSaveInMinutes; set { _settings.AutoSaveInMinutes = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.FloraDensity"/>
|
||||
public int FloraDensity
|
||||
{
|
||||
get => _settings.FloraDensity; set { _settings.FloraDensity = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.FloraDensityMultiplier"/>
|
||||
public float FloraDensityMultiplier
|
||||
{
|
||||
get => _settings.FloraDensityMultiplier; set { _settings.FloraDensityMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxBackupSaves"/>
|
||||
public short MaxBackupSaves
|
||||
{
|
||||
get => _settings.MaxBackupSaves; set { _settings.MaxBackupSaves = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxBlocksPerPlayer"/>
|
||||
public int MaxBlocksPerPlayer
|
||||
{
|
||||
get => _settings.MaxBlocksPerPlayer; set { _settings.MaxBlocksPerPlayer = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxFloatingObjects"/>
|
||||
public short MaxFloatingObjects
|
||||
{
|
||||
get => _settings.MaxFloatingObjects; set { _settings.MaxFloatingObjects = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxGridSize"/>
|
||||
public int MaxGridSize
|
||||
{
|
||||
get => _settings.MaxGridSize; set { _settings.MaxGridSize = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.MaxPlayers"/>
|
||||
public short MaxPlayers
|
||||
{
|
||||
get => _settings.MaxPlayers; set { _settings.MaxPlayers = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.PhysicsIterations"/>
|
||||
public int PhysicsIterations
|
||||
{
|
||||
get => _settings.PhysicsIterations; set { _settings.PhysicsIterations = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.SpawnShipTimeMultiplier"/>
|
||||
public float SpawnTimeMultiplier
|
||||
{
|
||||
get => _settings.SpawnShipTimeMultiplier; set { _settings.SpawnShipTimeMultiplier = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.SunRotationIntervalMinutes"/>
|
||||
public float SunRotationInterval
|
||||
{
|
||||
get => _settings.SunRotationIntervalMinutes; set { _settings.SunRotationIntervalMinutes = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ViewDistance"/>
|
||||
public int ViewDistance
|
||||
{
|
||||
get => _settings.ViewDistance; set { _settings.ViewDistance = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.WorldSizeKm"/>
|
||||
public int WorldSize
|
||||
{
|
||||
get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <summary />
|
||||
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
|
||||
{
|
||||
viewModel._settings.BlockTypeLimits.Dictionary.Clear();
|
||||
foreach (var limit in viewModel.BlockLimits)
|
||||
viewModel._settings.BlockTypeLimits.Dictionary.Add(limit.BlockType, limit.Limit);
|
||||
return viewModel._settings;
|
||||
}
|
||||
}
|
||||
|
253
Torch.Server/ViewModels/SessionSettingsViewModel1.cs
Normal file
253
Torch.Server/ViewModels/SessionSettingsViewModel1.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
// This file is generated automatically! Any changes will be overwritten.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Torch;
|
||||
using Torch.Collections;
|
||||
using VRage.Game;
|
||||
using VRage.Library.Utils;
|
||||
using VRage.Serialization;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class SessionSettingsViewModel : ViewModel
|
||||
{
|
||||
private MyObjectBuilder_SessionSettings _settings;
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.GameMode" />
|
||||
public string GameMode { get => _settings.GameMode.ToString(); set { Enum.TryParse(value, true, out VRage.Library.Utils.MyGameModeEnum parsedVal); SetValue(ref _settings.GameMode, parsedVal); } }
|
||||
public List<string> GameModeValues { get; } = new List<string> {"Creative", "Survival"};
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.InventorySizeMultiplier" />
|
||||
public System.Single InventorySizeMultiplier { get => _settings.InventorySizeMultiplier; set => SetValue(ref _settings.InventorySizeMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AssemblerSpeedMultiplier" />
|
||||
public System.Single AssemblerSpeedMultiplier { get => _settings.AssemblerSpeedMultiplier; set => SetValue(ref _settings.AssemblerSpeedMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AssemblerEfficiencyMultiplier" />
|
||||
public System.Single AssemblerEfficiencyMultiplier { get => _settings.AssemblerEfficiencyMultiplier; set => SetValue(ref _settings.AssemblerEfficiencyMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.RefinerySpeedMultiplier" />
|
||||
public System.Single RefinerySpeedMultiplier { get => _settings.RefinerySpeedMultiplier; set => SetValue(ref _settings.RefinerySpeedMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.OnlineMode" />
|
||||
public string OnlineMode { get => _settings.OnlineMode.ToString(); set { Enum.TryParse(value, true, out VRage.Game.MyOnlineModeEnum parsedVal); SetValue(ref _settings.OnlineMode, parsedVal); } }
|
||||
public List<string> OnlineModeValues { get; } = new List<string> {"OFFLINE", "PUBLIC", "FRIENDS", "PRIVATE"};
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxPlayers" />
|
||||
public System.Int16 MaxPlayers { get => _settings.MaxPlayers; set => SetValue(ref _settings.MaxPlayers, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxFloatingObjects" />
|
||||
public System.Int16 MaxFloatingObjects { get => _settings.MaxFloatingObjects; set => SetValue(ref _settings.MaxFloatingObjects, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxBackupSaves" />
|
||||
public System.Int16 MaxBackupSaves { get => _settings.MaxBackupSaves; set => SetValue(ref _settings.MaxBackupSaves, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxGridSize" />
|
||||
public System.Int32 MaxGridSize { get => _settings.MaxGridSize; set => SetValue(ref _settings.MaxGridSize, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxBlocksPerPlayer" />
|
||||
public System.Int32 MaxBlocksPerPlayer { get => _settings.MaxBlocksPerPlayer; set => SetValue(ref _settings.MaxBlocksPerPlayer, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableBlockLimits" />
|
||||
public System.Boolean EnableBlockLimits { get => _settings.EnableBlockLimits; set => SetValue(ref _settings.EnableBlockLimits, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableRemoteBlockRemoval" />
|
||||
public System.Boolean EnableRemoteBlockRemoval { get => _settings.EnableRemoteBlockRemoval; set => SetValue(ref _settings.EnableRemoteBlockRemoval, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnvironmentHostility" />
|
||||
public string EnvironmentHostility { get => _settings.EnvironmentHostility.ToString(); set { Enum.TryParse(value, true, out VRage.Game.MyEnvironmentHostilityEnum parsedVal); SetValue(ref _settings.EnvironmentHostility, parsedVal); } }
|
||||
public List<string> EnvironmentHostilityValues { get; } = new List<string> {"SAFE", "NORMAL", "CATACLYSM", "CATACLYSM_UNREAL"};
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AutoHealing" />
|
||||
public System.Boolean AutoHealing { get => _settings.AutoHealing; set => SetValue(ref _settings.AutoHealing, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableCopyPaste" />
|
||||
public System.Boolean EnableCopyPaste { get => _settings.EnableCopyPaste; set => SetValue(ref _settings.EnableCopyPaste, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.WeaponsEnabled" />
|
||||
public System.Boolean WeaponsEnabled { get => _settings.WeaponsEnabled; set => SetValue(ref _settings.WeaponsEnabled, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ShowPlayerNamesOnHud" />
|
||||
public System.Boolean ShowPlayerNamesOnHud { get => _settings.ShowPlayerNamesOnHud; set => SetValue(ref _settings.ShowPlayerNamesOnHud, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ThrusterDamage" />
|
||||
public System.Boolean ThrusterDamage { get => _settings.ThrusterDamage; set => SetValue(ref _settings.ThrusterDamage, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.CargoShipsEnabled" />
|
||||
public System.Boolean CargoShipsEnabled { get => _settings.CargoShipsEnabled; set => SetValue(ref _settings.CargoShipsEnabled, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSpectator" />
|
||||
public System.Boolean EnableSpectator { get => _settings.EnableSpectator; set => SetValue(ref _settings.EnableSpectator, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.WorldSizeKm" />
|
||||
public System.Int32 WorldSizeKm { get => _settings.WorldSizeKm; set => SetValue(ref _settings.WorldSizeKm, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.RespawnShipDelete" />
|
||||
public System.Boolean RespawnShipDelete { get => _settings.RespawnShipDelete; set => SetValue(ref _settings.RespawnShipDelete, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ResetOwnership" />
|
||||
public System.Boolean ResetOwnership { get => _settings.ResetOwnership; set => SetValue(ref _settings.ResetOwnership, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.WelderSpeedMultiplier" />
|
||||
public System.Single WelderSpeedMultiplier { get => _settings.WelderSpeedMultiplier; set => SetValue(ref _settings.WelderSpeedMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.GrinderSpeedMultiplier" />
|
||||
public System.Single GrinderSpeedMultiplier { get => _settings.GrinderSpeedMultiplier; set => SetValue(ref _settings.GrinderSpeedMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.RealisticSound" />
|
||||
public System.Boolean RealisticSound { get => _settings.RealisticSound; set => SetValue(ref _settings.RealisticSound, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.HackSpeedMultiplier" />
|
||||
public System.Single HackSpeedMultiplier { get => _settings.HackSpeedMultiplier; set => SetValue(ref _settings.HackSpeedMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.PermanentDeath" />
|
||||
public System.Nullable<System.Boolean> PermanentDeath { get => _settings.PermanentDeath; set => SetValue(ref _settings.PermanentDeath, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.AutoSaveInMinutes" />
|
||||
public System.UInt32 AutoSaveInMinutes { get => _settings.AutoSaveInMinutes; set => SetValue(ref _settings.AutoSaveInMinutes, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSaving" />
|
||||
public System.Boolean EnableSaving { get => _settings.EnableSaving; set => SetValue(ref _settings.EnableSaving, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableRespawnScreen" />
|
||||
public System.Boolean EnableRespawnScreen { get => _settings.EnableRespawnScreen; set => SetValue(ref _settings.EnableRespawnScreen, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.InfiniteAmmo" />
|
||||
public System.Boolean InfiniteAmmo { get => _settings.InfiniteAmmo; set => SetValue(ref _settings.InfiniteAmmo, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableContainerDrops" />
|
||||
public System.Boolean EnableContainerDrops { get => _settings.EnableContainerDrops; set => SetValue(ref _settings.EnableContainerDrops, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.SpawnShipTimeMultiplier" />
|
||||
public System.Single SpawnShipTimeMultiplier { get => _settings.SpawnShipTimeMultiplier; set => SetValue(ref _settings.SpawnShipTimeMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ProceduralDensity" />
|
||||
public System.Single ProceduralDensity { get => _settings.ProceduralDensity; set => SetValue(ref _settings.ProceduralDensity, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ProceduralSeed" />
|
||||
public System.Int32 ProceduralSeed { get => _settings.ProceduralSeed; set => SetValue(ref _settings.ProceduralSeed, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.DestructibleBlocks" />
|
||||
public System.Boolean DestructibleBlocks { get => _settings.DestructibleBlocks; set => SetValue(ref _settings.DestructibleBlocks, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableIngameScripts" />
|
||||
public System.Boolean EnableIngameScripts { get => _settings.EnableIngameScripts; set => SetValue(ref _settings.EnableIngameScripts, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ViewDistance" />
|
||||
public System.Int32 ViewDistance { get => _settings.ViewDistance; set => SetValue(ref _settings.ViewDistance, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.FloraDensity" />
|
||||
public System.Int32 FloraDensity { get => _settings.FloraDensity; set => SetValue(ref _settings.FloraDensity, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableToolShake" />
|
||||
public System.Boolean EnableToolShake { get => _settings.EnableToolShake; set => SetValue(ref _settings.EnableToolShake, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.VoxelGeneratorVersion" />
|
||||
public System.Int32 VoxelGeneratorVersion { get => _settings.VoxelGeneratorVersion; set => SetValue(ref _settings.VoxelGeneratorVersion, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableOxygen" />
|
||||
public System.Boolean EnableOxygen { get => _settings.EnableOxygen; set => SetValue(ref _settings.EnableOxygen, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableOxygenPressurization" />
|
||||
public System.Boolean EnableOxygenPressurization { get => _settings.EnableOxygenPressurization; set => SetValue(ref _settings.EnableOxygenPressurization, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.Enable3rdPersonView" />
|
||||
public System.Boolean Enable3rdPersonView { get => _settings.Enable3rdPersonView; set => SetValue(ref _settings.Enable3rdPersonView, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableEncounters" />
|
||||
public System.Boolean EnableEncounters { get => _settings.EnableEncounters; set => SetValue(ref _settings.EnableEncounters, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableFlora" />
|
||||
public System.Boolean EnableFlora { get => _settings.EnableFlora; set => SetValue(ref _settings.EnableFlora, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableConvertToStation" />
|
||||
public System.Boolean EnableConvertToStation { get => _settings.EnableConvertToStation; set => SetValue(ref _settings.EnableConvertToStation, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.StationVoxelSupport" />
|
||||
public System.Boolean StationVoxelSupport { get => _settings.StationVoxelSupport; set => SetValue(ref _settings.StationVoxelSupport, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSunRotation" />
|
||||
public System.Boolean EnableSunRotation { get => _settings.EnableSunRotation; set => SetValue(ref _settings.EnableSunRotation, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableRespawnShips" />
|
||||
public System.Boolean EnableRespawnShips { get => _settings.EnableRespawnShips; set => SetValue(ref _settings.EnableRespawnShips, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.ScenarioEditMode" />
|
||||
public System.Boolean ScenarioEditMode { get => _settings.ScenarioEditMode; set => SetValue(ref _settings.ScenarioEditMode, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.Scenario" />
|
||||
public System.Boolean Scenario { get => _settings.Scenario; set => SetValue(ref _settings.Scenario, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.CanJoinRunning" />
|
||||
public System.Boolean CanJoinRunning { get => _settings.CanJoinRunning; set => SetValue(ref _settings.CanJoinRunning, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.PhysicsIterations" />
|
||||
public System.Int32 PhysicsIterations { get => _settings.PhysicsIterations; set => SetValue(ref _settings.PhysicsIterations, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.SunRotationIntervalMinutes" />
|
||||
public System.Single SunRotationIntervalMinutes { get => _settings.SunRotationIntervalMinutes; set => SetValue(ref _settings.SunRotationIntervalMinutes, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableJetpack" />
|
||||
public System.Boolean EnableJetpack { get => _settings.EnableJetpack; set => SetValue(ref _settings.EnableJetpack, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.SpawnWithTools" />
|
||||
public System.Boolean SpawnWithTools { get => _settings.SpawnWithTools; set => SetValue(ref _settings.SpawnWithTools, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.StartInRespawnScreen" />
|
||||
public System.Boolean StartInRespawnScreen { get => _settings.StartInRespawnScreen; set => SetValue(ref _settings.StartInRespawnScreen, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableVoxelDestruction" />
|
||||
public System.Boolean EnableVoxelDestruction { get => _settings.EnableVoxelDestruction; set => SetValue(ref _settings.EnableVoxelDestruction, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxDrones" />
|
||||
public System.Int32 MaxDrones { get => _settings.MaxDrones; set => SetValue(ref _settings.MaxDrones, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableDrones" />
|
||||
public System.Boolean EnableDrones { get => _settings.EnableDrones; set => SetValue(ref _settings.EnableDrones, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableWolfs" />
|
||||
public System.Boolean EnableWolfs { get => _settings.EnableWolfs; set => SetValue(ref _settings.EnableWolfs, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSpiders" />
|
||||
public System.Boolean EnableSpiders { get => _settings.EnableSpiders; set => SetValue(ref _settings.EnableSpiders, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.FloraDensityMultiplier" />
|
||||
public System.Single FloraDensityMultiplier { get => _settings.FloraDensityMultiplier; set => SetValue(ref _settings.FloraDensityMultiplier, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableStructuralSimulation" />
|
||||
public System.Boolean EnableStructuralSimulation { get => _settings.EnableStructuralSimulation; set => SetValue(ref _settings.EnableStructuralSimulation, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxActiveFracturePieces" />
|
||||
public System.Int32 MaxActiveFracturePieces { get => _settings.MaxActiveFracturePieces; set => SetValue(ref _settings.MaxActiveFracturePieces, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.BlockTypeLimits" />
|
||||
public VRage.Serialization.SerializableDictionary<System.String, System.Int16> BlockTypeLimits { get => _settings.BlockTypeLimits; set => SetValue(ref _settings.BlockTypeLimits, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableScripterRole" />
|
||||
public System.Boolean EnableScripterRole { get => _settings.EnableScripterRole; set => SetValue(ref _settings.EnableScripterRole, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MinDropContainerRespawnTime" />
|
||||
public System.Int32 MinDropContainerRespawnTime { get => _settings.MinDropContainerRespawnTime; set => SetValue(ref _settings.MinDropContainerRespawnTime, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.MaxDropContainerRespawnTime" />
|
||||
public System.Int32 MaxDropContainerRespawnTime { get => _settings.MaxDropContainerRespawnTime; set => SetValue(ref _settings.MaxDropContainerRespawnTime, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableTurretsFriendlyFire" />
|
||||
public System.Boolean EnableTurretsFriendlyFire { get => _settings.EnableTurretsFriendlyFire; set => SetValue(ref _settings.EnableTurretsFriendlyFire, value); }
|
||||
|
||||
/// <inheritdoc cref="VRage.Game.MyObjectBuilder_SessionSettings.EnableSubgridDamage" />
|
||||
public System.Boolean EnableSubgridDamage { get => _settings.EnableSubgridDamage; set => SetValue(ref _settings.EnableSubgridDamage, value); }
|
||||
|
||||
|
||||
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
|
||||
{
|
||||
return viewModel._settings;
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SteamSDK;
|
||||
using Steamworks;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
@@ -15,7 +15,7 @@ namespace Torch.Server.ViewModels
|
||||
public SteamUserViewModel(ulong id)
|
||||
{
|
||||
SteamId = id;
|
||||
Name = SteamAPI.Instance.Friends.GetPersonaName(id);
|
||||
Name = SteamFriends.GetFriendPersonaName(new CSteamID(id));
|
||||
}
|
||||
|
||||
public SteamUserViewModel() : this(0) { }
|
||||
|
@@ -3,12 +3,11 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Torch.Server"
|
||||
mc:Ignorable="d"
|
||||
Title="Add Workshop Item" Height="200" Width="400">
|
||||
<DockPanel Background="LightGray">
|
||||
<DockPanel>
|
||||
<Label DockPanel.Dock="Top" Content="Add each workshop URL or ID on its own line." HorizontalAlignment="Center"/>
|
||||
<Button DockPanel.Dock="Bottom" Content="Done" Margin="5,0,5,5" Click="Done_Clicked"/>
|
||||
<TextBox x:Name="urlBlock" Margin="5,0,5,5" Background="White" AcceptsReturn="True"/>
|
||||
<TextBox x:Name="urlBlock" Margin="5,0,5,5" AcceptsReturn="True"/>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
@@ -3,27 +3,15 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Torch.Server"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ListView Grid.Row="0" x:Name="ChatItems" ItemsSource="{Binding ChatHistory}" Margin="5,5,5,5">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled"/>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<WrapPanel>
|
||||
<TextBlock Text="{Binding Timestamp}"/>
|
||||
<TextBlock Text=" "/>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
|
||||
<TextBlock Text=": "/>
|
||||
<TextBlock Text="{Binding Message}"/>
|
||||
</WrapPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<ScrollViewer x:Name="ChatScroller" Grid.Row="0" Margin="5,5,5,5" HorizontalScrollBarVisibility="Disabled">
|
||||
<TextBlock x:Name="ChatItems" TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
|
@@ -2,7 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -14,13 +16,17 @@ using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Windows.Threading;
|
||||
using NLog;
|
||||
using Torch;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using SteamSDK;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.API.Session;
|
||||
using Torch.Managers;
|
||||
using Torch.Server.Managers;
|
||||
using VRage.Game;
|
||||
|
||||
namespace Torch.Server
|
||||
{
|
||||
@@ -29,35 +35,111 @@ namespace Torch.Server
|
||||
/// </summary>
|
||||
public partial class ChatControl : UserControl
|
||||
{
|
||||
private TorchBase _server;
|
||||
private MultiplayerManager _multiplayer;
|
||||
private static Logger _log = LogManager.GetCurrentClassLogger();
|
||||
private ITorchServer _server;
|
||||
|
||||
public ChatControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.IsVisibleChanged += OnIsVisibleChanged;
|
||||
}
|
||||
|
||||
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
_log.Info($"VisibleChanged: {IsVisible}");
|
||||
if (IsVisible)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
Message.Focus();
|
||||
Keyboard.Focus(Message);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void BindServer(ITorchServer server)
|
||||
{
|
||||
_server = (TorchBase)server;
|
||||
_multiplayer = (MultiplayerManager)server.Multiplayer;
|
||||
ChatItems.Items.Clear();
|
||||
DataContext = _multiplayer;
|
||||
if (_multiplayer.ChatHistory is INotifyCollectionChanged ncc)
|
||||
ncc.CollectionChanged += ChatHistory_CollectionChanged;
|
||||
_server = server;
|
||||
|
||||
server.Initialized += Server_Initialized ;
|
||||
}
|
||||
|
||||
private void ChatHistory_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
private void Server_Initialized(ITorchServer obj)
|
||||
{
|
||||
ChatItems.ScrollToItem(ChatItems.Items.Count - 1);
|
||||
/*
|
||||
if (VisualTreeHelper.GetChildrenCount(ChatItems) > 0)
|
||||
Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
|
||||
Border border = (Border)VisualTreeHelper.GetChild(ChatItems, 0);
|
||||
ScrollViewer scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
|
||||
scrollViewer.ScrollToBottom();
|
||||
}*/
|
||||
ChatItems.Inlines.Clear();
|
||||
});
|
||||
|
||||
var sessionManager = _server.Managers.GetManager<ITorchSessionManager>();
|
||||
if (sessionManager != null)
|
||||
sessionManager.SessionStateChanged += SessionStateChanged;
|
||||
}
|
||||
|
||||
private void SessionStateChanged(ITorchSession session, TorchSessionState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case TorchSessionState.Loading:
|
||||
Dispatcher.InvokeAsync(() => ChatItems.Inlines.Clear());
|
||||
break;
|
||||
case TorchSessionState.Loaded:
|
||||
{
|
||||
var chatMgr = session.Managers.GetManager<IChatManagerClient>();
|
||||
if (chatMgr != null)
|
||||
chatMgr.MessageRecieved += OnMessageRecieved;
|
||||
}
|
||||
break;
|
||||
case TorchSessionState.Unloading:
|
||||
{
|
||||
var chatMgr = session.Managers.GetManager<IChatManagerClient>();
|
||||
if (chatMgr != null)
|
||||
chatMgr.MessageRecieved -= OnMessageRecieved;
|
||||
}
|
||||
break;
|
||||
case TorchSessionState.Unloaded:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageRecieved(TorchChatMessage msg, ref bool consumed)
|
||||
{
|
||||
InsertMessage(msg);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, Brush> _brushes = new Dictionary<string, Brush>();
|
||||
private static Brush LookupBrush(string font)
|
||||
{
|
||||
if (_brushes.TryGetValue(font, out Brush result))
|
||||
return result;
|
||||
Brush brush = typeof(Brushes).GetField(font, BindingFlags.Static)?.GetValue(null) as Brush ?? Brushes.Blue;
|
||||
_brushes.Add(font, brush);
|
||||
return brush;
|
||||
}
|
||||
|
||||
private void InsertMessage(TorchChatMessage msg)
|
||||
{
|
||||
if (Dispatcher.CheckAccess())
|
||||
{
|
||||
bool atBottom = ChatScroller.VerticalOffset + 8 > ChatScroller.ScrollableHeight;
|
||||
var span = new Span();
|
||||
span.Inlines.Add($"{msg.Timestamp} ");
|
||||
span.Inlines.Add(new Run(msg.Author) { Foreground = LookupBrush(msg.Font) });
|
||||
span.Inlines.Add($": {msg.Message}");
|
||||
span.Inlines.Add(new LineBreak());
|
||||
ChatItems.Inlines.Add(span);
|
||||
if (atBottom)
|
||||
ChatScroller.ScrollToBottom();
|
||||
}
|
||||
else
|
||||
Dispatcher.InvokeAsync(() => InsertMessage(msg));
|
||||
}
|
||||
|
||||
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -78,27 +160,20 @@ namespace Torch.Server
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
var commands = _server.Commands;
|
||||
if (commands.IsCommand(text))
|
||||
var commands = _server.CurrentSession?.Managers.GetManager<Torch.Commands.CommandManager>();
|
||||
if (commands != null && commands.IsCommand(text))
|
||||
{
|
||||
_multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", text));
|
||||
InsertMessage(new TorchChatMessage("Server", text, MyFontEnum.DarkBlue));
|
||||
_server.Invoke(() =>
|
||||
{
|
||||
var response = commands.HandleCommandFromServer(text);
|
||||
Dispatcher.BeginInvoke(() => OnMessageEntered_Callback(response));
|
||||
commands.HandleCommandFromServer(text);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_server.Multiplayer.SendMessage(text);
|
||||
_server.CurrentSession?.Managers.GetManager<IChatManagerClient>().SendMessageAsSelf(text);
|
||||
}
|
||||
Message.Text = "";
|
||||
}
|
||||
|
||||
private void OnMessageEntered_Callback(string response)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(response))
|
||||
_multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,237 +3,149 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Torch.Server.Views"
|
||||
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
Background="White">
|
||||
xmlns:managers="clr-namespace:Torch.Server.Managers"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:validationRules="clr-namespace:Torch.Server.Views.ValidationRules"
|
||||
xmlns:views="clr-namespace:Torch.Views;assembly=Torch"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Resources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<UserControl.DataContext>
|
||||
<viewModels:ConfigDedicatedViewModel />
|
||||
</UserControl.DataContext>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel Grid.Row="0">
|
||||
<Label Content="World:" DockPanel.Dock="Left" />
|
||||
<Button Content="New World" Margin="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
|
||||
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" SelectionChanged="Selector_OnSelectionChanged"/>
|
||||
<Button Content="Import World Config" Margin="3" Padding="3" DockPanel.Dock="Right" Click="ImportConfig_OnClick" ToolTip="Override the DS config with the one from the selected world." IsEnabled="{Binding ElementName=WorldList, Path=Items.Count, Mode=OneWay}"/>
|
||||
<Button Content="New World" Margin="3" Padding="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
|
||||
<ComboBox x:Name="WorldList" ItemsSource="{Binding Worlds}" SelectedItem="{Binding SelectedWorld}" Margin="3"
|
||||
SelectionChanged="Selector_OnSelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate DataType="managers:WorldViewModel">
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{Binding Checkpoint.SessionName}" FontWeight="Bold" Padding="0" />
|
||||
<Label Content="{Binding WorldPath}" Padding="5,0,0,0" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="Size (KB): " Padding="0" />
|
||||
<Label Content="{Binding WorldSizeKB}" Padding="0" />
|
||||
<Label Content="Last saved: " Padding="5,0,0,0" />
|
||||
<Label Content="{Binding Checkpoint.LastSaveTime}" Padding="0" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</DockPanel>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="7*" />
|
||||
<ColumnDefinition Width="10*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ScrollViewer Grid.Row="0" Margin="3">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<StackPanel Margin="3" DockPanel.Dock="Left">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Margin="3" DockPanel.Dock="Left">
|
||||
<Label Content="Server Name" />
|
||||
<TextBox Text="{Binding ServerName}" Margin="3,0,3,3" Width="160" />
|
||||
<Label Content="World Name" />
|
||||
<TextBox Text="{Binding WorldName}" Margin="3,0,3,3" Width="160" />
|
||||
<Label Content="Whitelist Group ID" />
|
||||
<TextBox Text="{Binding GroupId}" Margin="3,0,3,3" Width="160" />
|
||||
<Label Content="World Name" />
|
||||
<TextBox Text="{Binding WorldName}" Margin="3,0,3,3" Width="160" />
|
||||
<Label Content="Server Description" />
|
||||
<TextBox Text="{Binding ServerDescription}" Margin="3,0,3,3" Width="160" Height="100"
|
||||
AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||
<Label Content="Whitelist Group ID" />
|
||||
<TextBox Margin="3,0,3,3" Width="160" Style="{StaticResource ValidatedTextBox}">
|
||||
<TextBox.Text>
|
||||
<Binding Path="GroupId" UpdateSourceTrigger="PropertyChanged"
|
||||
ValidatesOnDataErrors="True" NotifyOnValidationError="True">
|
||||
<Binding.ValidationRules>
|
||||
<validationRules:NumberValidationRule />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<Label Content="Server IP" />
|
||||
<StackPanel Orientation="Horizontal" Margin="3,0,3,3">
|
||||
<TextBox Text="{Binding IP}" Width="100" Height="20" />
|
||||
<Label Content=":" Width="12" />
|
||||
<TextBox Text="{Binding Port}" Width="48" Height="20" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding IgnoreLastSession}" Content="Ignore Last Session" Margin="3" />
|
||||
<Label Content="Server Password"/>
|
||||
<TextBox Text="{Binding Password}" Width="160"/>
|
||||
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding AutodetectDependencies}" Content="Auto Detect Dependencies" Margin="3" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="3">
|
||||
<StackPanel Grid.Column="1" Margin="3">
|
||||
<Label Content="Mods" />
|
||||
<TextBox Text="{Binding Mods}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||
<TextBox Margin="3" Height="60" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"
|
||||
Style="{StaticResource ValidatedTextBox}">
|
||||
<TextBox.Text>
|
||||
<Binding Path="Mods" UpdateSourceTrigger="PropertyChanged"
|
||||
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
|
||||
Converter="{StaticResource ListConverterUInt64}">
|
||||
<Binding.ValidationRules>
|
||||
<validationRules:ListConverterValidationRule Type="system:UInt64" />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<Label Content="Administrators" />
|
||||
<TextBox Text="{Binding Administrators}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||
<TextBox Text="{Binding Administrators, Converter={StaticResource ListConverterString}}"
|
||||
Margin="3"
|
||||
Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto" />
|
||||
<Label Content="Reserved Players" />
|
||||
<TextBox Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"
|
||||
Style="{StaticResource ValidatedTextBox}">
|
||||
<TextBox.Text>
|
||||
<Binding Path="Reserved" UpdateSourceTrigger="PropertyChanged"
|
||||
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
|
||||
Converter="{StaticResource ListConverterUInt64}">
|
||||
<Binding.ValidationRules>
|
||||
<validationRules:ListConverterValidationRule Type="system:UInt64" />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
<Label Content="Banned Players" />
|
||||
<TextBox Text="{Binding Banned}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||
<TextBox Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"
|
||||
Style="{StaticResource ValidatedTextBox}">
|
||||
<TextBox.Text>
|
||||
<Binding Path="Banned" UpdateSourceTrigger="PropertyChanged"
|
||||
ValidatesOnDataErrors="True" NotifyOnValidationError="True"
|
||||
Converter="{StaticResource ListConverterUInt64}">
|
||||
<Binding.ValidationRules>
|
||||
<validationRules:ListConverterValidationRule Type="system:UInt64" />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
|
||||
</Grid>
|
||||
<ScrollViewer Grid.Column="1" Margin="3">
|
||||
<StackPanel DataContext="{Binding SessionSettings}">
|
||||
<Expander Header="Block Limits">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
|
||||
<Label Content="Max Blocks Per Player" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
|
||||
<Label Content="Max Grid Size" />
|
||||
</StackPanel>
|
||||
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
|
||||
<ListView ItemsSource="{Binding BlockLimits}" Margin="3">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding BlockType}" Width="150" Margin="3" />
|
||||
<TextBox Text="{Binding Limit}" Width="50" Margin="3" />
|
||||
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Multipliers">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding InventorySizeMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Inventory Size" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding RefinerySpeedMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Refinery Speed" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding AssemblerEfficiencyMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Assembler Efficiency" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding AssemblerSpeedMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Assembler Speed" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding WelderSpeedMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Welder Speed" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding GrinderSpeedMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Grinder Speed" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding HackSpeedMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Hacking Speed" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="NPCs">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<CheckBox IsChecked="{Binding EnableDrones}" Content="Enable Drones" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableEncounters}" Content="Enable Encounters" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableSpiders}" Content="Enable Spiders" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableWolves}" Content="Enable Wolves" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableCargoShips}" Content="Enable Cargo Ships" Margin="3" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Environment">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<StackPanel Orientation="Horizontal" ToolTip="Increases physics precision at the cost of performance.">
|
||||
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
|
||||
<Label Content="Physics Iterations" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
|
||||
<Label Content="Max Floating Objects" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableOxygen}" Content="Enable Oxygen" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableDestructibleBlocks}"
|
||||
Content="Enable Destructible Blocks" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableToolShake}" Content="Enable Tool Shake" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableVoxelDestruction}" Content="Enable Voxel Destruction"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableSunRotation}" Content="Enable Sun Rotation" Margin="3" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding SunRotationInterval}" Margin="3" Width="70" />
|
||||
<Label Content="Sun Rotation Interval (mins)" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableFlora}" Content="Enable Flora" Margin="3" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding FloraDensity}" Margin="3" Width="70" />
|
||||
<Label Content="Flora Density" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding FloraDensityMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Flora Density Multiplier" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding ViewDistance}" Margin="3" Width="70" />
|
||||
<Label Content="View Distance (meters)" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding WorldSize}" Margin="3" Width="70" />
|
||||
<Label Content="World Size (km)" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ComboBox SelectedItem="{Binding EnvironmentHostility}"
|
||||
ItemsSource="{Binding EnvironmentHostilityValues}" Margin="3" Width="100"
|
||||
DockPanel.Dock="Left" />
|
||||
<Label Content="Environment Hostility" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Players">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
|
||||
<Label Content="Max Players" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableAutoHealing}" Content="Auto Healing" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
|
||||
Margin="3" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
|
||||
<Label Content="Respawn Time Multiplier" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Miscellaneous">
|
||||
<StackPanel Margin="10,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
|
||||
<Label Content="Autosave Interval (minutes)" />
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
|
||||
Margin="3" />
|
||||
|
||||
<CheckBox IsChecked="{Binding EnableRemoteOwnerRemoval}"
|
||||
Content="Enable Remote Ownership Removal" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableRespawnShips}" Content="Enable Respawn Ships"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableScripterRole}" Content="Enable Scripter Role"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableSpectator}" Content="Enable Spectator Camera"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding DeleteRespawnShips}" Content="Remove Respawn Ships on Logoff"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableThrusterDamage}" Content="Enable Thruster Damage"
|
||||
Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
|
||||
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
|
||||
Margin="3" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
|
||||
Margin="3" Width="100" DockPanel.Dock="Left" />
|
||||
<Label Content="Game Mode" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
|
||||
<Label Content="Max Backup Saves" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" IgnoreDisplay ="True" />
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True"
|
||||
Width="2" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -1,6 +1,15 @@
|
||||
using System.Windows;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Server.Annotations;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
|
||||
@@ -9,15 +18,74 @@ namespace Torch.Server.Views
|
||||
/// <summary>
|
||||
/// Interaction logic for ConfigControl.xaml
|
||||
/// </summary>
|
||||
public partial class ConfigControl : UserControl
|
||||
public partial class ConfigControl : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
private InstanceManager _instanceManager;
|
||||
|
||||
private bool _configValid;
|
||||
public bool ConfigValid { get => _configValid; private set { _configValid = value; OnPropertyChanged(); } }
|
||||
|
||||
private List<BindingExpression> _bindingExpressions = new List<BindingExpression>();
|
||||
|
||||
public ConfigControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
|
||||
_instanceManager.InstanceLoaded += _instanceManager_InstanceLoaded;
|
||||
DataContext = _instanceManager.DedicatedConfig;
|
||||
|
||||
// Gets called once all children are loaded
|
||||
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(ApplyStyles));
|
||||
}
|
||||
|
||||
private void CheckValid()
|
||||
{
|
||||
ConfigValid = !_bindingExpressions.Any(x => x.HasError);
|
||||
}
|
||||
|
||||
private void ApplyStyles()
|
||||
{
|
||||
foreach (var textbox in GetAllChildren<TextBox>(this))
|
||||
{
|
||||
textbox.Style = (Style)Resources["ValidatedTextBox"];
|
||||
var binding = textbox.GetBindingExpression(TextBox.TextProperty);
|
||||
if (binding == null)
|
||||
continue;
|
||||
|
||||
_bindingExpressions.Add(binding);
|
||||
textbox.TextChanged += (sender, args) =>
|
||||
{
|
||||
binding.UpdateSource();
|
||||
CheckValid();
|
||||
};
|
||||
|
||||
textbox.LostKeyboardFocus += (sender, args) =>
|
||||
{
|
||||
if (binding.HasError)
|
||||
binding.UpdateTarget();
|
||||
CheckValid();
|
||||
};
|
||||
|
||||
CheckValid();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<T> GetAllChildren<T>(DependencyObject control) where T : DependencyObject
|
||||
{
|
||||
var children = LogicalTreeHelper.GetChildren(control).OfType<DependencyObject>();
|
||||
foreach (var child in children)
|
||||
{
|
||||
if (child is T t)
|
||||
yield return t;
|
||||
|
||||
foreach (var grandChild in GetAllChildren<T>(child))
|
||||
yield return grandChild;
|
||||
}
|
||||
}
|
||||
|
||||
private void _instanceManager_InstanceLoaded(ConfigDedicatedViewModel obj)
|
||||
{
|
||||
Dispatcher.Invoke(() => DataContext = obj);
|
||||
}
|
||||
|
||||
private void Save_OnClick(object sender, RoutedEventArgs e)
|
||||
@@ -25,20 +93,9 @@ namespace Torch.Server.Views
|
||||
_instanceManager.SaveConfig();
|
||||
}
|
||||
|
||||
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
|
||||
private void ImportConfig_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
|
||||
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Remove(vm);
|
||||
}
|
||||
|
||||
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_instanceManager.DedicatedConfig.SessionSettings, "", 0));
|
||||
}
|
||||
|
||||
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MessageBox.Show("Feature coming soon :)");
|
||||
_instanceManager.ImportSelectedWorldConfig();
|
||||
}
|
||||
|
||||
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
@@ -46,8 +103,24 @@ namespace Torch.Server.Views
|
||||
//The control doesn't update the binding before firing the event.
|
||||
if (e.AddedItems.Count > 0)
|
||||
{
|
||||
_instanceManager.SelectWorld((string)e.AddedItems[0]);
|
||||
var result = MessageBoxResult.Yes; //MessageBox.Show("Do you want to import the session settings from the selected world?", "Import Config", MessageBoxButton.YesNo);
|
||||
var world = (WorldViewModel)e.AddedItems[0];
|
||||
_instanceManager.SelectWorld(world.WorldPath, result != MessageBoxResult.Yes);
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
[NotifyPropertyChangedInvocator]
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var c = new WorldGeneratorDialog(_instanceManager);
|
||||
c.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
Torch.Server/Views/Converters/BooleanAndConverter.cs
Normal file
29
Torch.Server/Views/Converters/BooleanAndConverter.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Torch.Server.Views.Converters
|
||||
{
|
||||
public class BooleanAndConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
if (value is bool b && b == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException("BooleanAndConverter is a OneWay converter.");
|
||||
}
|
||||
}
|
||||
}
|
40
Torch.Server/Views/Converters/DefinitionToIdConverter.cs
Normal file
40
Torch.Server/Views/Converters/DefinitionToIdConverter.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using Sandbox.Definitions;
|
||||
using VRage.Game;
|
||||
|
||||
namespace Torch.Server.Views.Converters
|
||||
{
|
||||
public class DefinitionToIdConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
MyDefinitionId id = ((MyDefinitionBase) value).Id;
|
||||
string typeName = id.TypeId.ToString();
|
||||
if (typeName.StartsWith("MyObjectBuilder_"))
|
||||
typeName = typeName.Substring("MyObjectBuilder_".Length);
|
||||
string subtype = id.SubtypeName;
|
||||
return string.IsNullOrWhiteSpace(subtype) ? typeName : $"{typeName}: {subtype}";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
string[] parts = value.ToString().Split(':');
|
||||
Type type;
|
||||
try
|
||||
{
|
||||
type = Type.GetType(parts[0]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
type = Type.GetType("MyObjectBuilder_" + parts[0]);
|
||||
}
|
||||
return MyDefinitionManager.Static.GetDefinition(
|
||||
new MyDefinitionId(type, parts.Length > 1 ? parts[1].Trim() : ""));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Torch.Server.Views.Converters
|
||||
@@ -15,9 +11,6 @@ namespace Torch.Server.Views.Converters
|
||||
public object Convert(object value, Type targetType, object parameter,
|
||||
System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if (targetType != typeof(bool))
|
||||
throw new InvalidOperationException("The target must be a boolean");
|
||||
|
||||
return !(bool)value;
|
||||
}
|
||||
|
||||
|
54
Torch.Server/Views/Converters/ListConverter.cs
Normal file
54
Torch.Server/Views/Converters/ListConverter.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace Torch.Server.Views.Converters
|
||||
{
|
||||
public class ListConverter : IValueConverter
|
||||
{
|
||||
public Type Type { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (!(value is IList list))
|
||||
throw new InvalidOperationException("Value is not the proper type.");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var item in list)
|
||||
{
|
||||
sb.AppendLine(item.ToString());
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(Type));
|
||||
var converter = TypeDescriptor.GetConverter(Type);
|
||||
var input = ((string)value).Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var item in input)
|
||||
{
|
||||
try
|
||||
{
|
||||
list.Add(converter.ConvertFromString(item));
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new InvalidOperationException("Could not convert input value.");
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,6 +5,8 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Torch.Server.Views.Blocks"
|
||||
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.Blocks"
|
||||
xmlns:entities="clr-namespace:Torch.Server.Views.Entities"
|
||||
xmlns:entities1="clr-namespace:Torch.Server.ViewModels.Entities"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.DataContext>
|
||||
<blocks:BlockViewModel />
|
||||
@@ -13,6 +15,7 @@
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<Label Content="{Binding FullName}" FontSize="16" />
|
||||
@@ -22,22 +25,27 @@
|
||||
</StackPanel>
|
||||
<Label Content="Properties"/>
|
||||
</StackPanel>
|
||||
<ListView Grid.Row="1" ItemsSource="{Binding Properties}" Margin="3" IsEnabled="True">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:PropertyView />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
<Expander Grid.Row="1" Header="Block Properties" IsExpanded="true">
|
||||
<ListView ItemsSource="{Binding Properties}" Margin="3" IsEnabled="True">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:PropertyView />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="Focusable"
|
||||
<Setter Property="Focusable"
|
||||
Value="false" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
</Expander>
|
||||
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
|
||||
<entities:EntityControlsView DataContext="{Binding}"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -28,6 +28,15 @@ namespace Torch.Server.Views.Blocks
|
||||
public BlockView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
|
||||
UpdateResourceDict(ThemeControl.currentTheme);
|
||||
}
|
||||
|
||||
public void UpdateResourceDict(ResourceDictionary dictionary)
|
||||
{
|
||||
this.Resources.MergedDictionaries.Clear();
|
||||
this.Resources.MergedDictionaries.Add(dictionary);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -3,8 +3,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Torch.Server.Views.Blocks"
|
||||
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.Blocks"
|
||||
xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
|
@@ -26,6 +26,15 @@ namespace Torch.Server.Views.Blocks
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += OnDataContextChanged;
|
||||
|
||||
ThemeControl.UpdateDynamicControls += UpdateResourceDict;
|
||||
UpdateResourceDict(ThemeControl.currentTheme);
|
||||
}
|
||||
|
||||
public void UpdateResourceDict(ResourceDictionary dictionary)
|
||||
{
|
||||
this.Resources.MergedDictionaries.Clear();
|
||||
this.Resources.MergedDictionaries.Add(dictionary);
|
||||
}
|
||||
|
||||
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs args)
|
||||
|
30
Torch.Server/Views/Entities/CharacterView.xaml
Normal file
30
Torch.Server/Views/Entities/CharacterView.xaml
Normal file
@@ -0,0 +1,30 @@
|
||||
<UserControl x:Class="Torch.Server.Views.Entities.CharacterView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
|
||||
xmlns:local="clr-namespace:Torch.Server.Views.Entities"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.DataContext>
|
||||
<entities:CharacterViewModel />
|
||||
</UserControl.DataContext>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<Label Content="Name" Width="100"/>
|
||||
<TextBox Text="{Binding Name}" Margin="3"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<Label Content="Position" Width="100"/>
|
||||
<TextBox Text="{Binding Position}" Margin="3" />
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
|
||||
<local:EntityControlsView DataContext="{Binding}"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user