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
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
#Rider directory
|
||||||
|
.idea/
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
# Making a Pull Request
|
# Making a Pull Request
|
||||||
* Fork this repository and make sure your local **master** branch is up to date with the main repository.
|
* Fork this repository and make sure your local **staging** 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**
|
* 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.
|
* 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**.
|
* 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
|
## Naming Conventions
|
||||||
* Types: **PascalCase**
|
* Types: **PascalCase**
|
||||||
|
52
Jenkins/release.ps1
Normal file
52
Jenkins/release.ps1
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
param([string] $ApiBase, [string]$tagName, [string]$authinfo, [string[]] $assetPaths)
|
||||||
|
Add-Type -AssemblyName "System.Web"
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
Authorization = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authinfo))
|
||||||
|
Accept = "application/vnd.github.v3+json"
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Write-Output("Checking if release with tag " + $tagName + " already exists...")
|
||||||
|
$release = Invoke-RestMethod -Uri ($ApiBase+"releases/tags/$tagName") -Method "GET" -Headers $headers
|
||||||
|
Write-Output(" Using existing release " + $release.id + " at " + $release.html_url)
|
||||||
|
} catch {
|
||||||
|
Write-Output(" Doesn't exist")
|
||||||
|
$rel_arg = @{
|
||||||
|
tag_name=$tagName
|
||||||
|
name="Generated $tagName"
|
||||||
|
body=""
|
||||||
|
draft=$TRUE
|
||||||
|
prerelease=$tagName.Contains("alpha") -or $tagName.Contains("beta")
|
||||||
|
}
|
||||||
|
Write-Output("Creating new release " + $tagName + "...")
|
||||||
|
$release = Invoke-RestMethod -Uri ($ApiBase+"releases") -Method "POST" -Headers $headers -Body (ConvertTo-Json($rel_arg))
|
||||||
|
Write-Output(" Created new release " + $tagName + " at " + $release.html_url)
|
||||||
|
}
|
||||||
|
|
||||||
|
$assetsApiBase = $release.assets_url
|
||||||
|
Write-Output("Checking for existing assets...")
|
||||||
|
$existingAssets = Invoke-RestMethod -Uri ($assetsApiBase) -Method "GET" -Headers $headers
|
||||||
|
$assetLabels = ($assetPaths | ForEach-Object {[System.IO.Path]::GetFileName($_)})
|
||||||
|
foreach ($asset in $existingAssets) {
|
||||||
|
if ($assetLabels -contains $asset.name) {
|
||||||
|
$uri = $asset.url
|
||||||
|
Write-Output(" Deleting old asset " + $asset.name + " (id " + $asset.id + "); URI=" + $uri)
|
||||||
|
$result = Invoke-RestMethod -Uri $uri -Method "DELETE" -Headers $headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Output("Uploading assets...")
|
||||||
|
$uploadUrl = $release.upload_url.Substring(0, $release.upload_url.LastIndexOf('{'))
|
||||||
|
foreach ($asset in $assetPaths) {
|
||||||
|
$assetName = [System.IO.Path]::GetFileName($asset)
|
||||||
|
$assetType = [System.Web.MimeMapping]::GetMimeMapping($asset)
|
||||||
|
$assetData = [System.IO.File]::ReadAllBytes($asset)
|
||||||
|
$headerExtra = $headers + @{
|
||||||
|
"Content-Type" = $assetType
|
||||||
|
Name = $assetName
|
||||||
|
}
|
||||||
|
$uri = $uploadUrl + "?name=" + $assetName
|
||||||
|
Write-Output(" Uploading " + $asset + " as " + $assetType + "; URI=" + $uri)
|
||||||
|
$result = Invoke-RestMethod -Uri $uri -Method "POST" -Headers $headerExtra -Body $assetData
|
||||||
|
Write-Output(" ID=" + $result.id + ", found at=" + $result.browser_download_url)
|
||||||
|
}
|
56
Jenkinsfile
vendored
56
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 {
|
node {
|
||||||
stage('Checkout') {
|
stage('Checkout') {
|
||||||
checkout scm
|
checkout scm
|
||||||
@@ -16,12 +31,30 @@ node {
|
|||||||
|
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
currentBuild.description = bat(returnStdout: true, script: '@powershell -File Versioning/version.ps1').trim()
|
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') {
|
stage('Test') {
|
||||||
bat 'IF NOT EXIST reports MKDIR reports'
|
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([
|
step([
|
||||||
$class: 'XUnitBuilder',
|
$class: 'XUnitBuilder',
|
||||||
thresholdMode: 1,
|
thresholdMode: 1,
|
||||||
@@ -36,22 +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
|
|
||||||
}
|
|
||||||
}
|
}
|
20
NLog.config
20
NLog.config
@@ -1,15 +1,27 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<variable name="logStamp" value="${time} ${pad:padding=-8:inner=[${level:uppercase=true}]}" />
|
||||||
|
<variable name="logContent" value="${message:withException=true}"/>
|
||||||
|
|
||||||
<targets>
|
<targets async="true">
|
||||||
<target xsi:type="File" name="main" layout="${time} [${level:uppercase=true}] ${logger}: ${message}" fileName="Logs\Torch-${shortdate}.log" />
|
<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="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>
|
</targets>
|
||||||
|
|
||||||
<rules>
|
<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="Chat" minlevel="Info" writeTo="chat" />
|
||||||
|
<!--<logger name="Torch.Managers.PatchManager.*" minlevel="Trace" writeTo="patch"/>-->
|
||||||
</rules>
|
</rules>
|
||||||
</nlog>
|
</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?
|
# What is Torch?
|
||||||
Torch is the successor to SE Server Extender and gives server admins the tools they need to keep their Space Engineers servers running smoothly. It features a user interface with live management tools and a plugin system so you can run your server exactly how you'd like. Torch is still in early development so there may be bugs and incomplete features.
|
Torch is the successor to SE Server Extender and gives server admins the tools they need to keep their Space Engineers servers running smoothly. It features a user interface with live management tools and a plugin system so you can run your server exactly how you'd like. Torch is still in early development so there may be bugs and incomplete features.
|
||||||
|
|
||||||
# Features
|
## Torch.Server
|
||||||
|
|
||||||
|
### Features
|
||||||
* WPF-based user interface
|
* WPF-based user interface
|
||||||
* Chat: interact with the game chat and run commands without having to join the game.
|
* Chat: interact with the game chat and run commands without having to join the game.
|
||||||
* Entity manager: realtime modification of ingame entities such as stopping grids and changing block settings without having to join the game
|
* Entity manager: realtime modification of ingame entities such as stopping grids and changing block settings without having to join the game
|
||||||
* Organized, easy to use configuration editor
|
* Organized, easy to use configuration editor
|
||||||
* Extensible using the Torch plugin system
|
* Extensible using the Torch plugin system
|
||||||
|
|
||||||
# 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.
|
* 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.
|
- 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.
|
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.
|
If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon.
|
||||||
[](https://www.patreon.com/bePatron?u=847269)!
|
[](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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
@@ -17,21 +18,25 @@ namespace Torch.API
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the session begins loading.
|
/// Fired when the session begins loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||||
event Action SessionLoading;
|
event Action SessionLoading;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the session finishes loading.
|
/// Fired when the session finishes loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||||
event Action SessionLoaded;
|
event Action SessionLoaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires when the session begins unloading.
|
/// Fires when the session begins unloading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||||
event Action SessionUnloading;
|
event Action SessionUnloading;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the session finishes unloading.
|
/// Fired when the session finishes unloading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Prefer using the TorchSessionManager.SessionStateChanged event")]
|
||||||
event Action SessionUnloaded;
|
event Action SessionUnloaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -44,10 +49,6 @@ namespace Torch.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
ITorchConfig Config { get; }
|
ITorchConfig Config { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IMultiplayerManager"/>
|
|
||||||
[Obsolete]
|
|
||||||
IMultiplayerManager Multiplayer { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IPluginManager"/>
|
/// <inheritdoc cref="IPluginManager"/>
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
IPluginManager Plugins { get; }
|
IPluginManager Plugins { get; }
|
||||||
@@ -55,52 +56,83 @@ namespace Torch.API
|
|||||||
/// <inheritdoc cref="IDependencyManager"/>
|
/// <inheritdoc cref="IDependencyManager"/>
|
||||||
IDependencyManager Managers { get; }
|
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>
|
/// <summary>
|
||||||
/// The binary version of the current instance.
|
/// The binary version of the current instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Version TorchVersion { get; }
|
InformationalVersion TorchVersion { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoke an action on the game thread.
|
/// Invoke an action on the game thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Invoke(Action action);
|
void Invoke(Action action, [CallerMemberName] string caller = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoke an action on the game thread and block until it has completed.
|
/// 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>
|
/// </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>
|
/// <summary>
|
||||||
/// Invoke an action on the game thread asynchronously.
|
/// Invoke an action on the game thread asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task InvokeAsync(Action action);
|
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop the Torch instance.
|
/// Signals the torch instance to stop, then blocks until it's stopped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restart the Torch instance.
|
/// Restart the Torch instance, blocking until the restart has been performed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Restart();
|
void Restart();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a save of the game.
|
/// Initializes a save of the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="callerId">Id of the player who initiated the save.</param>
|
/// <param name="timeoutMs">timeout before the save is treated as failed, or -1 for no timeout</param>
|
||||||
Task Save(long callerId);
|
/// <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>
|
/// <summary>
|
||||||
/// Initialize the Torch instance.
|
/// Initialize the Torch instance. Before this <see cref="Start"/> is invalid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Init();
|
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>
|
/// <summary>
|
||||||
@@ -108,10 +140,20 @@ namespace Torch.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITorchServer : ITorchBase
|
public interface ITorchServer : ITorchBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current <see cref="ServerState"/>
|
||||||
|
/// </summary>
|
||||||
|
ServerState State { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path of the dedicated instance folder.
|
/// Path of the dedicated instance folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string InstancePath { get; }
|
string InstancePath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when the server's Init() method has completed.
|
||||||
|
/// </summary>
|
||||||
|
event Action<ITorchServer> Initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
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.
|
/// Register a network handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void RegisterNetworkHandler(INetworkHandler handler);
|
void RegisterNetworkHandler(INetworkHandler handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister a network handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the handler was unregistered, false if it wasn't registered to begin with</returns>
|
||||||
|
bool UnregisterNetworkHandler(INetworkHandler handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -33,6 +39,7 @@ namespace Torch.API.Managers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes a network message.
|
/// Processes a network message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>true if the message should be discarded</returns>
|
||||||
bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet);
|
bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,12 +14,12 @@ namespace Torch.API.Managers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when plugins are loaded.
|
/// Fired when plugins are loaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event Action<IList<ITorchPlugin>> PluginsLoaded;
|
event Action<IReadOnlyCollection<ITorchPlugin>> PluginsLoaded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Collection of loaded plugins.
|
/// Collection of loaded plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IList<ITorchPlugin> Plugins { get; }
|
IReadOnlyDictionary<Guid, ITorchPlugin> Plugins { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates all loaded plugins.
|
/// Updates all loaded plugins.
|
||||||
|
@@ -17,7 +17,7 @@ namespace Torch.API.Plugins
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The version of the plugin.
|
/// The version of the plugin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Version Version { get; }
|
string Version { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the plugin.
|
/// The name of the plugin.
|
||||||
|
@@ -10,6 +10,7 @@ namespace Torch.API.Plugins
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the given type should be loaded by the plugin manager as a plugin.
|
/// Indicates that the given type should be loaded by the plugin manager as a plugin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("All plugin meta-information is now defined in the manifest.xml.")]
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class PluginAttribute : Attribute
|
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"/>
|
/// <inheritdoc cref="IDependencyManager"/>
|
||||||
IDependencyManager Managers { get; }
|
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>
|
/// </summary>
|
||||||
ITorchSession CurrentSession { get; }
|
ITorchSession CurrentSession { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when any <see cref="ITorchSession"/> <see cref="ITorchSession.State"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
event TorchSessionStateChangedDel SessionStateChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the given factory as a supplier for session based managers
|
/// Adds the given factory as a supplier for session based managers
|
||||||
/// </summary>
|
/// </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>
|
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SteamSDK, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
<HintPath>..\GameBinaries\SteamSDK.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -160,15 +155,23 @@
|
|||||||
<Link>Properties\AssemblyVersion.cs</Link>
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="ConnectionState.cs" />
|
<Compile Include="ConnectionState.cs" />
|
||||||
<Compile Include="IChatMessage.cs" />
|
<Compile Include="InformationalVersion.cs" />
|
||||||
<Compile Include="ITorchConfig.cs" />
|
<Compile Include="ITorchConfig.cs" />
|
||||||
<Compile Include="Managers\DependencyManagerExtensions.cs" />
|
<Compile Include="Managers\DependencyManagerExtensions.cs" />
|
||||||
<Compile Include="Managers\DependencyProviderExtensions.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\IDependencyManager.cs" />
|
||||||
<Compile Include="Managers\IDependencyProvider.cs" />
|
<Compile Include="Managers\IDependencyProvider.cs" />
|
||||||
|
<Compile Include="Event\IEventManager.cs" />
|
||||||
<Compile Include="Managers\IManager.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="IPlayer.cs" />
|
||||||
|
<Compile Include="Managers\IMultiplayerManagerServer.cs" />
|
||||||
<Compile Include="Managers\INetworkManager.cs" />
|
<Compile Include="Managers\INetworkManager.cs" />
|
||||||
<Compile Include="Managers\IPluginManager.cs" />
|
<Compile Include="Managers\IPluginManager.cs" />
|
||||||
<Compile Include="Plugins\ITorchPlugin.cs" />
|
<Compile Include="Plugins\ITorchPlugin.cs" />
|
||||||
@@ -180,8 +183,11 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServerState.cs" />
|
<Compile Include="ServerState.cs" />
|
||||||
<Compile Include="ModAPI\TorchAPI.cs" />
|
<Compile Include="ModAPI\TorchAPI.cs" />
|
||||||
|
<Compile Include="Session\GameSaveResult.cs" />
|
||||||
<Compile Include="Session\ITorchSession.cs" />
|
<Compile Include="Session\ITorchSession.cs" />
|
||||||
<Compile Include="Session\ITorchSessionManager.cs" />
|
<Compile Include="Session\ITorchSessionManager.cs" />
|
||||||
|
<Compile Include="Session\TorchSessionState.cs" />
|
||||||
|
<Compile Include="TorchGameState.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
@@ -189,6 +195,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
</Project>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(SolutionDir)\TransformOnBuild.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.Client;
|
||||||
using Torch.Tests;
|
using Torch.Tests;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
@@ -29,6 +30,10 @@ namespace Torch.Client.Tests
|
|||||||
|
|
||||||
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Events => Manager().Events;
|
||||||
|
|
||||||
#region Binding
|
#region Binding
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(Getters))]
|
[MemberData(nameof(Getters))]
|
||||||
@@ -62,6 +67,28 @@ namespace Torch.Client.Tests
|
|||||||
if (field.Field.IsStatic)
|
if (field.Field.IsStatic)
|
||||||
Assert.NotNull(field.Field.GetValue(null));
|
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
|
#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";
|
public const string SpaceEngineersBinaries = "Bin64";
|
||||||
private static string _spaceEngInstallAlias = null;
|
private static string _spaceEngInstallAlias = null;
|
||||||
|
|
||||||
public static string SpaceEngineersInstallAlias
|
public static string SpaceEngineersInstallAlias
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -26,7 +27,8 @@ namespace Torch.Client
|
|||||||
if (_spaceEngInstallAlias == null)
|
if (_spaceEngInstallAlias == null)
|
||||||
{
|
{
|
||||||
// ReSharper disable once AssignNullToNotNullAttribute
|
// 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;
|
return _spaceEngInstallAlias;
|
||||||
}
|
}
|
||||||
@@ -52,16 +54,20 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
AllocConsole();
|
AllocConsole();
|
||||||
#endif
|
#endif
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
if (!TorchLauncher.IsTorchWrapped())
|
||||||
|
|
||||||
// Early config: Resolve SE install directory.
|
|
||||||
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
|
|
||||||
SetupSpaceEngInstallAlias();
|
|
||||||
|
|
||||||
using (new TorchAssemblyResolver(Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries)))
|
|
||||||
{
|
{
|
||||||
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
|
#if DEBUG
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -77,7 +83,8 @@ namespace Torch.Client
|
|||||||
|
|
||||||
// TODO look at Steam/config/Config.VDF? Has alternate directories.
|
// TODO look at Steam/config/Config.VDF? Has alternate directories.
|
||||||
var steamDir =
|
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)
|
if (steamDir != null)
|
||||||
{
|
{
|
||||||
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
|
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
|
||||||
@@ -85,7 +92,10 @@ namespace Torch.Client
|
|||||||
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
|
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
|
||||||
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
|
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_log.Debug("Couldn't find Space Engineers in {0}", spaceEngineersDirectory);
|
_log.Debug("Couldn't find Space Engineers in {0}", spaceEngineersDirectory);
|
||||||
|
spaceEngineersDirectory = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (spaceEngineersDirectory == null)
|
if (spaceEngineersDirectory == null)
|
||||||
{
|
{
|
||||||
@@ -97,7 +107,8 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
if (dialog.ShowDialog() != DialogResult.OK)
|
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);
|
_log.Fatal(ex);
|
||||||
LogManager.Flush();
|
LogManager.Flush();
|
||||||
throw ex;
|
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?",
|
$"Unable to find {0} in {1}. Are you sure it's the Space Engineers install directory?",
|
||||||
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
break;
|
break;
|
||||||
} while (true); // Repeat until they confirm.
|
} while (true); // Repeat until they confirm.
|
||||||
}
|
}
|
||||||
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
|
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);
|
_log.Fatal(ex);
|
||||||
LogManager.Flush();
|
LogManager.Flush();
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -121,7 +133,8 @@ namespace Torch.Client
|
|||||||
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
|
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
|
||||||
if (!File.Exists(junctionVerify))
|
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);
|
_log.Fatal(ex);
|
||||||
LogManager.Flush();
|
LogManager.Flush();
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -153,7 +166,7 @@ namespace Torch.Client
|
|||||||
|
|
||||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
var ex = (Exception)e.ExceptionObject;
|
var ex = (Exception) e.ExceptionObject;
|
||||||
_log.Error(ex);
|
_log.Error(ex);
|
||||||
LogManager.Flush();
|
LogManager.Flush();
|
||||||
MessageBox.Show(ex.StackTrace, ex.Message);
|
MessageBox.Show(ex.StackTrace, ex.Message);
|
||||||
|
@@ -121,11 +121,12 @@
|
|||||||
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
<Link>Properties\AssemblyVersion.cs</Link>
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Manager\MultiplayerManagerClient.cs" />
|
||||||
|
<Compile Include="Manager\MultiplayerManagerLobby.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="TorchClient.cs" />
|
<Compile Include="TorchClient.cs" />
|
||||||
<Compile Include="TorchClientConfig.cs" />
|
<Compile Include="TorchClientConfig.cs" />
|
||||||
<Compile Include="TorchConsoleScreen.cs" />
|
<Compile Include="TorchConsoleScreen.cs" />
|
||||||
<Compile Include="TorchMainMenuScreen.cs" />
|
|
||||||
<Compile Include="TorchSettingsScreen.cs" />
|
<Compile Include="TorchSettingsScreen.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
@@ -138,6 +139,9 @@
|
|||||||
<DependentUpon>Settings.settings</DependentUpon>
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="UI\TorchMainMenuScreen.cs" />
|
||||||
|
<Compile Include="UI\TorchNavScreen.cs" />
|
||||||
|
<Compile Include="UI\TorchSettingsScreen.cs" />
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
@@ -167,6 +171,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@@ -4,12 +4,18 @@ using System.IO;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
using Sandbox.Engine.Networking;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Engine.Platform;
|
using Sandbox.Engine.Platform;
|
||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
using SpaceEngineers.Game;
|
using SpaceEngineers.Game;
|
||||||
using VRage.Steam;
|
using VRage.Steam;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
|
using Torch.Client.Manager;
|
||||||
|
using Torch.Client.UI;
|
||||||
|
using Torch.Session;
|
||||||
using VRage;
|
using VRage;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.GameServices;
|
using VRage.GameServices;
|
||||||
@@ -20,13 +26,19 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
public class TorchClient : TorchBase, ITorchClient
|
public class TorchClient : TorchBase, ITorchClient
|
||||||
{
|
{
|
||||||
private MyCommonProgramStartup _startup;
|
protected override uint SteamAppId => 244850;
|
||||||
private IMyRender _renderer;
|
protected override string SteamAppName => "SpaceEngineers";
|
||||||
private const uint APP_ID = 244850;
|
|
||||||
|
|
||||||
public TorchClient()
|
public TorchClient()
|
||||||
{
|
{
|
||||||
Config = new TorchClientConfig();
|
Config = new TorchClientConfig();
|
||||||
|
var sessionManager = Managers.GetManager<ITorchSessionManager>();
|
||||||
|
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerLobby
|
||||||
|
? new MultiplayerManagerLobby(this)
|
||||||
|
: null);
|
||||||
|
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerClientBase
|
||||||
|
? new MultiplayerManagerClient(this)
|
||||||
|
: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
@@ -34,39 +46,11 @@ namespace Torch.Client
|
|||||||
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
|
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
|
||||||
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
|
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
|
||||||
Log.Info("Initializing Torch Client");
|
Log.Info("Initializing Torch Client");
|
||||||
|
Config.InstancePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
|
SteamAppName);
|
||||||
base.Init();
|
base.Init();
|
||||||
|
OverrideMenus();
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
||||||
_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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OverrideMenus()
|
private void OverrideMenus()
|
||||||
@@ -74,28 +58,16 @@ namespace Torch.Client
|
|||||||
var credits = new MyCreditsDepartment("Torch Developed By")
|
var credits = new MyCreditsDepartment("Torch Developed By")
|
||||||
{
|
{
|
||||||
Persons = new List<MyCreditsPerson>
|
Persons = new List<MyCreditsPerson>
|
||||||
{
|
{
|
||||||
new MyCreditsPerson("THE TORCH TEAM"),
|
new MyCreditsPerson("THE TORCH TEAM"),
|
||||||
new MyCreditsPerson("http://github.com/TorchSE"),
|
new MyCreditsPerson("http://github.com/TorchSE"),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
MyPerGameSettings.Credits.Departments.Insert(0, credits);
|
MyPerGameSettings.Credits.Departments.Insert(0, credits);
|
||||||
|
|
||||||
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Start()
|
|
||||||
{
|
|
||||||
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)
|
private void SetRenderWindowTitle(string title)
|
||||||
{
|
{
|
||||||
MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
|
MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
|
||||||
@@ -105,66 +77,16 @@ namespace Torch.Client
|
|||||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
if (renderWindowField == null)
|
if (renderWindowField == null)
|
||||||
return;
|
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)
|
if (window != null)
|
||||||
renderThread.Invoke(() =>
|
renderThread.Invoke(() => { window.Text = title; });
|
||||||
{
|
|
||||||
window.Text = title;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e)
|
public override void Restart()
|
||||||
{
|
{
|
||||||
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
throw new NotImplementedException();
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
@@ -46,6 +47,10 @@
|
|||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Xml" />
|
<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">
|
<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>
|
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@@ -65,6 +70,7 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="TorchServerReflectionTest.cs" />
|
<Compile Include="TorchServerReflectionTest.cs" />
|
||||||
|
<Compile Include="TorchServerSessionSettingsTest.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
@@ -88,6 +94,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
</Project>
|
</Project>
|
@@ -1,10 +1,12 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Torch.Tests;
|
using Torch.Tests;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Torch.Server.Tests
|
namespace Torch.Server.Tests
|
||||||
{
|
{
|
||||||
|
#warning Disabled reflection tests because of seemingly random failures
|
||||||
public class TorchServerReflectionTest
|
public class TorchServerReflectionTest
|
||||||
{
|
{
|
||||||
static TorchServerReflectionTest()
|
static TorchServerReflectionTest()
|
||||||
@@ -28,8 +30,12 @@ namespace Torch.Server.Tests
|
|||||||
|
|
||||||
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Events => Manager().Events;
|
||||||
|
|
||||||
#region Binding
|
#region Binding
|
||||||
[Theory]
|
//[Theory]
|
||||||
[MemberData(nameof(Getters))]
|
[MemberData(nameof(Getters))]
|
||||||
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
{
|
{
|
||||||
@@ -40,7 +46,7 @@ namespace Torch.Server.Tests
|
|||||||
Assert.NotNull(field.Field.GetValue(null));
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
//[Theory]
|
||||||
[MemberData(nameof(Setters))]
|
[MemberData(nameof(Setters))]
|
||||||
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
{
|
{
|
||||||
@@ -51,7 +57,7 @@ namespace Torch.Server.Tests
|
|||||||
Assert.NotNull(field.Field.GetValue(null));
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
//[Theory]
|
||||||
[MemberData(nameof(Invokers))]
|
[MemberData(nameof(Invokers))]
|
||||||
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
{
|
{
|
||||||
@@ -61,6 +67,17 @@ namespace Torch.Server.Tests
|
|||||||
if (field.Field.IsStatic)
|
if (field.Field.IsStatic)
|
||||||
Assert.NotNull(field.Field.GetValue(null));
|
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
|
#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.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Threading;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NLog.Targets;
|
||||||
|
using Sandbox.Engine.Utils;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
|
using VRage.FileSystem;
|
||||||
|
using VRage.Library.Exceptions;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
@@ -21,12 +28,12 @@ namespace Torch.Server
|
|||||||
private const string STEAMCMD_ZIP = "temp.zip";
|
private const string STEAMCMD_ZIP = "temp.zip";
|
||||||
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
||||||
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
||||||
|
|
||||||
private const string RUNSCRIPT = @"force_install_dir ../
|
private const string RUNSCRIPT = @"force_install_dir ../
|
||||||
login anonymous
|
login anonymous
|
||||||
app_update 298740
|
app_update 298740
|
||||||
quit";
|
quit";
|
||||||
|
|
||||||
private TorchAssemblyResolver _resolver;
|
|
||||||
private TorchConfig _config;
|
private TorchConfig _config;
|
||||||
private TorchServer _server;
|
private TorchServer _server;
|
||||||
private string _basePath;
|
private string _basePath;
|
||||||
@@ -44,12 +51,21 @@ quit";
|
|||||||
if (_init)
|
if (_init)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
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();
|
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();
|
_config = InitConfig();
|
||||||
if (!_config.Parse(args))
|
if (!_config.Parse(args))
|
||||||
return false;
|
return false;
|
||||||
@@ -61,13 +77,12 @@ quit";
|
|||||||
var pid = int.Parse(_config.WaitForPID);
|
var pid = int.Parse(_config.WaitForPID);
|
||||||
var waitProc = Process.GetProcessById(pid);
|
var waitProc = Process.GetProcessById(pid);
|
||||||
Log.Info("Continuing in 5 seconds.");
|
Log.Info("Continuing in 5 seconds.");
|
||||||
Thread.Sleep(5000);
|
Log.Warn($"Waiting for process {pid} to close");
|
||||||
if (!waitProc.HasExited)
|
while (!waitProc.HasExited)
|
||||||
{
|
{
|
||||||
Log.Warn($"Killing old process {pid}.");
|
Console.Write(".");
|
||||||
waitProc.Kill();
|
Thread.Sleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -82,19 +97,29 @@ quit";
|
|||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
_server = new TorchServer(_config);
|
_server = new TorchServer(_config);
|
||||||
_server.Init();
|
var init = Task.Run(() => _server.Init()).ContinueWith(x =>
|
||||||
|
|
||||||
if (_config.NoGui || _config.Autostart)
|
|
||||||
{
|
{
|
||||||
new Thread(_server.Start).Start();
|
if (!x.IsFaulted)
|
||||||
}
|
return;
|
||||||
|
|
||||||
|
Log.Error("Error initializing server.");
|
||||||
|
LogException(x.Exception);
|
||||||
|
});
|
||||||
if (!_config.NoGui)
|
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();
|
new TorchUI(_server).ShowDialog();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
_resolver?.Dispose();
|
{
|
||||||
|
init.Wait();
|
||||||
|
_server.Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TorchConfig InitConfig()
|
private TorchConfig InitConfig()
|
||||||
@@ -109,13 +134,13 @@ quit";
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Info($"Generating default config at {configPath}");
|
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);
|
config.Save(configPath);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunSteamCmd()
|
public static void RunSteamCmd()
|
||||||
{
|
{
|
||||||
var log = LogManager.GetLogger("SteamCMD");
|
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)
|
private void HandleException(object sender, UnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
var ex = (Exception)e.ExceptionObject;
|
var ex = (Exception)e.ExceptionObject;
|
||||||
Log.Fatal(ex);
|
LogException(ex);
|
||||||
Console.WriteLine("Exiting in 5 seconds.");
|
if (MyFakes.ENABLE_MINIDUMP_SENDING)
|
||||||
Thread.Sleep(5000);
|
{
|
||||||
|
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)
|
if (_config.RestartOnCrash)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("Restarting in 5 seconds.");
|
||||||
|
Thread.Sleep(5000);
|
||||||
var exe = typeof(Program).Assembly.Location;
|
var exe = typeof(Program).Assembly.Location;
|
||||||
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||||
Process.Start(exe, _config.ToString());
|
Process.Start(exe, _config.ToString());
|
||||||
}
|
}
|
||||||
//1627 = Function failed during execution.
|
else
|
||||||
Environment.Exit(1627);
|
{
|
||||||
|
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 NLog;
|
||||||
using Sandbox.Engine.Networking;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Engine.Utils;
|
using Sandbox.Engine.Utils;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
|
using Torch.Mod;
|
||||||
using Torch.Server.ViewModels;
|
using Torch.Server.ViewModels;
|
||||||
|
using VRage;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
|
using VRage.Game.ObjectBuilder;
|
||||||
using VRage.ObjectBuilders;
|
using VRage.ObjectBuilders;
|
||||||
|
using VRage.Plugins;
|
||||||
|
|
||||||
namespace Torch.Server.Managers
|
namespace Torch.Server.Managers
|
||||||
{
|
{
|
||||||
public class InstanceManager : Manager
|
public class InstanceManager : Manager
|
||||||
{
|
{
|
||||||
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
||||||
|
|
||||||
|
public event Action<ConfigDedicatedViewModel> InstanceLoaded;
|
||||||
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
||||||
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
|
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
|
||||||
[Dependency]
|
[Dependency]
|
||||||
@@ -33,22 +41,14 @@ 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)
|
public void LoadInstance(string path, bool validate = true)
|
||||||
{
|
{
|
||||||
|
Log.Info($"Loading instance {path}");
|
||||||
|
|
||||||
if (validate)
|
if (validate)
|
||||||
ValidateInstance(path);
|
ValidateInstance(path);
|
||||||
|
|
||||||
MyFileSystem.Reset();
|
MyFileSystem.Reset();
|
||||||
MyFileSystem.ExePath = Path.Combine(_filesystemManager.TorchDirectory, "DedicatedServer64");
|
|
||||||
MyFileSystem.Init("Content", path);
|
MyFileSystem.Init("Content", path);
|
||||||
//Initializes saves path. Why this isn't in Init() we may never know.
|
//Initializes saves path. Why this isn't in Init() we may never know.
|
||||||
MyFileSystem.InitUserSpecific(null);
|
MyFileSystem.InitUserSpecific(null);
|
||||||
@@ -64,33 +64,73 @@ namespace Torch.Server.Managers
|
|||||||
config.Load(configPath);
|
config.Load(configPath);
|
||||||
|
|
||||||
DedicatedConfig = new ConfigDedicatedViewModel(config);
|
DedicatedConfig = new ConfigDedicatedViewModel(config);
|
||||||
|
|
||||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
|
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
|
||||||
|
|
||||||
foreach (var f in worldFolders)
|
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}.");
|
Log.Warn($"No worlds found in the current instance {path}.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImportWorldConfig();
|
SelectWorld(DedicatedConfig.LoadWorld ?? DedicatedConfig.Worlds.First().WorldPath, false);
|
||||||
|
|
||||||
/*
|
InstanceLoaded?.Invoke(DedicatedConfig);
|
||||||
if (string.IsNullOrEmpty(DedicatedConfig.LoadWorld))
|
|
||||||
{
|
|
||||||
Log.Warn("No world specified, importing first available world.");
|
|
||||||
SelectWorld(DedicatedConfig.WorldPaths[0], false);
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SelectWorld(string worldPath, bool modsOnly = true)
|
public void SelectWorld(string worldPath, bool modsOnly = true)
|
||||||
{
|
{
|
||||||
DedicatedConfig.LoadWorld = worldPath;
|
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)
|
private void ImportWorldConfig(bool modsOnly = true)
|
||||||
{
|
{
|
||||||
@@ -107,15 +147,11 @@ namespace Torch.Server.Managers
|
|||||||
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||||
if (checkpoint == null)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
DedicatedConfig.Mods = checkpoint.Mods.Select(x => x.PublishedFileId).ToList();
|
||||||
foreach (var mod in checkpoint.Mods)
|
|
||||||
sb.AppendLine(mod.PublishedFileId.ToString());
|
|
||||||
|
|
||||||
DedicatedConfig.Mods = sb.ToString();
|
|
||||||
|
|
||||||
Log.Debug("Loaded mod list from world");
|
Log.Debug("Loaded mod list from world");
|
||||||
|
|
||||||
@@ -131,23 +167,39 @@ namespace Torch.Server.Managers
|
|||||||
|
|
||||||
public void SaveConfig()
|
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.");
|
Log.Info("Saved dedicated config.");
|
||||||
|
|
||||||
try
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkpoint.SessionName = DedicatedConfig.WorldName;
|
||||||
checkpoint.Settings = DedicatedConfig.SessionSettings;
|
checkpoint.Settings = DedicatedConfig.SessionSettings;
|
||||||
checkpoint.Mods.Clear();
|
checkpoint.Mods.Clear();
|
||||||
foreach (var modId in DedicatedConfig.Model.Mods)
|
|
||||||
|
foreach (var modId in DedicatedConfig.Mods)
|
||||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
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.");
|
Log.Info("Saved world config.");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -172,4 +224,44 @@ namespace Torch.Server.Managers
|
|||||||
config.Save(configPath);
|
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;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Configuration.Install;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.IO.Compression;
|
||||||
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Threading;
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using Sandbox.Game.World;
|
using NLog.Targets;
|
||||||
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 Torch.Utils;
|
using Torch.Utils;
|
||||||
using VRage.FileSystem;
|
|
||||||
using VRageRender;
|
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
@@ -39,18 +21,22 @@ namespace Torch.Server
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
Target.Register<FlowDocumentTarget>("FlowDocument");
|
||||||
//Ensures that all the files are downloaded in the Torch directory.
|
//Ensures that all the files are downloaded in the Torch directory.
|
||||||
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
|
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
|
||||||
var binDir = Path.Combine(workingDir, "DedicatedServer64");
|
var binDir = Path.Combine(workingDir, "DedicatedServer64");
|
||||||
Directory.SetCurrentDirectory(workingDir);
|
Directory.SetCurrentDirectory(workingDir);
|
||||||
|
|
||||||
|
if (!TorchLauncher.IsTorchWrapped())
|
||||||
|
{
|
||||||
|
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName, args, binDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Environment.UserInteractive)
|
if (!Environment.UserInteractive)
|
||||||
{
|
{
|
||||||
using (var service = new TorchService())
|
using (var service = new TorchService())
|
||||||
using (new TorchAssemblyResolver(binDir))
|
|
||||||
{
|
|
||||||
ServiceBase.Run(service);
|
ServiceBase.Run(service);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 />
|
<TargetFrameworkProfile />
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</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>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -44,11 +59,17 @@
|
|||||||
<ApplicationIcon>torchicon.ico</ApplicationIcon>
|
<ApplicationIcon>torchicon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<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">
|
<Reference Include="HavokWrapper, Version=1.0.6051.28726, Culture=neutral, processorArchitecture=AMD64">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</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">
|
<Reference Include="Microsoft.CodeAnalysis, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.dll</HintPath>
|
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.dll</HintPath>
|
||||||
@@ -96,18 +117,20 @@
|
|||||||
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
|
<HintPath>..\GameBinaries\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SteamSDK, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
<Reference Include="Steamworks.NET">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<HintPath>..\GameBinaries\Steamworks.NET.dll</HintPath>
|
||||||
<HintPath>..\GameBinaries\SteamSDK.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
<Reference Include="System.Configuration.Install" />
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.IO.Compression.FileSystem" />
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
<Reference Include="System.ServiceProcess" />
|
<Reference Include="System.ServiceProcess" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<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="System.Xml" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -182,6 +205,10 @@
|
|||||||
<HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath>
|
<HintPath>..\GameBinaries\VRage.Scripting.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="VRage.Steam">
|
||||||
|
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="WindowsBase" />
|
<Reference Include="WindowsBase" />
|
||||||
<Reference Include="PresentationCore" />
|
<Reference Include="PresentationCore" />
|
||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
@@ -190,12 +217,18 @@
|
|||||||
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
<Link>Properties\AssemblyVersion.cs</Link>
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Commands\WhitelistCommands.cs" />
|
||||||
|
<Compile Include="FlowDocumentTarget.cs" />
|
||||||
<Compile Include="ListBoxExtensions.cs" />
|
<Compile Include="ListBoxExtensions.cs" />
|
||||||
|
<Compile Include="Managers\EntityControlManager.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
||||||
<Compile Include="Managers\InstanceManager.cs" />
|
<Compile Include="Managers\InstanceManager.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicatedEventShim.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicatedPatchShim.cs" />
|
||||||
<Compile Include="NativeMethods.cs" />
|
<Compile Include="NativeMethods.cs" />
|
||||||
<Compile Include="Initializer.cs" />
|
<Compile Include="Initializer.cs" />
|
||||||
|
<Compile Include="Properties\Annotations.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServerStatistics.cs" />
|
|
||||||
<Compile Include="TorchConfig.cs" />
|
<Compile Include="TorchConfig.cs" />
|
||||||
<Compile Include="TorchService.cs">
|
<Compile Include="TorchService.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
@@ -204,11 +237,32 @@
|
|||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
|
<Compile Include="ViewModels\BlockLimitViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\CheckpointViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
|
<Compile Include="ViewModels\Entities\Blocks\BlockViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
|
<Compile Include="ViewModels\Entities\Blocks\BlockViewModelGenerator.cs" />
|
||||||
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
|
<Compile Include="ViewModels\Entities\Blocks\PropertyViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\CharacterViewModel.cs" />
|
<Compile Include="ViewModels\Entities\CharacterViewModel.cs" />
|
||||||
<Compile Include="ViewModels\ConfigDedicatedViewModel.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\EntityTreeViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\EntityViewModel.cs" />
|
<Compile Include="ViewModels\Entities\EntityViewModel.cs" />
|
||||||
<Compile Include="ViewModels\Entities\FloatingObjectViewModel.cs" />
|
<Compile Include="ViewModels\Entities\FloatingObjectViewModel.cs" />
|
||||||
@@ -216,7 +270,6 @@
|
|||||||
<Compile Include="ViewModels\ILazyLoad.cs" />
|
<Compile Include="ViewModels\ILazyLoad.cs" />
|
||||||
<Compile Include="ViewModels\PluginManagerViewModel.cs" />
|
<Compile Include="ViewModels\PluginManagerViewModel.cs" />
|
||||||
<Compile Include="ViewModels\PluginViewModel.cs" />
|
<Compile Include="ViewModels\PluginViewModel.cs" />
|
||||||
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
|
|
||||||
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
|
<Compile Include="ViewModels\Entities\VoxelMapViewModel.cs" />
|
||||||
<Compile Include="ViewModels\SteamUserViewModel.cs" />
|
<Compile Include="ViewModels\SteamUserViewModel.cs" />
|
||||||
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
|
||||||
@@ -247,15 +300,15 @@
|
|||||||
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
|
<Compile Include="Views\Entities\VoxelMapView.xaml.cs">
|
||||||
<DependentUpon>VoxelMapView.xaml</DependentUpon>
|
<DependentUpon>VoxelMapView.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Views\FirstTimeSetup.xaml.cs">
|
|
||||||
<DependentUpon>FirstTimeSetup.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Views\ModsControl.xaml.cs">
|
<Compile Include="Views\ModsControl.xaml.cs">
|
||||||
<DependentUpon>ModsControl.xaml</DependentUpon>
|
<DependentUpon>ModsControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Views\PluginsControl.xaml.cs">
|
<Compile Include="Views\PluginsControl.xaml.cs">
|
||||||
<DependentUpon>PluginsControl.xaml</DependentUpon>
|
<DependentUpon>PluginsControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Views\SessionSettingsView.xaml.cs">
|
||||||
|
<DependentUpon>SessionSettingsView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Views\TorchUI.xaml.cs">
|
<Compile Include="Views\TorchUI.xaml.cs">
|
||||||
<DependentUpon>TorchUI.xaml</DependentUpon>
|
<DependentUpon>TorchUI.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -264,6 +317,10 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="TorchServer.cs" />
|
<Compile Include="TorchServer.cs" />
|
||||||
|
<Compile Include="Views\ValidationRules\NumberValidationRule.cs" />
|
||||||
|
<Compile Include="Views\WorldGeneratorDialog.xaml.cs">
|
||||||
|
<DependentUpon>WorldGeneratorDialog.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
@@ -300,6 +357,30 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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">
|
<Page Include="Views\AddWorkshopItemsDialog.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@@ -324,26 +405,38 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\Entities\CharacterView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\Entities\GridView.xaml">
|
<Page Include="Views\Entities\GridView.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\PluginsControl.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\Entities\VoxelMapView.xaml">
|
<Page Include="Views\Entities\VoxelMapView.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
<Page Include="Views\FirstTimeSetup.xaml">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Include="Views\ModsControl.xaml">
|
<Page Include="Views\ModsControl.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
<Page Include="Views\PluginsControl.xaml">
|
<Page Include="Views\Resources.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</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">
|
<Page Include="Views\TorchUI.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
@@ -352,6 +445,10 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\WorldGeneratorDialog.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="torchicon.ico" />
|
<Resource Include="torchicon.ico" />
|
||||||
@@ -359,7 +456,18 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</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="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@@ -60,8 +60,22 @@ namespace Torch.Server
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public List<string> Plugins { get; set; } = new List<string>();
|
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 WindowSize { get; set; } = new Point(800, 600);
|
||||||
internal Point WindowPosition { get; set; } = new Point();
|
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]
|
[XmlIgnore]
|
||||||
private string _path;
|
private string _path;
|
||||||
|
|
||||||
|
@@ -1,35 +1,35 @@
|
|||||||
using Sandbox;
|
#region
|
||||||
using Sandbox.Engine.Utils;
|
|
||||||
using Sandbox.Game;
|
|
||||||
using Sandbox.Game.World;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Net;
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Text;
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Xml.Serialization.GeneratedAssembly;
|
using System.Windows.Forms;
|
||||||
using Sandbox.Engine.Analytics;
|
using NLog;
|
||||||
|
using Sandbox;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Game.Multiplayer;
|
using Sandbox.Game.Multiplayer;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.Game.World;
|
||||||
using SteamSDK;
|
|
||||||
using Torch.API;
|
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.Server.Managers;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
|
using VRage;
|
||||||
using VRage.Dedicated;
|
using VRage.Dedicated;
|
||||||
using VRage.FileSystem;
|
using VRage.Dedicated.RemoteAPI;
|
||||||
using VRage.Game;
|
using VRage.GameServices;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Steam;
|
||||||
using VRage.Game.ObjectBuilder;
|
using Timer = System.Threading.Timer;
|
||||||
using VRage.Game.SessionComponents;
|
|
||||||
using VRage.Library;
|
#endregion
|
||||||
using VRage.ObjectBuilders;
|
|
||||||
using VRage.Plugins;
|
|
||||||
using VRage.Utils;
|
|
||||||
|
|
||||||
#pragma warning disable 618
|
#pragma warning disable 618
|
||||||
|
|
||||||
@@ -37,167 +37,100 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
public class TorchServer : TorchBase, ITorchServer
|
public class TorchServer : TorchBase, ITorchServer
|
||||||
{
|
{
|
||||||
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
private bool _canRun;
|
||||||
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 TimeSpan _elapsedPlayTime;
|
private TimeSpan _elapsedPlayTime;
|
||||||
|
private bool _hasRun;
|
||||||
|
private bool _isRunning;
|
||||||
private float _simRatio;
|
private float _simRatio;
|
||||||
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
private ServerState _state;
|
||||||
private Timer _watchdog;
|
|
||||||
private Stopwatch _uptime;
|
private Stopwatch _uptime;
|
||||||
|
private Timer _watchdog;
|
||||||
|
|
||||||
public TorchServer(TorchConfig config = null)
|
/// <inheritdoc />
|
||||||
|
public TorchServer(TorchConfig config = null)
|
||||||
{
|
{
|
||||||
DedicatedInstance = new InstanceManager(this);
|
DedicatedInstance = new InstanceManager(this);
|
||||||
AddManager(DedicatedInstance);
|
AddManager(DedicatedInstance);
|
||||||
|
AddManager(new EntityControlManager(this));
|
||||||
Config = config ?? new TorchConfig();
|
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 />
|
/// <inheritdoc />
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
Log.Info("Initializing server");
|
||||||
|
MySandboxGame.IsDedicated = true;
|
||||||
base.Init();
|
base.Init();
|
||||||
|
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
|
||||||
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();
|
|
||||||
|
|
||||||
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
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 />
|
/// <inheritdoc />
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
if (State != ServerState.Stopped)
|
if (State != ServerState.Stopped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DedicatedInstance.SaveConfig();
|
if (_hasRun)
|
||||||
_uptime = Stopwatch.StartNew();
|
{
|
||||||
IsRunning = true;
|
Restart();
|
||||||
GameThread = Thread.CurrentThread;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
State = ServerState.Starting;
|
State = ServerState.Starting;
|
||||||
|
IsRunning = true;
|
||||||
|
CanRun = false;
|
||||||
|
_hasRun = true;
|
||||||
Log.Info("Starting server.");
|
Log.Info("Starting server.");
|
||||||
|
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
|
||||||
|
if (MySandboxGame.ConfigDedicated.RemoteApiEnabled && !string.IsNullOrEmpty(MySandboxGame.ConfigDedicated.RemoteSecurityKey))
|
||||||
|
{
|
||||||
|
var myRemoteServer = new MyRemoteServer(MySandboxGame.ConfigDedicated.RemoteApiPort, MySandboxGame.ConfigDedicated.RemoteSecurityKey);
|
||||||
|
Log.Info($"Remote API started on port {myRemoteServer.Port}");
|
||||||
|
}
|
||||||
|
|
||||||
MySandboxGame.IsDedicated = true;
|
_uptime = Stopwatch.StartNew();
|
||||||
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
|
|
||||||
|
|
||||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
|
||||||
|
|
||||||
base.Start();
|
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 />
|
/// <inheritdoc />
|
||||||
@@ -205,71 +138,161 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
if (State == ServerState.Stopped)
|
if (State == ServerState.Stopped)
|
||||||
Log.Error("Server is already 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.");
|
Log.Info("Stopping server.");
|
||||||
|
base.Stop();
|
||||||
//Unload all the static junk.
|
|
||||||
//TODO: Finish unloading all server data so it's in a completely clean state.
|
|
||||||
MySandboxGame.Static.Exit();
|
|
||||||
|
|
||||||
Log.Info("Server stopped.");
|
Log.Info("Server stopped.");
|
||||||
_stopHandle.Set();
|
|
||||||
State = ServerState.Stopped;
|
State = ServerState.Stopped;
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
|
CanRun = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restart the program. DOES NOT SAVE!
|
/// Restart the program.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Restart()
|
public override void Restart()
|
||||||
{
|
{
|
||||||
var exe = Assembly.GetExecutingAssembly().Location;
|
if (IsRunning)
|
||||||
((TorchConfig)Config).WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
Save().ContinueWith(DoRestart, this, TaskContinuationOptions.RunContinuationsAsynchronously);
|
||||||
Process.Start(exe, Config.ToString());
|
else
|
||||||
Environment.Exit(0);
|
DoRestart(null, this);
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
void DoRestart(Task<GameSaveResult> task, object torch0)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
case SaveGameStatus.Success:
|
var torch = (TorchServer)torch0;
|
||||||
Log.Info("Save completed.");
|
torch.Stop();
|
||||||
Multiplayer.SendMessage("Saved game.", playerId: callerId);
|
// TODO clone this
|
||||||
break;
|
var config = (TorchConfig)torch.Config;
|
||||||
case SaveGameStatus.SaveInProgress:
|
LogManager.Flush();
|
||||||
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);
|
string exe = Assembly.GetExecutingAssembly().Location;
|
||||||
break;
|
Debug.Assert(exe != null);
|
||||||
case SaveGameStatus.GameNotReady:
|
config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||||
Log.Error("Save failed, game was not ready.");
|
config.Autostart = true;
|
||||||
Multiplayer.SendMessage("Save failed, game was not ready.", playerId: callerId, font: MyFontEnum.Red);
|
Process.Start(exe, config.ToString());
|
||||||
break;
|
|
||||||
case SaveGameStatus.TimedOut:
|
Process.GetCurrentProcess().Kill();
|
||||||
Log.Error("Save failed, save timed out.");
|
|
||||||
Multiplayer.SendMessage("Save failed, save timed out.", playerId: callerId, font: MyFontEnum.Red);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 System.Threading.Tasks;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Sandbox.Engine.Utils;
|
using Sandbox.Engine.Utils;
|
||||||
|
using Torch.Collections;
|
||||||
|
using Torch.Server.Managers;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@ namespace Torch.Server.ViewModels
|
|||||||
{
|
{
|
||||||
public class ConfigDedicatedViewModel : ViewModel
|
public class ConfigDedicatedViewModel : ViewModel
|
||||||
{
|
{
|
||||||
private static readonly Logger Log = LogManager.GetLogger("Config");
|
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
||||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Model => _config;
|
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Model => _config;
|
||||||
|
|
||||||
@@ -25,111 +27,110 @@ namespace Torch.Server.ViewModels
|
|||||||
public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated)
|
public ConfigDedicatedViewModel(MyConfigDedicated<MyObjectBuilder_SessionSettings> configDedicated)
|
||||||
{
|
{
|
||||||
_config = configDedicated;
|
_config = configDedicated;
|
||||||
|
_config.IgnoreLastSession = true;
|
||||||
SessionSettings = new SessionSettingsViewModel(_config.SessionSettings);
|
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)
|
public void Save(string path = null)
|
||||||
{
|
{
|
||||||
var newline = new [] {Environment.NewLine};
|
Validate();
|
||||||
|
|
||||||
_config.Administrators.Clear();
|
_config.SessionSettings = _sessionSettings;
|
||||||
foreach (var admin in Administrators.Split(newline, StringSplitOptions.RemoveEmptyEntries))
|
// Never ever
|
||||||
_config.Administrators.Add(admin);
|
_config.IgnoreLastSession = true;
|
||||||
|
_config.Save(path);
|
||||||
|
}
|
||||||
|
|
||||||
_config.Banned.Clear();
|
public bool Validate()
|
||||||
foreach (var banned in Banned.Split(newline, StringSplitOptions.RemoveEmptyEntries))
|
{
|
||||||
_config.Banned.Add(ulong.Parse(banned));
|
if (SelectedWorld == null)
|
||||||
|
|
||||||
_config.Mods.Clear();
|
|
||||||
foreach (var mod in Mods.Split(newline, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
{
|
||||||
if (ulong.TryParse(mod, out ulong modId))
|
Log.Warn($"{nameof(SelectedWorld)} == null");
|
||||||
_config.Mods.Add(modId);
|
return false;
|
||||||
else
|
|
||||||
Log.Warn($"'{mod}' is not a valid mod ID.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_config.Save(path);
|
if (LoadWorld == null)
|
||||||
|
{
|
||||||
|
Log.Warn($"{nameof(LoadWorld)} == null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SessionSettingsViewModel _sessionSettings;
|
private SessionSettingsViewModel _sessionSettings;
|
||||||
public SessionSettingsViewModel SessionSettings { get => _sessionSettings; set { _sessionSettings = value; OnPropertyChanged(); } }
|
public SessionSettingsViewModel SessionSettings { get => _sessionSettings; set { _sessionSettings = value; OnPropertyChanged(); } }
|
||||||
|
|
||||||
public ObservableList<string> WorldPaths { get; } = new ObservableList<string>();
|
public MtObservableList<WorldViewModel> Worlds { get; } = new MtObservableList<WorldViewModel>();
|
||||||
private string _administrators;
|
private WorldViewModel _selectedWorld;
|
||||||
public string Administrators { get => _administrators; set { _administrators = value; OnPropertyChanged(); } }
|
public WorldViewModel SelectedWorld
|
||||||
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
|
|
||||||
{
|
{
|
||||||
get { return _config.AsteroidAmount; }
|
get => _selectedWorld;
|
||||||
set { _config.AsteroidAmount = value; OnPropertyChanged(); }
|
set
|
||||||
|
{
|
||||||
|
SetValue(ref _selectedWorld, value);
|
||||||
|
LoadWorld = _selectedWorld?.WorldPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GroupId
|
public List<string> Administrators { get => _config.Administrators; set => SetValue(x => _config.Administrators = x, value); }
|
||||||
{
|
|
||||||
get { return _config.GroupID; }
|
|
||||||
set { _config.GroupID = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IgnoreLastSession
|
public List<ulong> Banned { get => _config.Banned; set => SetValue(x => _config.Banned = x, value); }
|
||||||
{
|
|
||||||
get { return _config.IgnoreLastSession; }
|
|
||||||
set { _config.IgnoreLastSession = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string IP
|
public List<ulong> Reserved { get => _config.Reserved; set => SetValue(x => _config.Reserved = x, value); }
|
||||||
{
|
|
||||||
get { return _config.IP; }
|
|
||||||
set { _config.IP = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Port
|
private List<ulong> _mods = new List<ulong>();
|
||||||
{
|
public List<ulong> Mods { get => _mods; set => SetValue(x => _mods = x, value); }
|
||||||
get { return _config.ServerPort; }
|
|
||||||
set { _config.ServerPort = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ServerName
|
public int AsteroidAmount { get => _config.AsteroidAmount; set => SetValue(x => _config.AsteroidAmount = x, value); }
|
||||||
{
|
|
||||||
get { return _config.ServerName; }
|
|
||||||
set { _config.ServerName = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool PauseGameWhenEmpty
|
public ulong GroupId { get => _config.GroupID; set => SetValue(x => _config.GroupID = x, value); }
|
||||||
{
|
|
||||||
get { return _config.PauseGameWhenEmpty; }
|
|
||||||
set { _config.PauseGameWhenEmpty = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string PremadeCheckpointPath
|
public string IP { get => _config.IP; set => SetValue(x => _config.IP = x, value); }
|
||||||
{
|
|
||||||
get { return _config.PremadeCheckpointPath; }
|
|
||||||
set { _config.PremadeCheckpointPath = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LoadWorld
|
public int Port { get => _config.ServerPort; set => SetValue(x => _config.ServerPort = x, value); }
|
||||||
{
|
|
||||||
get { return _config.LoadWorld; }
|
|
||||||
set { _config.LoadWorld = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int SteamPort
|
public string ServerName { get => _config.ServerName; set => SetValue(x => _config.ServerName = x, value); }
|
||||||
{
|
|
||||||
get { return _config.SteamPort; }
|
|
||||||
set { _config.SteamPort = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
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; }
|
get
|
||||||
set { _config.WorldName = value; OnPropertyChanged(); }
|
{
|
||||||
|
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.Game.Entities.Cube;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using Sandbox.ModAPI.Interfaces;
|
using Sandbox.ModAPI.Interfaces;
|
||||||
|
using Torch.Collections;
|
||||||
using Torch.Server.ViewModels.Entities;
|
using Torch.Server.ViewModels.Entities;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels.Blocks
|
namespace Torch.Server.ViewModels.Blocks
|
||||||
{
|
{
|
||||||
public class BlockViewModel : EntityViewModel
|
public class BlockViewModel : EntityViewModel
|
||||||
{
|
{
|
||||||
public IMyTerminalBlock Block { get; }
|
public IMyTerminalBlock Block => (IMyTerminalBlock) Entity;
|
||||||
public ObservableList<PropertyViewModel> Properties { get; } = new ObservableList<PropertyViewModel>();
|
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
|
public override string Name
|
||||||
{
|
{
|
||||||
@@ -37,7 +38,7 @@ namespace Torch.Server.ViewModels.Blocks
|
|||||||
|
|
||||||
public long BuiltBy
|
public long BuiltBy
|
||||||
{
|
{
|
||||||
get => ((MySlimBlock)Block.SlimBlock).BuiltBy;
|
get => ((MySlimBlock)Block?.SlimBlock)?.BuiltBy ?? 0;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TorchBase.Instance.Invoke(() =>
|
TorchBase.Instance.Invoke(() =>
|
||||||
@@ -58,7 +59,6 @@ namespace Torch.Server.ViewModels.Blocks
|
|||||||
|
|
||||||
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
|
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
|
||||||
{
|
{
|
||||||
Block = block;
|
|
||||||
if (Block == null)
|
if (Block == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@@ -6,7 +6,12 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
{
|
{
|
||||||
public CharacterViewModel(MyCharacter character, EntityTreeViewModel tree) : base(character, tree)
|
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 VRage.ModAPI;
|
||||||
using VRageMath;
|
using VRageMath;
|
||||||
|
|
||||||
@@ -7,28 +13,46 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
public class EntityViewModel : ViewModel
|
public class EntityViewModel : ViewModel
|
||||||
{
|
{
|
||||||
protected EntityTreeViewModel Tree { get; }
|
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 long Id => Entity.EntityId;
|
||||||
|
|
||||||
|
public MtObservableList<EntityControlViewModel> EntityControls { get; private set; }
|
||||||
|
|
||||||
public virtual string Name
|
public virtual string Name
|
||||||
{
|
{
|
||||||
get => Entity.DisplayName;
|
get => Entity?.DisplayName ?? (Entity != null ? $"eid:{Entity.EntityId}" : "nil");
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
|
if (Entity!=null)
|
||||||
|
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string Position
|
public virtual string Position
|
||||||
{
|
{
|
||||||
get => Entity.GetPosition().ToString();
|
get => Entity?.GetPosition().ToString();
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (!Vector3D.TryParse(value, out Vector3D v))
|
if (!Vector3D.TryParse(value, out Vector3D v))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
|
if (Entity != null)
|
||||||
|
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,66 +1,117 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Sandbox.Definitions;
|
||||||
using Sandbox.Game.Entities;
|
using Sandbox.Game.Entities;
|
||||||
|
using Sandbox.Game.Entities.Cube;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Collections;
|
||||||
using Torch.Server.ViewModels.Blocks;
|
using Torch.Server.ViewModels.Blocks;
|
||||||
|
using VRage.Game;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels.Entities
|
namespace Torch.Server.ViewModels.Entities
|
||||||
{
|
{
|
||||||
public class GridViewModel : EntityViewModel, ILazyLoad
|
public class GridViewModel : EntityViewModel, ILazyLoad
|
||||||
{
|
{
|
||||||
private MyCubeGrid Grid => (MyCubeGrid)Entity;
|
private static readonly MyCubeBlockDefinition _fillerDefinition = new MyCubeBlockDefinition()
|
||||||
public ObservableList<BlockViewModel> Blocks { get; } = new ObservableList<BlockViewModel>();
|
{
|
||||||
|
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 />
|
/// <inheritdoc />
|
||||||
public string DescriptiveName { get; }
|
public string DescriptiveName { get; }
|
||||||
|
|
||||||
public GridViewModel() { }
|
public GridViewModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
|
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
|
||||||
{
|
{
|
||||||
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
|
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)
|
private void Grid_OnBlockRemoved(Sandbox.Game.Entities.Cube.MySlimBlock obj)
|
||||||
{
|
{
|
||||||
if (obj.FatBlock != null)
|
if (obj.FatBlock != null)
|
||||||
Blocks.RemoveWhere(b => b.Block.EntityId == obj.FatBlock?.EntityId);
|
RemoveBlock(obj.FatBlock);
|
||||||
|
|
||||||
OnPropertyChanged(nameof(Name));
|
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)
|
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)
|
if (block != null)
|
||||||
Blocks.Insert(new BlockViewModel(block, Tree), b => b.Name);
|
AddBlock(block);
|
||||||
|
|
||||||
OnPropertyChanged(nameof(Name));
|
OnPropertyChanged(nameof(Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _load;
|
private bool _load;
|
||||||
|
|
||||||
public void Load()
|
public void Load()
|
||||||
{
|
{
|
||||||
if (_load)
|
if (_load)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_load = true;
|
_load = true;
|
||||||
Blocks.Clear();
|
|
||||||
TorchBase.Instance.Invoke(() =>
|
TorchBase.Instance.Invoke(() =>
|
||||||
{
|
{
|
||||||
foreach (var block in Grid.GetFatBlocks().Where(b => b is IMyTerminalBlock))
|
Blocks.Clear();
|
||||||
{
|
foreach (var block in Grid.GetFatBlocks().OfType<MyTerminalBlock>())
|
||||||
Blocks.Add(new BlockViewModel((IMyTerminalBlock)block, Tree));
|
AddBlock(block);
|
||||||
}
|
|
||||||
|
|
||||||
Grid.OnBlockAdded += Grid_OnBlockAdded;
|
Grid.OnBlockAdded += Grid_OnBlockAdded;
|
||||||
Grid.OnBlockRemoved += Grid_OnBlockRemoved;
|
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.Entity;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Torch.Collections;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels.Entities
|
namespace Torch.Server.ViewModels.Entities
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,7 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
|
|
||||||
public override bool CanStop => false;
|
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()
|
public async Task UpdateAttachedGrids()
|
||||||
{
|
{
|
||||||
@@ -28,11 +29,10 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
await TorchBase.Instance.InvokeAsync(() => MyEntities.GetTopMostEntitiesInBox(ref box, entities)).ConfigureAwait(false);
|
await TorchBase.Instance.InvokeAsync(() => MyEntities.GetTopMostEntitiesInBox(ref box, entities)).ConfigureAwait(false);
|
||||||
foreach (var entity in entities.Where(e => e is IMyCubeGrid))
|
foreach (var entity in entities.Where(e => e is IMyCubeGrid))
|
||||||
{
|
{
|
||||||
var gridModel = Tree.Grids.FirstOrDefault(g => g.Entity.EntityId == entity.EntityId);
|
if (Tree.Grids.TryGetValue(entity.EntityId, out var gridModel))
|
||||||
if (gridModel == null)
|
|
||||||
{
|
{
|
||||||
gridModel = new GridViewModel((MyCubeGrid)entity, Tree);
|
gridModel = new GridViewModel((MyCubeGrid)entity, Tree);
|
||||||
Tree.Grids.Add(gridModel);
|
Tree.Grids.Add(entity.EntityId, gridModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachedGrids.Add(gridModel);
|
AttachedGrids.Add(gridModel);
|
||||||
|
@@ -11,16 +11,19 @@ using VRage.Game.ModAPI;
|
|||||||
using VRage.ModAPI;
|
using VRage.ModAPI;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using Torch.Collections;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
public class EntityTreeViewModel : ViewModel
|
public class EntityTreeViewModel : ViewModel
|
||||||
{
|
{
|
||||||
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
//TODO: these should be sorted sets for speed
|
//TODO: these should be sorted sets for speed
|
||||||
public ObservableList<GridViewModel> Grids { get; set; } = new ObservableList<GridViewModel>();
|
public MtObservableSortedDictionary<long, GridViewModel> Grids { get; set; } = new MtObservableSortedDictionary<long, GridViewModel>();
|
||||||
public ObservableList<CharacterViewModel> Characters { get; set; } = new ObservableList<CharacterViewModel>();
|
public MtObservableSortedDictionary<long, CharacterViewModel> Characters { get; set; } = new MtObservableSortedDictionary<long, CharacterViewModel>();
|
||||||
public ObservableList<EntityViewModel> FloatingObjects { get; set; } = new ObservableList<EntityViewModel>();
|
public MtObservableSortedDictionary<long, EntityViewModel> FloatingObjects { get; set; } = new MtObservableSortedDictionary<long, EntityViewModel>();
|
||||||
public ObservableList<VoxelMapViewModel> VoxelMaps { get; set; } = new ObservableList<VoxelMapViewModel>();
|
public MtObservableSortedDictionary<long, VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableSortedDictionary<long, VoxelMapViewModel>();
|
||||||
public Dispatcher ControlDispatcher => _control.Dispatcher;
|
public Dispatcher ControlDispatcher => _control.Dispatcher;
|
||||||
|
|
||||||
private EntityViewModel _currentEntity;
|
private EntityViewModel _currentEntity;
|
||||||
@@ -29,7 +32,12 @@ namespace Torch.Server.ViewModels
|
|||||||
public EntityViewModel CurrentEntity
|
public EntityViewModel CurrentEntity
|
||||||
{
|
{
|
||||||
get => _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)
|
public EntityTreeViewModel(UserControl control)
|
||||||
@@ -45,39 +53,55 @@ namespace Torch.Server.ViewModels
|
|||||||
|
|
||||||
private void MyEntities_OnEntityRemove(VRage.Game.Entity.MyEntity obj)
|
private void MyEntities_OnEntityRemove(VRage.Game.Entity.MyEntity obj)
|
||||||
{
|
{
|
||||||
switch (obj)
|
try
|
||||||
{
|
{
|
||||||
case MyCubeGrid grid:
|
switch (obj)
|
||||||
Grids.RemoveWhere(m => m.Id == grid.EntityId);
|
{
|
||||||
break;
|
case MyCubeGrid grid:
|
||||||
case MyCharacter character:
|
Grids.Remove(grid.EntityId);
|
||||||
Characters.RemoveWhere(m => m.Id == character.EntityId);
|
break;
|
||||||
break;
|
case MyCharacter character:
|
||||||
case MyFloatingObject floating:
|
Characters.Remove(character.EntityId);
|
||||||
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
|
break;
|
||||||
break;
|
case MyFloatingObject floating:
|
||||||
case MyVoxelBase voxel:
|
FloatingObjects.Remove(floating.EntityId);
|
||||||
VoxelMaps.RemoveWhere(m => m.Id == voxel.EntityId);
|
break;
|
||||||
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)
|
private void MyEntities_OnEntityAdd(VRage.Game.Entity.MyEntity obj)
|
||||||
{
|
{
|
||||||
switch (obj)
|
try
|
||||||
{
|
{
|
||||||
case MyCubeGrid grid:
|
switch (obj)
|
||||||
Grids.Insert(new GridViewModel(grid, this), g => g.Name);
|
{
|
||||||
break;
|
case MyCubeGrid grid:
|
||||||
case MyCharacter character:
|
Grids.Add(obj.EntityId, new GridViewModel(grid, this));
|
||||||
Characters.Insert(new CharacterViewModel(character, this), c => c.Name);
|
break;
|
||||||
break;
|
case MyCharacter character:
|
||||||
case MyFloatingObject floating:
|
Characters.Add(obj.EntityId, new CharacterViewModel(character, this));
|
||||||
FloatingObjects.Insert(new FloatingObjectViewModel(floating, this), f => f.Name);
|
break;
|
||||||
break;
|
case MyFloatingObject floating:
|
||||||
case MyVoxelBase voxel:
|
FloatingObjects.Add(obj.EntityId, new FloatingObjectViewModel(floating, this));
|
||||||
VoxelMaps.Insert(new VoxelMapViewModel(voxel, this), v => v.Name);
|
break;
|
||||||
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;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
using Torch.API.Plugins;
|
using Torch.API.Plugins;
|
||||||
|
using Torch.Collections;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
public class PluginManagerViewModel : ViewModel
|
public class PluginManagerViewModel : ViewModel
|
||||||
{
|
{
|
||||||
public ObservableList<PluginViewModel> Plugins { get; } = new ObservableList<PluginViewModel>();
|
public MtObservableList<PluginViewModel> Plugins { get; } = new MtObservableList<PluginViewModel>();
|
||||||
|
|
||||||
private PluginViewModel _selectedPlugin;
|
private PluginViewModel _selectedPlugin;
|
||||||
public PluginViewModel SelectedPlugin
|
public PluginViewModel SelectedPlugin
|
||||||
{
|
{
|
||||||
get => _selectedPlugin;
|
get => _selectedPlugin;
|
||||||
set { _selectedPlugin = value; OnPropertyChanged(); }
|
set { _selectedPlugin = value; OnPropertyChanged(nameof(SelectedPlugin)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginManagerViewModel() { }
|
public PluginManagerViewModel() { }
|
||||||
@@ -29,7 +30,7 @@ namespace Torch.Server.ViewModels
|
|||||||
pluginManager.PluginsLoaded += PluginManager_PluginsLoaded;
|
pluginManager.PluginsLoaded += PluginManager_PluginsLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PluginManager_PluginsLoaded(IList<ITorchPlugin> obj)
|
private void PluginManager_PluginsLoaded(IReadOnlyCollection<ITorchPlugin> obj)
|
||||||
{
|
{
|
||||||
Plugins.Clear();
|
Plugins.Clear();
|
||||||
foreach (var plugin in obj)
|
foreach (var plugin in obj)
|
||||||
|
@@ -3,32 +3,40 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Plugins;
|
using Torch.API.Plugins;
|
||||||
|
using Torch.Server.Views;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
public class PluginViewModel
|
public class PluginViewModel
|
||||||
{
|
{
|
||||||
public UserControl Control
|
public UserControl Control { get; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Plugin is IWpfPlugin p)
|
|
||||||
return p.GetControl();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public ITorchPlugin Plugin { get; }
|
public ITorchPlugin Plugin { get; }
|
||||||
|
|
||||||
public PluginViewModel(ITorchPlugin plugin)
|
public PluginViewModel(ITorchPlugin plugin)
|
||||||
{
|
{
|
||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
|
|
||||||
|
if (Plugin is IWpfPlugin p)
|
||||||
|
Control = p.GetControl();
|
||||||
|
|
||||||
Name = $"{plugin.Name} ({plugin.Version})";
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using Torch;
|
||||||
using System.Threading.Tasks;
|
using Torch.Collections;
|
||||||
using SharpDX.Toolkit.Collections;
|
using Torch.Views;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Library.Utils;
|
using VRage.Library.Utils;
|
||||||
|
using VRage.Serialization;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// View model for <see cref="MyObjectBuilder_SessionSettings"/>
|
|
||||||
/// </summary>
|
|
||||||
public class SessionSettingsViewModel : ViewModel
|
public class SessionSettingsViewModel : ViewModel
|
||||||
{
|
{
|
||||||
private MyObjectBuilder_SessionSettings _settings;
|
private MyObjectBuilder_SessionSettings _settings;
|
||||||
|
|
||||||
/// <summary>
|
[Torch.Views.Display(Description = "The type of the game mode.", Name = "Game Mode", GroupName = "Others")]
|
||||||
/// Creates a new view model with a new <see cref="MyObjectBuilder_SessionSettings"/> object.
|
public MyGameModeEnum GameMode { get => _settings.GameMode; set => SetValue(ref _settings.GameMode, value); }
|
||||||
/// </summary>
|
[Torch.Views.Display(Description = "The type of the game online mode.", Name = "Online Mode", GroupName = "Others")]
|
||||||
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
|
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>
|
/// <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>
|
/// </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)
|
public SessionSettingsViewModel(MyObjectBuilder_SessionSettings settings)
|
||||||
{
|
{
|
||||||
_settings = 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)
|
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;
|
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.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SteamSDK;
|
using Steamworks;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,7 @@ namespace Torch.Server.ViewModels
|
|||||||
public SteamUserViewModel(ulong id)
|
public SteamUserViewModel(ulong id)
|
||||||
{
|
{
|
||||||
SteamId = id;
|
SteamId = id;
|
||||||
Name = SteamAPI.Instance.Friends.GetPersonaName(id);
|
Name = SteamFriends.GetFriendPersonaName(new CSteamID(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SteamUserViewModel() : this(0) { }
|
public SteamUserViewModel() : this(0) { }
|
||||||
|
@@ -3,12 +3,11 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Torch.Server"
|
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Add Workshop Item" Height="200" Width="400">
|
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"/>
|
<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"/>
|
<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>
|
</DockPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
@@ -3,27 +3,15 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Torch.Server"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<ListView Grid.Row="0" x:Name="ChatItems" ItemsSource="{Binding ChatHistory}" Margin="5,5,5,5">
|
<ScrollViewer x:Name="ChatScroller" Grid.Row="0" Margin="5,5,5,5" HorizontalScrollBarVisibility="Disabled">
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled"/>
|
<TextBlock x:Name="ChatItems" TextWrapping="Wrap" />
|
||||||
<ListView.ItemTemplate>
|
</ScrollViewer>
|
||||||
<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>
|
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition/>
|
||||||
|
@@ -2,7 +2,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@@ -14,13 +16,17 @@ using System.Windows.Media.Imaging;
|
|||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using NLog;
|
||||||
using Torch;
|
using Torch;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Sandbox.Engine.Multiplayer;
|
using Sandbox.Engine.Multiplayer;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using SteamSDK;
|
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
using Torch.Managers;
|
using Torch.Managers;
|
||||||
|
using Torch.Server.Managers;
|
||||||
|
using VRage.Game;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
@@ -29,35 +35,111 @@ namespace Torch.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ChatControl : UserControl
|
public partial class ChatControl : UserControl
|
||||||
{
|
{
|
||||||
private TorchBase _server;
|
private static Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
private MultiplayerManager _multiplayer;
|
private ITorchServer _server;
|
||||||
|
|
||||||
public ChatControl()
|
public ChatControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
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)
|
public void BindServer(ITorchServer server)
|
||||||
{
|
{
|
||||||
_server = (TorchBase)server;
|
_server = server;
|
||||||
_multiplayer = (MultiplayerManager)server.Multiplayer;
|
|
||||||
ChatItems.Items.Clear();
|
server.Initialized += Server_Initialized ;
|
||||||
DataContext = _multiplayer;
|
|
||||||
if (_multiplayer.ChatHistory is INotifyCollectionChanged ncc)
|
|
||||||
ncc.CollectionChanged += ChatHistory_CollectionChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChatHistory_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void Server_Initialized(ITorchServer obj)
|
||||||
{
|
{
|
||||||
ChatItems.ScrollToItem(ChatItems.Items.Count - 1);
|
Dispatcher.InvokeAsync(() =>
|
||||||
/*
|
|
||||||
if (VisualTreeHelper.GetChildrenCount(ChatItems) > 0)
|
|
||||||
{
|
{
|
||||||
|
ChatItems.Inlines.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
Border border = (Border)VisualTreeHelper.GetChild(ChatItems, 0);
|
var sessionManager = _server.Managers.GetManager<ITorchSessionManager>();
|
||||||
ScrollViewer scrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(border, 0);
|
if (sessionManager != null)
|
||||||
scrollViewer.ScrollToBottom();
|
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)
|
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -78,27 +160,20 @@ namespace Torch.Server
|
|||||||
if (string.IsNullOrEmpty(text))
|
if (string.IsNullOrEmpty(text))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var commands = _server.Commands;
|
var commands = _server.CurrentSession?.Managers.GetManager<Torch.Commands.CommandManager>();
|
||||||
if (commands.IsCommand(text))
|
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(() =>
|
_server.Invoke(() =>
|
||||||
{
|
{
|
||||||
var response = commands.HandleCommandFromServer(text);
|
commands.HandleCommandFromServer(text);
|
||||||
Dispatcher.BeginInvoke(() => OnMessageEntered_Callback(response));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_server.Multiplayer.SendMessage(text);
|
_server.CurrentSession?.Managers.GetManager<IChatManagerClient>().SendMessageAsSelf(text);
|
||||||
}
|
}
|
||||||
Message.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:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Torch.Server.Views"
|
|
||||||
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
|
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
|
||||||
mc:Ignorable="d"
|
xmlns:managers="clr-namespace:Torch.Server.Managers"
|
||||||
Background="White">
|
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>
|
<UserControl.DataContext>
|
||||||
<viewModels:ConfigDedicatedViewModel />
|
<viewModels:ConfigDedicatedViewModel />
|
||||||
</UserControl.DataContext>
|
</UserControl.DataContext>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition/>
|
<RowDefinition />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<DockPanel Grid.Row="0">
|
<DockPanel Grid.Row="0">
|
||||||
<Label Content="World:" DockPanel.Dock="Left" />
|
<Label Content="World:" DockPanel.Dock="Left" />
|
||||||
<Button Content="New World" Margin="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
|
<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}"/>
|
||||||
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" SelectionChanged="Selector_OnSelectionChanged"/>
|
<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>
|
</DockPanel>
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="7*" />
|
||||||
<ColumnDefinition/>
|
<ColumnDefinition Width="10*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid Grid.Column="0">
|
<Grid Grid.Column="0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition/>
|
<RowDefinition />
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<ScrollViewer Grid.Row="0" Margin="3">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Orientation="Horizontal">
|
<Grid>
|
||||||
<StackPanel Margin="3" DockPanel.Dock="Left">
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0" Margin="3" DockPanel.Dock="Left">
|
||||||
<Label Content="Server Name" />
|
<Label Content="Server Name" />
|
||||||
<TextBox Text="{Binding ServerName}" Margin="3,0,3,3" Width="160" />
|
<TextBox Text="{Binding ServerName}" Margin="3,0,3,3" Width="160" />
|
||||||
<Label Content="World Name" />
|
<Label Content="World Name" />
|
||||||
<TextBox Text="{Binding WorldName}" Margin="3,0,3,3" Width="160" />
|
<TextBox Text="{Binding WorldName}" Margin="3,0,3,3" Width="160" />
|
||||||
<Label Content="Whitelist Group ID" />
|
<Label Content="Server Description" />
|
||||||
<TextBox Text="{Binding GroupId}" Margin="3,0,3,3" Width="160" />
|
<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" />
|
<Label Content="Server IP" />
|
||||||
<StackPanel Orientation="Horizontal" Margin="3,0,3,3">
|
<StackPanel Orientation="Horizontal" Margin="3,0,3,3">
|
||||||
<TextBox Text="{Binding IP}" Width="100" Height="20" />
|
<TextBox Text="{Binding IP}" Width="100" Height="20" />
|
||||||
<Label Content=":" Width="12" />
|
<Label Content=":" Width="12" />
|
||||||
<TextBox Text="{Binding Port}" Width="48" Height="20" />
|
<TextBox Text="{Binding Port}" Width="48" Height="20" />
|
||||||
</StackPanel>
|
</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 PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
||||||
|
<CheckBox IsChecked="{Binding AutodetectDependencies}" Content="Auto Detect Dependencies" Margin="3" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="3">
|
<StackPanel Grid.Column="1" Margin="3">
|
||||||
<Label Content="Mods" />
|
<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" />
|
<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" />
|
<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>
|
||||||
</StackPanel>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
|
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<ScrollViewer Grid.Column="1" Margin="3">
|
<views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" IgnoreDisplay ="True" />
|
||||||
<StackPanel DataContext="{Binding SessionSettings}">
|
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True"
|
||||||
<Expander Header="Block Limits">
|
Width="2" />
|
||||||
<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>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</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.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Threading;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
|
using Torch.Server.Annotations;
|
||||||
using Torch.Server.Managers;
|
using Torch.Server.Managers;
|
||||||
using Torch.Server.ViewModels;
|
using Torch.Server.ViewModels;
|
||||||
|
|
||||||
@@ -9,15 +18,74 @@ namespace Torch.Server.Views
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for ConfigControl.xaml
|
/// Interaction logic for ConfigControl.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ConfigControl : UserControl
|
public partial class ConfigControl : UserControl, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private InstanceManager _instanceManager;
|
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()
|
public ConfigControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
|
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
|
||||||
|
_instanceManager.InstanceLoaded += _instanceManager_InstanceLoaded;
|
||||||
DataContext = _instanceManager.DedicatedConfig;
|
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)
|
private void Save_OnClick(object sender, RoutedEventArgs e)
|
||||||
@@ -25,20 +93,9 @@ namespace Torch.Server.Views
|
|||||||
_instanceManager.SaveConfig();
|
_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.ImportSelectedWorldConfig();
|
||||||
_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 :)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
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.
|
//The control doesn't update the binding before firing the event.
|
||||||
if (e.AddedItems.Count > 0)
|
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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
|
|
||||||
namespace Torch.Server.Views.Converters
|
namespace Torch.Server.Views.Converters
|
||||||
@@ -15,9 +11,6 @@ namespace Torch.Server.Views.Converters
|
|||||||
public object Convert(object value, Type targetType, object parameter,
|
public object Convert(object value, Type targetType, object parameter,
|
||||||
System.Globalization.CultureInfo culture)
|
System.Globalization.CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (targetType != typeof(bool))
|
|
||||||
throw new InvalidOperationException("The target must be a boolean");
|
|
||||||
|
|
||||||
return !(bool)value;
|
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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Torch.Server.Views.Blocks"
|
xmlns:local="clr-namespace:Torch.Server.Views.Blocks"
|
||||||
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.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">
|
mc:Ignorable="d">
|
||||||
<UserControl.DataContext>
|
<UserControl.DataContext>
|
||||||
<blocks:BlockViewModel />
|
<blocks:BlockViewModel />
|
||||||
@@ -13,6 +15,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<StackPanel Grid.Row="0">
|
<StackPanel Grid.Row="0">
|
||||||
<Label Content="{Binding FullName}" FontSize="16" />
|
<Label Content="{Binding FullName}" FontSize="16" />
|
||||||
@@ -22,22 +25,27 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Label Content="Properties"/>
|
<Label Content="Properties"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ListView Grid.Row="1" ItemsSource="{Binding Properties}" Margin="3" IsEnabled="True">
|
<Expander Grid.Row="1" Header="Block Properties" IsExpanded="true">
|
||||||
<ListView.ItemTemplate>
|
<ListView ItemsSource="{Binding Properties}" Margin="3" IsEnabled="True">
|
||||||
<DataTemplate>
|
<ListView.ItemTemplate>
|
||||||
<local:PropertyView />
|
<DataTemplate>
|
||||||
</DataTemplate>
|
<local:PropertyView />
|
||||||
</ListView.ItemTemplate>
|
</DataTemplate>
|
||||||
<ListView.ItemContainerStyle>
|
</ListView.ItemTemplate>
|
||||||
<Style TargetType="ListViewItem">
|
<ListView.ItemContainerStyle>
|
||||||
<Setter Property="HorizontalContentAlignment"
|
<Style TargetType="ListViewItem">
|
||||||
|
<Setter Property="HorizontalContentAlignment"
|
||||||
Value="Stretch" />
|
Value="Stretch" />
|
||||||
<Setter Property="VerticalContentAlignment"
|
<Setter Property="VerticalContentAlignment"
|
||||||
Value="Center" />
|
Value="Center" />
|
||||||
<Setter Property="Focusable"
|
<Setter Property="Focusable"
|
||||||
Value="false" />
|
Value="false" />
|
||||||
</Style>
|
</Style>
|
||||||
</ListView.ItemContainerStyle>
|
</ListView.ItemContainerStyle>
|
||||||
</ListView>
|
</ListView>
|
||||||
|
</Expander>
|
||||||
|
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
|
||||||
|
<entities:EntityControlsView DataContext="{Binding}"/>
|
||||||
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -28,6 +28,15 @@ namespace Torch.Server.Views.Blocks
|
|||||||
public BlockView()
|
public BlockView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
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:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
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"
|
xmlns:converters="clr-namespace:Torch.Server.Views.Converters"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
|
@@ -26,6 +26,15 @@ namespace Torch.Server.Views.Blocks
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContextChanged += OnDataContextChanged;
|
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)
|
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