Compare commits
211 Commits
1.0.182.32
...
v1.3.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 | ||
![]() |
3d6806b63a | ||
![]() |
22e3019610 | ||
![]() |
7937cbd122 | ||
![]() |
2bef34ee5b | ||
![]() |
efe7236d31 | ||
![]() |
67dba9c820 | ||
![]() |
52c509aba0 | ||
![]() |
437c7d293e | ||
![]() |
2b6ce4f25b | ||
![]() |
64f123abe9 | ||
![]() |
599a98bceb | ||
![]() |
2cd1b8bd4e | ||
![]() |
c0be9c25da | ||
![]() |
5cea66374f | ||
![]() |
ee1c270c68 | ||
![]() |
4ab08e2faf | ||
![]() |
dd094edb88 | ||
![]() |
56e45236d8 | ||
![]() |
be9a8c5839 | ||
![]() |
8c11baf3b9 | ||
![]() |
6ce679bd83 | ||
![]() |
a4b1b9bb96 | ||
![]() |
91ad78e6a2 | ||
![]() |
4a68d66ab0 | ||
![]() |
4cb50b556f | ||
![]() |
b5f73a99cc | ||
![]() |
e9476a59e8 | ||
![]() |
f48f23c2eb | ||
![]() |
ddf465d8c9 | ||
![]() |
7149287b8e | ||
![]() |
cc709c6bb3 | ||
![]() |
8d101c4c11 | ||
![]() |
64eef6cd8e | ||
![]() |
f8ae3c0dd1 | ||
![]() |
589205edc3 | ||
![]() |
48b212faaf | ||
![]() |
0554dbc608 | ||
![]() |
3564eb805c | ||
![]() |
62a8064edd | ||
![]() |
55ed45190b | ||
![]() |
a6ae96093f | ||
![]() |
5b1afe6d50 | ||
![]() |
60df71a74c | ||
![]() |
a6d5da861f | ||
![]() |
afc10911f7 | ||
![]() |
0686e95c72 | ||
![]() |
234754fd49 | ||
![]() |
efb8d0f226 | ||
![]() |
3f881f7d67 | ||
![]() |
64d38abc99 | ||
![]() |
4a39362702 | ||
![]() |
db2d3794ae | ||
![]() |
526ff6fff0 | ||
![]() |
eebc0e428e | ||
![]() |
80aca514b2 | ||
![]() |
3e8068e82d | ||
![]() |
601fbcd176 | ||
![]() |
40eab15d69 | ||
![]() |
ceb272c0b4 | ||
![]() |
80d4f62694 | ||
![]() |
42f58a8649 | ||
![]() |
6b9af71967 | ||
![]() |
dbd98a09c5 | ||
![]() |
c6a6363163 | ||
![]() |
82815f66e5 | ||
![]() |
0a38eb770d | ||
![]() |
97da740e7e | ||
![]() |
42bb24ca6a | ||
![]() |
2f3b6cdda7 | ||
![]() |
525b496774 | ||
![]() |
562bb77dda | ||
![]() |
76a13dc53a | ||
![]() |
df0f8072a9 | ||
![]() |
87d9825c91 | ||
![]() |
3dba4f744f | ||
![]() |
1fcfe6fb5f | ||
![]() |
3ece4baba6 | ||
![]() |
f49dae2cbf | ||
![]() |
ddf15d756a | ||
![]() |
96d1faddbe | ||
![]() |
17ee96038c | ||
![]() |
e9b432288e | ||
![]() |
b814d1210b | ||
![]() |
c137fb4953 | ||
![]() |
4acce1c9c9 | ||
![]() |
8ab16c3d30 | ||
![]() |
7373dd37a6 | ||
![]() |
1251b945bc |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -252,3 +252,6 @@ ModelManifest.xml
|
|||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
GameBinaries
|
GameBinaries
|
||||||
|
|
||||||
|
# Generated Files
|
||||||
|
**Gen.cs
|
||||||
|
33
CHANGELOG.md
Normal file
33
CHANGELOG.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Torch 1.1.229.265
|
||||||
|
* Features
|
||||||
|
- Added more lenient version parsing for plugins (v#.# should work)
|
||||||
|
- Added countdown option to restart command (!restart [seconds])
|
||||||
|
* Fixes
|
||||||
|
- General fixes to work with the latest SE version
|
||||||
|
- Fixed config changes not saving
|
||||||
|
- (hopefully) Fixed issue causing crashes on servers using the Windows Classic theme
|
||||||
|
|
||||||
|
# Torch 1.1.207.7
|
||||||
|
* Notes
|
||||||
|
- This release makes significant changes to TorchConfig.xml. It has been renamed to Torch.cfg and has different options.
|
||||||
|
* Features
|
||||||
|
- Plugins, Torch, and the DS can now all update automatically
|
||||||
|
- Changed command prefix to !
|
||||||
|
- Added manual save command (thanks to Maldark)
|
||||||
|
- Added restart command
|
||||||
|
- Improved instance creation: now creates an entire skeleton instance with blank config
|
||||||
|
- Added instance name to console title
|
||||||
|
* Fixes
|
||||||
|
- Optimized UI so it's snappier and freezes less often
|
||||||
|
- Fixed NetworkManager.RaiseEvent overload that had an off-by-one bug
|
||||||
|
- Fixed chat window so it automatically scrolls down
|
||||||
|
|
||||||
|
# Torch 1.0.182.329
|
||||||
|
* Improved logging, logs now to go the Logs folder and aren't deleted on start
|
||||||
|
* Fixed chat tab not enabling with -autostart
|
||||||
|
* Fixed player list
|
||||||
|
* Watchdog time-out is now configurable in TorchConfig.xml
|
||||||
|
* Fixed infinario log spam
|
||||||
|
* Fixed crash when sending empty message from chat tab
|
||||||
|
* Fixed permissions on Torch commands
|
||||||
|
* Changed plugin StoragePath to the current instance path (per-instance configs)
|
26
CONTRIBUTING.md
Normal file
26
CONTRIBUTING.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Making a Pull Request
|
||||||
|
* Fork this repository and make sure your local **staging** branch is up to date with the main repository.
|
||||||
|
* Create a new branch from the **staging** branch for your addition with an appropriate name, e.g. **add-restart-command**
|
||||||
|
* PRs work by submitting the *entire* branch, so this allows you to continue work without locking up your whole repository.
|
||||||
|
* Commit your changes to that branch, making sure that you **follow the code guidelines below**.
|
||||||
|
* Submit your branch as a PR to be reviewed, with Torch's **staging** branch as the base.
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
* Types: **PascalCase**
|
||||||
|
* Prefix interfaces with "**I**"
|
||||||
|
* Suffix delegates with "**Del**"
|
||||||
|
* Methods: **PascalCase**
|
||||||
|
* Method names should generally use verbs in the infinitive tense, for example `GetValue()` or `OpenFile()`. Callbacks and events should use present continuous (-ing) or past tense depending on the context.
|
||||||
|
* Non-Private Members: **PascalCase**
|
||||||
|
* Private Members: **_camelCase**
|
||||||
|
|
||||||
|
## Code Design
|
||||||
|
* **One type per file** with the exception of nested types and delegate declarations.
|
||||||
|
* **No public fields** except for consts, use properties instead
|
||||||
|
* **No stateful static types.** These are a pain to clean up, static types should not store any information.
|
||||||
|
* Use **[dependency injection](https://stackoverflow.com/a/130862)** when possible. Most Torch code uses constructor injection.
|
||||||
|
* **Events and actions** should be null checked before calling or invoked with the `action?.Invoke()` syntax.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
* All types and members not marked **private** or **internal** should have XML documentation using the `/// <summary>` tag.
|
||||||
|
* Interface implementations and overridden methods should use the `/// <inheritdoc />` tag unless the summary needs to be changed from the base/interface summary.
|
22
Jenkins/jenkins-grab-se.ps1
Normal file
22
Jenkins/jenkins-grab-se.ps1
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
pushd
|
||||||
|
|
||||||
|
$steamData = "C:/Steam/Data/"
|
||||||
|
$steamCMDPath = "C:/Steam/steamcmd/"
|
||||||
|
$steamCMDZip = "C:/Steam/steamcmd.zip"
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
|
||||||
|
if (!(Test-Path $steamData)) {
|
||||||
|
mkdir "$steamData"
|
||||||
|
}
|
||||||
|
if (!(Test-Path $steamCMDPath)) {
|
||||||
|
if (!(Test-Path $steamCMDZip)) {
|
||||||
|
(New-Object System.Net.WebClient).DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", "$steamCMDZip");
|
||||||
|
}
|
||||||
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($steamCMDZip, $steamCMDPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
cd "$steamData"
|
||||||
|
& "$steamCMDPath/steamcmd.exe" "+login anonymous" "+force_install_dir $steamData" "+app_update 298740" "+quit"
|
||||||
|
|
||||||
|
popd
|
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)
|
||||||
|
}
|
74
Jenkinsfile
vendored
Normal file
74
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
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}"
|
||||||
|
}
|
||||||
|
if (buildMode == "Release") {
|
||||||
|
bat "del ${packageDir}*.pdb"
|
||||||
|
}
|
||||||
|
powershell "Add-Type -Assembly System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::CreateFromDirectory(\"\$PWD\\${packageDir}\", \"\$PWD\\${zipFile}\")"
|
||||||
|
archiveArtifacts artifacts: zipFile, caseSensitive: false, onlyIfSuccessful: true
|
||||||
|
}
|
||||||
|
|
||||||
|
node {
|
||||||
|
stage('Checkout') {
|
||||||
|
checkout scm
|
||||||
|
bat 'git pull --tags'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Acquire SE') {
|
||||||
|
bat 'powershell -File Jenkins/jenkins-grab-se.ps1'
|
||||||
|
bat 'IF EXIST GameBinaries RMDIR GameBinaries'
|
||||||
|
bat 'mklink /J GameBinaries "C:/Steam/Data/DedicatedServer64/"'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Acquire NuGet Packages') {
|
||||||
|
bat 'nuget restore Torch.sln'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
currentBuild.description = bat(returnStdout: true, script: '@powershell -File Versioning/version.ps1').trim()
|
||||||
|
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*")
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Test') {
|
||||||
|
bat 'IF NOT EXIST reports MKDIR reports'
|
||||||
|
bat "\"packages/xunit.runner.console.2.2.0/tools/xunit.console.exe\" \"bin-test/x64/${buildMode}/Torch.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Server.Tests.dll\" \"bin-test/x64/${buildMode}/Torch.Client.Tests.dll\" -parallel none -xml \"reports/Torch.Tests.xml\""
|
||||||
|
|
||||||
|
step([
|
||||||
|
$class: 'XUnitBuilder',
|
||||||
|
thresholdMode: 1,
|
||||||
|
thresholds: [[$class: 'FailedThreshold', failureThreshold: '1']],
|
||||||
|
tools: [[
|
||||||
|
$class: 'XUnitDotNetTestType',
|
||||||
|
deleteOutputFiles: true,
|
||||||
|
failIfNotNew: true,
|
||||||
|
pattern: 'reports/*.xml',
|
||||||
|
skipNoTestFiles: false,
|
||||||
|
stopProcessingIfError: true
|
||||||
|
]]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
14
NLog.config
14
NLog.config
@@ -1,15 +1,25 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<variable name="logStamp" value="${time} ${pad:padding=-8:inner=[${level:uppercase=true}]}" />
|
||||||
|
<variable name="logContent" value="${message:withException=true}"/>
|
||||||
|
|
||||||
<targets>
|
<targets>
|
||||||
<target 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}: ${var:logContent}" />
|
||||||
|
<target xsi:type="File" name="patch" layout="${var:logContent}" fileName="Logs\patch.log"/>
|
||||||
</targets>
|
</targets>
|
||||||
|
|
||||||
<rules>
|
<rules>
|
||||||
|
<logger name="Keen" minlevel="Info" writeTo="console"/>
|
||||||
|
<logger name="Keen" minlevel="Debug" writeTo="keen" final="true" />
|
||||||
|
<logger name="Keen" writeTo="null" final="true" />
|
||||||
|
|
||||||
<logger name="*" minlevel="Info" writeTo="main, console" />
|
<logger name="*" minlevel="Info" writeTo="main, console" />
|
||||||
<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>
|
27
README.md
27
README.md
@@ -1,27 +1,29 @@
|
|||||||
Discord: [](https://discord.gg/8uHZykr)
|
[](https://discord.gg/8uHZykr) [](http://server.torchapi.net:8080/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
|
||||||
|
|
||||||
|
* Get the latest Torch release here: https://github.com/TorchAPI/Torch/releases
|
||||||
|
* Unzip the Torch release into its own directory and run the executable. It will automatically download the SE DS and generate the other necessary files.
|
||||||
|
- If you already have a DS installed you can unzip the Torch files into the folder that contains the DedicatedServer64 folder.
|
||||||
|
|
||||||
|
## Torch.Client
|
||||||
|
* An optional client-side version of Torch. More documentation to come.
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
To build Torch you must first have a complete SE Dedicated installation somewhere. Before you open the solution, run the Setup batch file and enter the path of that installation's DedicatedServer64 folder. The script will make a symlink to that folder so the Torch solution can find the DLL references it needs.
|
To build Torch you must first have a complete SE Dedicated installation somewhere. Before you open the solution, run the Setup batch file and enter the path of that installation's DedicatedServer64 folder. The script will make a symlink to that folder so the Torch solution can find the DLL references it needs.
|
||||||
|
|
||||||
# Installation Guide
|
|
||||||
Note: Until Torch is in a stable, nearly feature complete state there will not be any binaries available. You'll have to compile the solution yourself.
|
|
||||||
|
|
||||||
### Automatic (recommended)
|
|
||||||
* Unzip Torch to its own folder, run Torch.Server.exe and enter 'y' in the prompt for automatic updates. Torch will automatically download the Space Engineers files and generate all of the configs/folders necessary.
|
|
||||||
|
|
||||||
### Manual (for hosting companies or the paranoid)
|
|
||||||
* Install the Space Engineers DS and then unzip the Torch files into the server's DedicatedServer64 directory. It will automatically detect the manual install and disable automatic updates.
|
|
||||||
|
|
||||||
In both cases you will need to set the InstancePath in TorchConfig.xml to an existing dedicated server instance as Torch can't fully generate it on its own yet.
|
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
|
# Official Plugins
|
||||||
@@ -29,4 +31,5 @@ Install plugins by unzipping them into the 'Plugins' folder which should be in t
|
|||||||
* [Essentials](https://github.com/TorchAPI/Essentials): Adds a slew of chat commands and other tools to help manage your server.
|
* [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.
|
* [Concealment](https://github.com/TorchAPI/Concealment): Adds game logic and physics optimizations that significantly improve sim speed.
|
||||||
|
|
||||||
If you have a more enjoyable server experience because of Torch, please consider supporting us on [Patreon](https://www.patreon.com/bePatron?u=847269)!
|
If you have a more enjoyable server experience because of Torch, please consider supporting us on Patreon.
|
||||||
|
[](https://www.patreon.com/bePatron?u=847269)!
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
@echo off
|
@echo off
|
||||||
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
|
set /p path="Please enter the folder location of your SpaceEngineersDedicated.exe: "
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
mklink /D GameBinaries %path%
|
mklink /J GameBinaries "%path%"
|
||||||
if errorlevel 1 goto Error
|
if errorlevel 1 goto Error
|
||||||
echo Done! You can now open the Torch solution without issue.
|
echo Done! You can now open the Torch solution without issue.
|
||||||
goto End
|
goto End
|
||||||
:Error
|
:Error
|
||||||
echo An error occured creating the symlink.
|
echo An error occured creating the symlink.
|
||||||
:End
|
:End
|
||||||
pause
|
pause
|
||||||
|
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,41 +1,147 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Torch.API.Managers;
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
|
|
||||||
namespace Torch.API
|
namespace Torch.API
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// API for Torch functions shared between client and server.
|
||||||
|
/// </summary>
|
||||||
public interface ITorchBase
|
public interface ITorchBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when the session begins loading.
|
||||||
|
/// </summary>
|
||||||
event Action SessionLoading;
|
event Action SessionLoading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when the session finishes loading.
|
||||||
|
/// </summary>
|
||||||
event Action SessionLoaded;
|
event Action SessionLoaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fires when the session begins unloading.
|
||||||
|
/// </summary>
|
||||||
event Action SessionUnloading;
|
event Action SessionUnloading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when the session finishes unloading.
|
||||||
|
/// </summary>
|
||||||
event Action SessionUnloaded;
|
event Action SessionUnloaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently running session instance, or null if none exists.
|
||||||
|
/// </summary>
|
||||||
|
ITorchSession CurrentSession { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for the current instance.
|
||||||
|
/// </summary>
|
||||||
ITorchConfig Config { get; }
|
ITorchConfig Config { get; }
|
||||||
IMultiplayerManager Multiplayer { get; }
|
|
||||||
|
/// <inheritdoc cref="IPluginManager"/>
|
||||||
|
[Obsolete]
|
||||||
IPluginManager Plugins { get; }
|
IPluginManager Plugins { get; }
|
||||||
Version TorchVersion { get; }
|
|
||||||
void Invoke(Action action);
|
/// <inheritdoc cref="IDependencyManager"/>
|
||||||
void InvokeBlocking(Action action);
|
IDependencyManager Managers { get; }
|
||||||
Task InvokeAsync(Action action);
|
|
||||||
string[] RunArgs { get; set; }
|
[Obsolete("Prefer using Managers.GetManager for global managers")]
|
||||||
bool IsOnGameThread();
|
|
||||||
void Start();
|
|
||||||
void Stop();
|
|
||||||
void Init();
|
|
||||||
T GetManager<T>() where T : class, IManager;
|
T GetManager<T>() where T : class, IManager;
|
||||||
|
|
||||||
|
[Obsolete("Prefer using Managers.AddManager for global managers")]
|
||||||
|
bool AddManager<T>(T manager) where T : class, IManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The binary version of the current instance.
|
||||||
|
/// </summary>
|
||||||
|
Version TorchVersion { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke an action on the game thread.
|
||||||
|
/// </summary>
|
||||||
|
void Invoke(Action action, [CallerMemberName] string caller = "");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke an action on the game thread and block until it has completed.
|
||||||
|
/// If this is called on the game thread the action will execute immediately.
|
||||||
|
/// </summary>
|
||||||
|
void InvokeBlocking(Action action, [CallerMemberName] string caller = "");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke an action on the game thread asynchronously.
|
||||||
|
/// </summary>
|
||||||
|
Task InvokeAsync(Action action, [CallerMemberName] string caller = "");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals the torch instance to start, then blocks until it's started.
|
||||||
|
/// </summary>
|
||||||
|
void Start();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals the torch instance to stop, then blocks until it's stopped.
|
||||||
|
/// </summary>
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restart the Torch instance.
|
||||||
|
/// </summary>
|
||||||
|
void Restart();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a save of the game.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callerId">Id of the player who initiated the save.</param>
|
||||||
|
Task Save(long callerId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the Torch instance. Before this <see cref="Start"/> is invalid.
|
||||||
|
/// </summary>
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes the Torch instance. After this <see cref="Start"/> is invalid.
|
||||||
|
/// </summary>
|
||||||
|
void Dispose();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current state of the game this instance of torch is controlling.
|
||||||
|
/// </summary>
|
||||||
|
TorchGameState GameState { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised when <see cref="GameState"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
event TorchGameStateChangedDel GameStateChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// API for the Torch server.
|
||||||
|
/// </summary>
|
||||||
public interface ITorchServer : ITorchBase
|
public interface ITorchServer : ITorchBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current <see cref="ServerState"/>
|
||||||
|
/// </summary>
|
||||||
|
ServerState State { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path of the dedicated instance folder.
|
||||||
|
/// </summary>
|
||||||
string InstancePath { get; }
|
string InstancePath { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// API for the Torch client.
|
||||||
|
/// </summary>
|
||||||
public interface ITorchClient : ITorchBase
|
public interface ITorchClient : ITorchBase
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,23 @@
|
|||||||
namespace Torch
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Torch
|
||||||
{
|
{
|
||||||
public interface ITorchConfig
|
public interface ITorchConfig
|
||||||
{
|
{
|
||||||
|
bool Autostart { get; set; }
|
||||||
|
bool ForceUpdate { get; set; }
|
||||||
|
bool GetPluginUpdates { get; set; }
|
||||||
|
bool GetTorchUpdates { get; set; }
|
||||||
string InstanceName { get; set; }
|
string InstanceName { get; set; }
|
||||||
string InstancePath { get; set; }
|
string InstancePath { get; set; }
|
||||||
bool RedownloadPlugins { get; set; }
|
bool NoGui { get; set; }
|
||||||
bool AutomaticUpdates { get; set; }
|
bool NoUpdate { get; set; }
|
||||||
|
List<string> Plugins { get; set; }
|
||||||
bool RestartOnCrash { get; set; }
|
bool RestartOnCrash { get; set; }
|
||||||
|
bool ShouldUpdatePlugins { get; }
|
||||||
|
bool ShouldUpdateTorch { get; }
|
||||||
int TickTimeout { get; set; }
|
int TickTimeout { get; set; }
|
||||||
|
string WaitForPID { get; set; }
|
||||||
|
|
||||||
bool Save(string path = null);
|
bool Save(string path = null);
|
||||||
}
|
}
|
||||||
|
30
Torch.API/Managers/DependencyManagerExtensions.cs
Normal file
30
Torch.API/Managers/DependencyManagerExtensions.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Torch.API.Managers
|
||||||
|
{
|
||||||
|
public static class DependencyManagerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a single manager from this dependency manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="managerType">The dependency type to remove</param>
|
||||||
|
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||||
|
public static IManager RemoveManager(this IDependencyManager depManager, Type managerType)
|
||||||
|
{
|
||||||
|
IManager mgr = depManager.GetManager(managerType);
|
||||||
|
return depManager.RemoveManager(mgr) ? mgr : null;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a single manager from this dependency manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The dependency type to remove</typeparam>
|
||||||
|
/// <returns>The manager that was removed, or null if one wasn't removed</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||||
|
public static IManager RemoveManager<T>(this IDependencyManager depManager)
|
||||||
|
{
|
||||||
|
return depManager.RemoveManager(typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
Torch.API/Managers/DependencyProviderExtensions.cs
Normal file
15
Torch.API/Managers/DependencyProviderExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace Torch.API.Managers
|
||||||
|
{
|
||||||
|
public static class DependencyProviderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the manager that provides the given type. If there is no such manager, returns null.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of manager</typeparam>
|
||||||
|
/// <returns>manager, or null if none exists</returns>
|
||||||
|
public static T GetManager<T>(this IDependencyProvider depProvider) where T : class, IManager
|
||||||
|
{
|
||||||
|
return (T)depProvider.GetManager(typeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
62
Torch.API/Managers/IDependencyManager.cs
Normal file
62
Torch.API/Managers/IDependencyManager.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Torch.API.Managers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages a set of <see cref="IManager"/> and the dependencies between them.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDependencyManager : IDependencyProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the given manager into the dependency system.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method only returns false when there is already a manager registered with a type derived from this given manager,
|
||||||
|
/// or when the given manager is derived from an already existing manager.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="manager">Manager to register</param>
|
||||||
|
/// <exception cref="InvalidOperationException">When adding a new manager to an initialized dependency manager</exception>
|
||||||
|
/// <returns>true if added, false if not</returns>
|
||||||
|
bool AddManager(IManager manager);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all managers registered with this dependency manager
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||||
|
void ClearManagers();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a single manager from this dependency manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager">The manager to remove</param>
|
||||||
|
/// <returns>true if successful, false if the manager wasn't found</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">When removing managers from an initialized dependency manager</exception>
|
||||||
|
bool RemoveManager(IManager manager);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sorts the dependency manager, then attaches all its registered managers in <see cref="AttachOrder" />
|
||||||
|
/// </summary>
|
||||||
|
void Attach();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detaches all registered managers in <see cref="DetachOrder"/>
|
||||||
|
/// </summary>
|
||||||
|
void Detach();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The order that managers should be attached in. (Dependencies, then dependents)
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">When trying to determine load order before this dependency manager is initialized</exception>
|
||||||
|
IEnumerable<IManager> AttachOrder { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The order that managers should be detached in. (Dependents, then dependencies)
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">When trying to determine unload order before this dependency manager is initialized</exception>
|
||||||
|
IEnumerable<IManager> DetachOrder { get; }
|
||||||
|
}
|
||||||
|
}
|
18
Torch.API/Managers/IDependencyProvider.cs
Normal file
18
Torch.API/Managers/IDependencyProvider.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Torch.API.Managers
|
||||||
|
{
|
||||||
|
public interface IDependencyProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the manager that provides the given type. If there is no such manager, returns null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type of manager</param>
|
||||||
|
/// <returns>manager, or null if none exists</returns>
|
||||||
|
IManager GetManager(Type type);
|
||||||
|
}
|
||||||
|
}
|
@@ -6,8 +6,19 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Torch.API.Managers
|
namespace Torch.API.Managers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base interface for Torch managers.
|
||||||
|
/// </summary>
|
||||||
public interface IManager
|
public interface IManager
|
||||||
{
|
{
|
||||||
void Init();
|
/// <summary>
|
||||||
|
/// Attaches the manager to the session. Called once this manager's dependencies have been attached.
|
||||||
|
/// </summary>
|
||||||
|
void Attach();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detaches the manager from the session. Called before this manager's dependencies are detached.
|
||||||
|
/// </summary>
|
||||||
|
void Detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using VRage.Game;
|
|
||||||
using VRage.Game.ModAPI;
|
|
||||||
|
|
||||||
namespace Torch.API.Managers
|
|
||||||
{
|
|
||||||
public delegate void MessageReceivedDel(IChatMessage message, ref bool sendToOthers);
|
|
||||||
|
|
||||||
public interface IMultiplayerManager : IManager
|
|
||||||
{
|
|
||||||
event Action<IPlayer> PlayerJoined;
|
|
||||||
event Action<IPlayer> PlayerLeft;
|
|
||||||
event MessageReceivedDel MessageReceived;
|
|
||||||
void SendMessage(string message, string author = "Server", long playerId = 0, string font = MyFontEnum.Blue);
|
|
||||||
void KickPlayer(ulong steamId);
|
|
||||||
void BanPlayer(ulong steamId, bool banned = true);
|
|
||||||
IMyPlayer GetPlayerBySteamId(ulong id);
|
|
||||||
IMyPlayer GetPlayerByName(string name);
|
|
||||||
}
|
|
||||||
}
|
|
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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
36
Torch.API/Managers/IMultiplayerManagerServer.cs
Normal file
36
Torch.API/Managers/IMultiplayerManagerServer.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -9,14 +9,37 @@ using VRage.Network;
|
|||||||
|
|
||||||
namespace Torch.API.Managers
|
namespace Torch.API.Managers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// API for the network intercept.
|
||||||
|
/// </summary>
|
||||||
public interface INetworkManager : IManager
|
public interface INetworkManager : IManager
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Register a network handler.
|
||||||
|
/// </summary>
|
||||||
void RegisterNetworkHandler(INetworkHandler handler);
|
void RegisterNetworkHandler(INetworkHandler handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister a network handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the handler was unregistered, false if it wasn't registered to begin with</returns>
|
||||||
|
bool UnregisterNetworkHandler(INetworkHandler handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for multiplayer network messages.
|
||||||
|
/// </summary>
|
||||||
public interface INetworkHandler
|
public interface INetworkHandler
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if the handler can process the call site.
|
||||||
|
/// </summary>
|
||||||
bool CanHandle(CallSite callSite);
|
bool CanHandle(CallSite callSite);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes a network message.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the message should be discarded</returns>
|
||||||
bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet);
|
bool Handle(ulong remoteUserId, CallSite site, BitStream stream, object obj, MyPacket packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,11 +6,29 @@ using VRage.Plugins;
|
|||||||
|
|
||||||
namespace Torch.API.Managers
|
namespace Torch.API.Managers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// API for the Torch plugin manager.
|
||||||
|
/// </summary>
|
||||||
public interface IPluginManager : IManager, IEnumerable<ITorchPlugin>
|
public interface IPluginManager : IManager, IEnumerable<ITorchPlugin>
|
||||||
{
|
{
|
||||||
event Action<List<ITorchPlugin>> PluginsLoaded;
|
/// <summary>
|
||||||
List<ITorchPlugin> Plugins { get; }
|
/// Fired when plugins are loaded.
|
||||||
|
/// </summary>
|
||||||
|
event Action<IReadOnlyCollection<ITorchPlugin>> PluginsLoaded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of loaded plugins.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyDictionary<Guid, ITorchPlugin> Plugins { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates all loaded plugins.
|
||||||
|
/// </summary>
|
||||||
void UpdatePlugins();
|
void UpdatePlugins();
|
||||||
void DisposePlugins();
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load plugins.
|
||||||
|
/// </summary>
|
||||||
|
void LoadPlugins();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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.
|
||||||
|
@@ -1,22 +1,56 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Torch.API.Plugins
|
namespace Torch.API.Plugins
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the given type should be loaded by the plugin manager as a plugin.
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("All plugin meta-information is now defined in the manifest.xml.")]
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class PluginAttribute : Attribute
|
public class PluginAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The display name of the plugin
|
||||||
|
/// </summary>
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The version of the plugin
|
||||||
|
/// </summary>
|
||||||
public Version Version { get; }
|
public Version Version { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The GUID of the plugin
|
||||||
|
/// </summary>
|
||||||
public Guid Guid { get; }
|
public Guid Guid { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new plugin attribute with the given attributes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="guid"></param>
|
||||||
public PluginAttribute(string name, string version, string guid)
|
public PluginAttribute(string name, string version, string guid)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Version = Version.Parse(version);
|
Version = Version.Parse(version);
|
||||||
Guid = Guid.Parse(guid);
|
Guid = Guid.Parse(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new plugin attribute with the given attributes. Version is computed as the version of the assembly containing the given type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="versionSupplier">Version is this type's assembly's version</param>
|
||||||
|
/// <param name="guid"></param>
|
||||||
|
public PluginAttribute(string name, Type versionSupplier, string guid)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Version = versionSupplier.Assembly.GetName().Version;
|
||||||
|
Guid = Guid.Parse(guid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
[assembly: AssemblyTitle("Torch API")]
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("TorchAPI")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("TorchAPI")]
|
[assembly: AssemblyProduct("Torch")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
#if DEBUG
|
||||||
[assembly: Guid("fba5d932-6254-4a1e-baf4-e229fa94e3c2")]
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
// Version information for an assembly consists of the following four values:
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
//
|
#endif
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@@ -6,11 +6,29 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Torch.API
|
namespace Torch.API
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to indicate the state of the dedicated server.
|
||||||
|
/// </summary>
|
||||||
public enum ServerState
|
public enum ServerState
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The server is not running.
|
||||||
|
/// </summary>
|
||||||
Stopped,
|
Stopped,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The server is starting/loading the session.
|
||||||
|
/// </summary>
|
||||||
Starting,
|
Starting,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The server is running.
|
||||||
|
/// </summary>
|
||||||
Running,
|
Running,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The server encountered an error.
|
||||||
|
/// </summary>
|
||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
Torch.API/Session/ITorchSession.cs
Normal file
39
Torch.API/Session/ITorchSession.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
|
||||||
|
namespace Torch.API.Session
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the Torch code working with a single game session
|
||||||
|
/// </summary>
|
||||||
|
public interface ITorchSession
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Torch instance this session is bound to
|
||||||
|
/// </summary>
|
||||||
|
ITorchBase Torch { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Space Engineers game session this session is bound to.
|
||||||
|
/// </summary>
|
||||||
|
MySession KeenSession { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IDependencyManager"/>
|
||||||
|
IDependencyManager Managers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current state of the session
|
||||||
|
/// </summary>
|
||||||
|
TorchSessionState State { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised when the <see cref="State"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
event TorchSessionStateChangedDel StateChanged;
|
||||||
|
}
|
||||||
|
}
|
51
Torch.API/Session/ITorchSessionManager.cs
Normal file
51
Torch.API/Session/ITorchSessionManager.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
|
||||||
|
namespace Torch.API.Session
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a manager for the given session if applicable.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is for creating managers that will live inside the session, not the manager that controls sesssions.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="session">The session to construct a bound manager for</param>
|
||||||
|
/// <returns>The manager that will live in the session, or null if none.</returns>
|
||||||
|
public delegate IManager SessionManagerFactoryDel(ITorchSession session);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the creation and destruction of <see cref="ITorchSession"/> instances for each <see cref="Sandbox.Game.World.MySession"/> created by Space Engineers.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITorchSessionManager : IManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The currently running session
|
||||||
|
/// </summary>
|
||||||
|
ITorchSession CurrentSession { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when any <see cref="ITorchSession"/> <see cref="ITorchSession.State"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
event TorchSessionStateChangedDel SessionStateChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the given factory as a supplier for session based managers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="factory">Session based manager supplier</param>
|
||||||
|
/// <returns>true if added, false if already present</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">If the factory is null</exception>
|
||||||
|
bool AddFactory(SessionManagerFactoryDel factory);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the given factory from the suppliers for session based managers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="factory">Session based manager supplier</param>
|
||||||
|
/// <returns>true if removed, false if not present</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">If the factory is null</exception>
|
||||||
|
bool RemoveFactory(SessionManagerFactoryDel factory);
|
||||||
|
}
|
||||||
|
}
|
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);
|
||||||
|
}
|
@@ -2,8 +2,6 @@
|
|||||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}</ProjectGuid>
|
<ProjectGuid>{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}</ProjectGuid>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
@@ -12,10 +10,12 @@
|
|||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
@@ -23,14 +23,14 @@
|
|||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
<OutputPath>bin\x64\Release\</OutputPath>
|
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<DocumentationFile>bin\x64\Release\Torch.API.xml</DocumentationFile>
|
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.API.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64">
|
<Reference Include="HavokWrapper, Version=1.0.6278.22649, Culture=neutral, processorArchitecture=AMD64">
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
<HintPath>..\GameBinaries\HavokWrapper.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NLog">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="PresentationCore" />
|
<Reference Include="PresentationCore" />
|
||||||
@@ -156,14 +156,28 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="ConnectionState.cs" />
|
<Compile Include="ConnectionState.cs" />
|
||||||
<Compile Include="IChatMessage.cs" />
|
<Compile Include="ITorchConfig.cs" />
|
||||||
|
<Compile Include="Managers\DependencyManagerExtensions.cs" />
|
||||||
|
<Compile Include="Managers\DependencyProviderExtensions.cs" />
|
||||||
|
<Compile Include="Event\EventHandlerAttribute.cs" />
|
||||||
|
<Compile Include="Event\IEvent.cs" />
|
||||||
|
<Compile Include="Event\IEventHandler.cs" />
|
||||||
|
<Compile Include="Managers\IChatManagerClient.cs" />
|
||||||
|
<Compile Include="Managers\IChatManagerServer.cs" />
|
||||||
|
<Compile Include="Managers\IDependencyManager.cs" />
|
||||||
|
<Compile Include="Managers\IDependencyProvider.cs" />
|
||||||
|
<Compile Include="Event\IEventManager.cs" />
|
||||||
<Compile Include="Managers\IManager.cs" />
|
<Compile Include="Managers\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="ITorchConfig.cs" />
|
|
||||||
<Compile Include="Plugins\ITorchPlugin.cs" />
|
<Compile Include="Plugins\ITorchPlugin.cs" />
|
||||||
<Compile Include="IServerControls.cs" />
|
<Compile Include="IServerControls.cs" />
|
||||||
<Compile Include="ITorchBase.cs" />
|
<Compile Include="ITorchBase.cs" />
|
||||||
@@ -173,16 +187,17 @@
|
|||||||
<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\ITorchSession.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" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</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);
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="4.4.1" targetFramework="net461" />
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
@@ -1,12 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyTitle("Torch Client")]
|
[assembly: AssemblyTitle("Torch Client Tests")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("Torch")]
|
[assembly: AssemblyProduct("Torch")]
|
||||||
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
96
Torch.Client.Tests/Torch.Client.Tests.csproj
Normal file
96
Torch.Client.Tests/Torch.Client.Tests.csproj
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<ProjectGuid>{632E78C0-0DAC-4B71-B411-2F1B333CC310}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Torch.Client.Tests</RootNamespace>
|
||||||
|
<AssemblyName>Torch.Client.Tests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<NoWarn>1591,0649</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<DocumentationFile>$(SolutionDir)\bin-test\x64\Release\Torch.Client.Tests.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.assert, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.assert.2.2.0\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.core, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.core.2.2.0\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.execution.desktop, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.execution.2.2.0\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TorchClientReflectionTest.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
|
<Name>Torch.API</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Client\Torch.Client.csproj">
|
||||||
|
<Project>{e36df745-260b-4956-a2e8-09f08b2e7161}</Project>
|
||||||
|
<Name>Torch.Client</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj">
|
||||||
|
<Project>{c3c8b671-6ad1-44aa-a8da-e0c0dc0fedf5}</Project>
|
||||||
|
<Name>Torch.Tests</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
|
<Name>Torch</Name>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
|
</Project>
|
94
Torch.Client.Tests/TorchClientReflectionTest.cs
Normal file
94
Torch.Client.Tests/TorchClientReflectionTest.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Torch.Client;
|
||||||
|
using Torch.Tests;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Torch.Client.Tests
|
||||||
|
{
|
||||||
|
public class TorchClientReflectionTest
|
||||||
|
{
|
||||||
|
static TorchClientReflectionTest()
|
||||||
|
{
|
||||||
|
TestUtils.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReflectionTestManager _manager;
|
||||||
|
|
||||||
|
private static ReflectionTestManager Manager()
|
||||||
|
{
|
||||||
|
if (_manager != null)
|
||||||
|
return _manager;
|
||||||
|
|
||||||
|
return _manager = new ReflectionTestManager().Init(typeof(TorchClient).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Getters => Manager().Getters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Setters => Manager().Setters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Events => Manager().Events;
|
||||||
|
|
||||||
|
#region Binding
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Getters))]
|
||||||
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Setters))]
|
||||||
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Invokers))]
|
||||||
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(MemberInfo))]
|
||||||
|
public void TestBindingMemberInfo(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Events))]
|
||||||
|
public void TestBindingEvents(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
((Func<ReflectedEventReplacer>)field.Field.GetValue(null)).Invoke();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
12
Torch.Client.Tests/packages.config
Normal file
12
Torch.Client.Tests/packages.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
|
<package id="xunit" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
|
||||||
|
<package id="xunit.assert" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.execution" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.runner.console" version="2.2.0" targetFramework="net461" developmentDependency="true" />
|
||||||
|
</packages>
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
Torch.Client/Manager/MultiplayerManagerLobby.cs
Normal file
44
Torch.Client/Manager/MultiplayerManagerLobby.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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 override void Attach()
|
||||||
|
{
|
||||||
|
base.Attach();
|
||||||
|
MyMultiplayer.Static.ClientJoined += RaiseClientJoined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Detach()
|
||||||
|
{
|
||||||
|
MyMultiplayer.Static.ClientJoined -= RaiseClientJoined;
|
||||||
|
base.Detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,17 +1,167 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Forms;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using Torch.Utils;
|
||||||
|
using MessageBox = System.Windows.MessageBox;
|
||||||
|
|
||||||
namespace Torch.Client
|
namespace Torch.Client
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
|
public const string SpaceEngineersBinaries = "Bin64";
|
||||||
|
private static string _spaceEngInstallAlias = null;
|
||||||
|
public static string SpaceEngineersInstallAlias
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
|
||||||
|
if (_spaceEngInstallAlias == null)
|
||||||
|
{
|
||||||
|
// ReSharper disable once AssignNullToNotNullAttribute
|
||||||
|
_spaceEngInstallAlias = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "SpaceEngineersAlias");
|
||||||
|
}
|
||||||
|
return _spaceEngInstallAlias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string _steamSpaceEngineersDirectory = @"steamapps\common\SpaceEngineers\";
|
||||||
|
private const string _spaceEngineersVerifyFile = SpaceEngineersBinaries + @"\SpaceEngineers.exe";
|
||||||
|
|
||||||
|
public const string ConfigName = "Torch.cfg";
|
||||||
|
|
||||||
private static Logger _log = LogManager.GetLogger("Torch");
|
private static Logger _log = LogManager.GetLogger("Torch");
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern void AllocConsole();
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern void FreeConsole();
|
||||||
|
#endif
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
#if DEBUG
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AllocConsole();
|
||||||
|
#endif
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||||
|
|
||||||
|
// Early config: Resolve SE install directory.
|
||||||
|
if (!File.Exists(Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile)))
|
||||||
|
SetupSpaceEngInstallAlias();
|
||||||
|
|
||||||
|
using (new TorchAssemblyResolver(Path.Combine(SpaceEngineersInstallAlias, SpaceEngineersBinaries)))
|
||||||
|
{
|
||||||
|
RunClient();
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
FreeConsole();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupSpaceEngInstallAlias()
|
||||||
|
{
|
||||||
|
string spaceEngineersDirectory = null;
|
||||||
|
|
||||||
|
// TODO look at Steam/config/Config.VDF? Has alternate directories.
|
||||||
|
var steamDir =
|
||||||
|
Microsoft.Win32.Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Valve\\Steam", "SteamPath", null) as string;
|
||||||
|
if (steamDir != null)
|
||||||
|
{
|
||||||
|
spaceEngineersDirectory = Path.Combine(steamDir, _steamSpaceEngineersDirectory);
|
||||||
|
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||||
|
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
|
||||||
|
_log.Debug("Found Space Engineers in {0}", spaceEngineersDirectory);
|
||||||
|
else
|
||||||
|
_log.Debug("Couldn't find Space Engineers in {0}", spaceEngineersDirectory);
|
||||||
|
}
|
||||||
|
if (spaceEngineersDirectory == null)
|
||||||
|
{
|
||||||
|
var dialog = new System.Windows.Forms.FolderBrowserDialog
|
||||||
|
{
|
||||||
|
Description = "Please select the SpaceEngineers installation folder"
|
||||||
|
};
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (dialog.ShowDialog() != DialogResult.OK)
|
||||||
|
{
|
||||||
|
var ex = new FileNotFoundException("Unable to find the Space Engineers install directory, aborting");
|
||||||
|
_log.Fatal(ex);
|
||||||
|
LogManager.Flush();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
spaceEngineersDirectory = dialog.SelectedPath;
|
||||||
|
if (File.Exists(Path.Combine(spaceEngineersDirectory, _spaceEngineersVerifyFile)))
|
||||||
|
break;
|
||||||
|
if (MessageBox.Show(
|
||||||
|
$"Unable to find {0} in {1}. Are you sure it's the Space Engineers install directory?",
|
||||||
|
"Invalid Space Engineers Directory", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
|
break;
|
||||||
|
} while (true); // Repeat until they confirm.
|
||||||
|
}
|
||||||
|
if (!JunctionLink(SpaceEngineersInstallAlias, spaceEngineersDirectory))
|
||||||
|
{
|
||||||
|
var ex = new IOException($"Failed to create junction link {SpaceEngineersInstallAlias} => {spaceEngineersDirectory}. Aborting.");
|
||||||
|
_log.Fatal(ex);
|
||||||
|
LogManager.Flush();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
string junctionVerify = Path.Combine(SpaceEngineersInstallAlias, _spaceEngineersVerifyFile);
|
||||||
|
if (!File.Exists(junctionVerify))
|
||||||
|
{
|
||||||
|
var ex = new FileNotFoundException($"Junction link is not working. File {junctionVerify} does not exist");
|
||||||
|
_log.Fatal(ex);
|
||||||
|
LogManager.Flush();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool JunctionLink(string linkName, string targetDir)
|
||||||
|
{
|
||||||
|
var junctionLinkProc = new ProcessStartInfo("cmd.exe", $"/c mklink /J \"{linkName}\" \"{targetDir}\"")
|
||||||
|
{
|
||||||
|
WorkingDirectory = Directory.GetCurrentDirectory(),
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
StandardOutputEncoding = Encoding.ASCII
|
||||||
|
};
|
||||||
|
Process cmd = Process.Start(junctionLinkProc);
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
while (!cmd.HasExited)
|
||||||
|
{
|
||||||
|
string line = cmd.StandardOutput.ReadLine();
|
||||||
|
if (!string.IsNullOrWhiteSpace(line))
|
||||||
|
_log.Info(line);
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
if (cmd.ExitCode != 0)
|
||||||
|
_log.Error("Unable to create junction link {0} => {1}", linkName, targetDir);
|
||||||
|
return cmd.ExitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
var ex = (Exception)e.ExceptionObject;
|
||||||
|
_log.Error(ex);
|
||||||
|
LogManager.Flush();
|
||||||
|
MessageBox.Show(ex.StackTrace, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void RunClient()
|
||||||
|
{
|
||||||
var client = new TorchClient();
|
var client = new TorchClient();
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -27,11 +177,5 @@ namespace Torch.Client
|
|||||||
|
|
||||||
client.Start();
|
client.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
var ex = (Exception)e.ExceptionObject;
|
|
||||||
MessageBox.Show(ex.StackTrace, ex.Message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.182.329")]
|
[assembly: AssemblyTitle("Torch Client")]
|
||||||
[assembly: AssemblyFileVersion("1.0.182.329")]
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Torch")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
@@ -1,16 +0,0 @@
|
|||||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
|
||||||
<#@ assembly name="System.Core" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#@ import namespace="System.Text" #>
|
|
||||||
<#@ import namespace="System.Collections.Generic" #>
|
|
||||||
<#@ output extension=".cs" #>
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
<# var dt = DateTime.Now;
|
|
||||||
int major = 1;
|
|
||||||
int minor = 0;
|
|
||||||
int build = dt.DayOfYear;
|
|
||||||
int rev = (int)dt.TimeOfDay.TotalMinutes / 2;
|
|
||||||
#>
|
|
||||||
[assembly: AssemblyVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
||||||
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
@@ -2,8 +2,6 @@
|
|||||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{E36DF745-260B-4956-A2E8-09F08B2E7161}</ProjectGuid>
|
<ProjectGuid>{E36DF745-260B-4956-A2E8-09F08B2E7161}</ProjectGuid>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
@@ -15,10 +13,12 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
<OutputPath>bin\x64\Release\</OutputPath>
|
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@@ -35,16 +35,21 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
<DocumentationFile>bin\x64\Release\Torch.Client.xml</DocumentationFile>
|
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.Client.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>torchicon.ico</ApplicationIcon>
|
<ApplicationIcon>torchicon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Sandbox.Game">
|
<Reference Include="Sandbox.Game">
|
||||||
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
|
<HintPath>..\GameBinaries\Sandbox.Game.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
@@ -60,6 +65,7 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -77,6 +83,10 @@
|
|||||||
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
|
<HintPath>..\GameBinaries\VRage.Game.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="VRage.Game.XmlSerializers">
|
||||||
|
<HintPath>..\GameBinaries\VRage.Game.XmlSerializers.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
<Reference Include="VRage.Input, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
|
<HintPath>..\GameBinaries\VRage.Input.dll</HintPath>
|
||||||
@@ -99,20 +109,24 @@
|
|||||||
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
|
<HintPath>..\GameBinaries\VRage.Render11.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="VRage.Steam">
|
||||||
|
<HintPath>..\GameBinaries\VRage.Steam.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="WindowsBase" />
|
<Reference Include="WindowsBase" />
|
||||||
<Reference Include="PresentationCore" />
|
<Reference Include="PresentationCore" />
|
||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
<DependentUpon>AssemblyInfo.tt</DependentUpon>
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo1.cs" />
|
<Compile Include="Manager\MultiplayerManagerClient.cs" />
|
||||||
|
<Compile Include="Manager\MultiplayerManagerLobby.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="TorchClient.cs" />
|
<Compile Include="TorchClient.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">
|
||||||
@@ -125,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>
|
||||||
@@ -140,35 +157,25 @@
|
|||||||
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
<Name>Torch.API</Name>
|
<Name>Torch.API</Name>
|
||||||
<Private>True</Private>
|
<Private>False</Private>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Torch\Torch.csproj">
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
<Project>{7E01635C-3B67-472E-BCD6-C5539564F214}</Project>
|
<Project>{7E01635C-3B67-472E-BCD6-C5539564F214}</Project>
|
||||||
<Name>Torch</Name>
|
<Name>Torch</Name>
|
||||||
<Private>True</Private>
|
<Private>False</Private>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Resource Include="torchicon.ico" />
|
<Resource Include="torchicon.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Properties\AssemblyInfo.tt">
|
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
|
||||||
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
<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" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
|
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"
|
||||||
|
</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
@@ -1,18 +1,26 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Reflection;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using Sandbox;
|
using Sandbox;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Engine.Platform;
|
using Sandbox.Engine.Platform;
|
||||||
using Sandbox.Engine.Utils;
|
|
||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
using SpaceEngineers.Game;
|
using SpaceEngineers.Game;
|
||||||
|
using VRage.Steam;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
|
using Torch.Client.Manager;
|
||||||
|
using Torch.Client.UI;
|
||||||
|
using Torch.Session;
|
||||||
|
using VRage;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
|
using VRage.GameServices;
|
||||||
using VRageRender;
|
using VRageRender;
|
||||||
|
using VRageRender.ExternalApp;
|
||||||
|
|
||||||
namespace Torch.Client
|
namespace Torch.Client
|
||||||
{
|
{
|
||||||
@@ -20,51 +28,48 @@ namespace Torch.Client
|
|||||||
{
|
{
|
||||||
private MyCommonProgramStartup _startup;
|
private MyCommonProgramStartup _startup;
|
||||||
private IMyRender _renderer;
|
private IMyRender _renderer;
|
||||||
private const uint APP_ID = 244850;
|
|
||||||
private VRageGameServices _services;
|
protected override uint SteamAppId => 244850;
|
||||||
|
protected override string SteamAppName => "Space Engineers";
|
||||||
|
|
||||||
|
public TorchClient()
|
||||||
|
{
|
||||||
|
Config = new TorchClientConfig();
|
||||||
|
var sessionManager = Managers.GetManager<ITorchSessionManager>();
|
||||||
|
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerLobby
|
||||||
|
? new MultiplayerManagerLobby(this)
|
||||||
|
: null);
|
||||||
|
sessionManager.AddFactory((x) => MyMultiplayer.Static is MyMultiplayerClientBase
|
||||||
|
? new MultiplayerManagerClient(this)
|
||||||
|
: null);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
|
Directory.SetCurrentDirectory(Program.SpaceEngineersInstallAlias);
|
||||||
|
MyFileSystem.ExePath = Path.Combine(Program.SpaceEngineersInstallAlias, Program.SpaceEngineersBinaries);
|
||||||
Log.Info("Initializing Torch Client");
|
Log.Info("Initializing Torch Client");
|
||||||
base.Init();
|
|
||||||
|
|
||||||
if (!File.Exists("steam_appid.txt"))
|
|
||||||
{
|
|
||||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(typeof(VRage.FastResourceLock).Assembly.Location) + "\\..");
|
|
||||||
}
|
|
||||||
|
|
||||||
SpaceEngineersGame.SetupBasicGameInfo();
|
|
||||||
_startup = new MyCommonProgramStartup(RunArgs);
|
_startup = new MyCommonProgramStartup(RunArgs);
|
||||||
|
SpaceEngineersGame.SetupBasicGameInfo();
|
||||||
|
SpaceEngineersGame.SetupPerGameSettings();
|
||||||
if (_startup.PerformReporting())
|
if (_startup.PerformReporting())
|
||||||
return;
|
throw new InvalidOperationException("Torch client won't launch when started in error reporting mode");
|
||||||
|
|
||||||
_startup.PerformAutoconnect();
|
_startup.PerformAutoconnect();
|
||||||
if (!_startup.CheckSingleInstance())
|
if (!_startup.CheckSingleInstance())
|
||||||
return;
|
throw new InvalidOperationException("Only one instance of Space Engineers can be running at a time.");
|
||||||
|
|
||||||
var appDataPath = _startup.GetAppDataPath();
|
var appDataPath = _startup.GetAppDataPath();
|
||||||
MyInitializer.InvokeBeforeRun(APP_ID, MyPerGameSettings.BasicGameInfo.ApplicationName, appDataPath);
|
Config.InstancePath = appDataPath;
|
||||||
MyInitializer.InitCheckSum();
|
base.Init();
|
||||||
if (!_startup.Check64Bit())
|
OverrideMenus();
|
||||||
return;
|
SetRenderWindowTitle($"Space Engineers v{GameVersion} with Torch v{TorchVersion}");
|
||||||
|
}
|
||||||
_startup.DetectSharpDxLeaksBeforeRun();
|
|
||||||
using (var mySteamService = new SteamService(Game.IsDedicated, APP_ID))
|
|
||||||
{
|
|
||||||
_renderer = null;
|
|
||||||
SpaceEngineersGame.SetupPerGameSettings();
|
|
||||||
|
|
||||||
OverrideMenus();
|
|
||||||
|
|
||||||
InitializeRender();
|
|
||||||
|
|
||||||
_services = new VRageGameServices(mySteamService);
|
|
||||||
if (!Game.IsDedicated)
|
|
||||||
MyFileSystem.InitUserSpecific(mySteamService.UserId.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
base.Dispose();
|
||||||
_startup.DetectSharpDxLeaksAfterRun();
|
_startup.DetectSharpDxLeaksAfterRun();
|
||||||
MyInitializer.InvokeAfterRun();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OverrideMenus()
|
private void OverrideMenus()
|
||||||
@@ -82,60 +87,26 @@ namespace Torch.Client
|
|||||||
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
MyPerGameSettings.GUI.MainMenu = typeof(TorchMainMenuScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Start()
|
private void SetRenderWindowTitle(string title)
|
||||||
{
|
{
|
||||||
using (var spaceEngineersGame = new SpaceEngineersGame(_services, RunArgs))
|
MyRenderThread renderThread = MySandboxGame.Static?.GameRenderComponent?.RenderThread;
|
||||||
{
|
if (renderThread == null)
|
||||||
Log.Info("Starting client");
|
return;
|
||||||
spaceEngineersGame.OnGameLoaded += SpaceEngineersGame_OnGameLoaded;
|
FieldInfo renderWindowField = typeof(MyRenderThread).GetField("m_renderWindow",
|
||||||
spaceEngineersGame.Run();
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
}
|
if (renderWindowField == null)
|
||||||
}
|
return;
|
||||||
|
var window = renderWindowField.GetValue(MySandboxGame.Static.GameRenderComponent.RenderThread) as System.Windows.Forms.Form;
|
||||||
private void SpaceEngineersGame_OnGameLoaded(object sender, EventArgs e)
|
if (window != null)
|
||||||
{
|
renderThread.Invoke(() =>
|
||||||
}
|
|
||||||
|
|
||||||
public override void Stop()
|
|
||||||
{
|
|
||||||
MySandboxGame.ExitThreadSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeRender()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Game.IsDedicated)
|
|
||||||
{
|
{
|
||||||
_renderer = new MyNullRender();
|
window.Text = title;
|
||||||
}
|
});
|
||||||
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;
|
public override void Restart()
|
||||||
}
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
MyRenderProxy.Initialize(_renderer);
|
|
||||||
MyRenderProxy.GetRenderProfiler().SetAutocommit(false);
|
|
||||||
MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
MessageBox.Show(ex.Message, "Render Initialization Failed");
|
|
||||||
Environment.Exit(-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
Torch.Client/TorchClientConfig.cs
Normal file
32
Torch.Client/TorchClientConfig.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Torch.Client
|
||||||
|
{
|
||||||
|
public class TorchClientConfig : ITorchConfig
|
||||||
|
{
|
||||||
|
// How do we want to handle client side config? It's radically different than the server.
|
||||||
|
public bool GetPluginUpdates { get; set; } = false;
|
||||||
|
public bool GetTorchUpdates { get; set; } = false;
|
||||||
|
public string InstanceName { get; set; } = "TorchClient";
|
||||||
|
public string InstancePath { get; set; }
|
||||||
|
public bool NoUpdate { get; set; } = true;
|
||||||
|
public List<string> Plugins { get; set; }
|
||||||
|
public bool ShouldUpdatePlugins { get; } = false;
|
||||||
|
public bool ShouldUpdateTorch { get; } = false;
|
||||||
|
public int TickTimeout { get; set; }
|
||||||
|
public bool Autostart { get; set; } = false;
|
||||||
|
public bool ForceUpdate { get; set; } = false;
|
||||||
|
public bool NoGui { get; set; } = false;
|
||||||
|
public bool RestartOnCrash { get; set; } = false;
|
||||||
|
public string WaitForPID { get; set; } = null;
|
||||||
|
|
||||||
|
public bool Save(string path = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="4.4.1" targetFramework="net461" />
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
17
Torch.Server.Tests/Properties/AssemblyInfo.cs
Normal file
17
Torch.Server.Tests/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("Torch Server Tests")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Torch")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
93
Torch.Server.Tests/Torch.Server.Tests.csproj
Normal file
93
Torch.Server.Tests/Torch.Server.Tests.csproj
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<ProjectGuid>{9EFD1D91-2FA2-47ED-B537-D8BC3B0E543E}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Torch.Server.Tests</RootNamespace>
|
||||||
|
<AssemblyName>Torch.Server.Tests</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<NoWarn>1591,0649</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
|
<OutputPath>$(SolutionDir)\bin-test\x64\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<DocumentationFile>$(SolutionDir)\bin-test\x64\Release\Torch.Server.Tests.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.assert, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.assert.2.2.0\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.core, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.core.2.2.0\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xunit.execution.desktop, Version=2.2.0.3545, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\xunit.extensibility.execution.2.2.0\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TorchServerReflectionTest.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
|
<Name>Torch.API</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Server\Torch.Server.csproj">
|
||||||
|
<Project>{ca50886b-7b22-4cd8-93a0-c06f38d4f77d}</Project>
|
||||||
|
<Name>Torch.Server</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch.Tests\Torch.Tests.csproj">
|
||||||
|
<Project>{c3c8b671-6ad1-44aa-a8da-e0c0dc0fedf5}</Project>
|
||||||
|
<Name>Torch.Tests</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
|
<Name>Torch</Name>
|
||||||
|
<Private>True</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
|
</Project>
|
82
Torch.Server.Tests/TorchServerReflectionTest.cs
Normal file
82
Torch.Server.Tests/TorchServerReflectionTest.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Torch.Tests;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Torch.Server.Tests
|
||||||
|
{
|
||||||
|
public class TorchServerReflectionTest
|
||||||
|
{
|
||||||
|
static TorchServerReflectionTest()
|
||||||
|
{
|
||||||
|
TestUtils.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReflectionTestManager _manager;
|
||||||
|
|
||||||
|
private static ReflectionTestManager Manager()
|
||||||
|
{
|
||||||
|
if (_manager != null)
|
||||||
|
return _manager;
|
||||||
|
|
||||||
|
return _manager = new ReflectionTestManager().Init(typeof(TorchServer).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Getters => Manager().Getters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Setters => Manager().Setters;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Invokers => Manager().Invokers;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> MemberInfo => Manager().MemberInfo;
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> Events => Manager().Events;
|
||||||
|
|
||||||
|
#region Binding
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Getters))]
|
||||||
|
public void TestBindingGetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Setters))]
|
||||||
|
public void TestBindingSetter(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Invokers))]
|
||||||
|
public void TestBindingInvoker(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
Assert.NotNull(field.Field.GetValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(Events))]
|
||||||
|
public void TestBindingEvents(ReflectionTestManager.FieldRef field)
|
||||||
|
{
|
||||||
|
if (field.Field == null)
|
||||||
|
return;
|
||||||
|
Assert.True(ReflectedManager.Process(field.Field));
|
||||||
|
if (field.Field.IsStatic)
|
||||||
|
((Func<ReflectedEventReplacer>)field.Field.GetValue(null)).Invoke();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
12
Torch.Server.Tests/packages.config
Normal file
12
Torch.Server.Tests/packages.config
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Mono.TextTransform" version="1.0.0" targetFramework="net461" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net461" />
|
||||||
|
<package id="xunit" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />
|
||||||
|
<package id="xunit.assert" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.core" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.extensibility.execution" version="2.2.0" targetFramework="net461" />
|
||||||
|
<package id="xunit.runner.console" version="2.2.0" targetFramework="net461" developmentDependency="true" />
|
||||||
|
</packages>
|
196
Torch.Server/Initializer.cs
Normal file
196
Torch.Server/Initializer.cs
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using NLog;
|
||||||
|
using Torch.Utils;
|
||||||
|
|
||||||
|
namespace Torch.Server
|
||||||
|
{
|
||||||
|
public class Initializer
|
||||||
|
{
|
||||||
|
private static readonly Logger Log = LogManager.GetLogger(nameof(Initializer));
|
||||||
|
private bool _init;
|
||||||
|
private const string STEAMCMD_DIR = "steamcmd";
|
||||||
|
private const string STEAMCMD_ZIP = "temp.zip";
|
||||||
|
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
||||||
|
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
||||||
|
private const string RUNSCRIPT = @"force_install_dir ../
|
||||||
|
login anonymous
|
||||||
|
app_update 298740
|
||||||
|
quit";
|
||||||
|
|
||||||
|
private TorchConfig _config;
|
||||||
|
private TorchServer _server;
|
||||||
|
private string _basePath;
|
||||||
|
|
||||||
|
public TorchConfig Config => _config;
|
||||||
|
public TorchServer Server => _server;
|
||||||
|
|
||||||
|
public Initializer(string basePath)
|
||||||
|
{
|
||||||
|
_basePath = basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Initialize(string[] args)
|
||||||
|
{
|
||||||
|
if (_init)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!args.Contains("-noupdate"))
|
||||||
|
RunSteamCmd();
|
||||||
|
|
||||||
|
_config = InitConfig();
|
||||||
|
if (!_config.Parse(args))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_config.WaitForPID))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pid = int.Parse(_config.WaitForPID);
|
||||||
|
var waitProc = Process.GetProcessById(pid);
|
||||||
|
Log.Info("Continuing in 5 seconds.");
|
||||||
|
Log.Warn($"Waiting for process {pid} to close");
|
||||||
|
while (!waitProc.HasExited)
|
||||||
|
{
|
||||||
|
Console.Write(".");
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_init = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
_server = new TorchServer(_config);
|
||||||
|
_server.Init();
|
||||||
|
|
||||||
|
if (!_config.NoGui)
|
||||||
|
{
|
||||||
|
var ui = new TorchUI(_server);
|
||||||
|
if (_config.Autostart)
|
||||||
|
_server.Start();
|
||||||
|
ui.ShowDialog();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_server.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TorchConfig InitConfig()
|
||||||
|
{
|
||||||
|
var configName = "Torch.cfg";
|
||||||
|
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
||||||
|
if (File.Exists(configName))
|
||||||
|
{
|
||||||
|
Log.Info($"Loading config {configPath}");
|
||||||
|
return TorchConfig.LoadFrom(configPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Info($"Generating default config at {configPath}");
|
||||||
|
var config = new TorchConfig { InstancePath = Path.GetFullPath("Instance") };
|
||||||
|
config.Save(configPath);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunSteamCmd()
|
||||||
|
{
|
||||||
|
var log = LogManager.GetLogger("SteamCMD");
|
||||||
|
|
||||||
|
if (!Directory.Exists(STEAMCMD_DIR))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(STEAMCMD_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(RUNSCRIPT_PATH))
|
||||||
|
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
|
||||||
|
|
||||||
|
if (!File.Exists(STEAMCMD_PATH))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.Info("Downloading SteamCMD.");
|
||||||
|
using (var client = new WebClient())
|
||||||
|
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
|
||||||
|
|
||||||
|
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
|
||||||
|
File.Delete(STEAMCMD_ZIP);
|
||||||
|
log.Info("SteamCMD downloaded successfully!");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
log.Error("Failed to download SteamCMD, unable to update the DS.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Checking for DS updates.");
|
||||||
|
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
|
||||||
|
{
|
||||||
|
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
StandardOutputEncoding = Encoding.ASCII
|
||||||
|
};
|
||||||
|
var cmd = Process.Start(steamCmdProc);
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
|
while (!cmd.HasExited)
|
||||||
|
{
|
||||||
|
log.Info(cmd.StandardOutput.ReadLine());
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
var ex = (Exception)e.ExceptionObject;
|
||||||
|
LogException(ex);
|
||||||
|
Console.WriteLine("Exiting in 5 seconds.");
|
||||||
|
Thread.Sleep(5000);
|
||||||
|
LogManager.Flush();
|
||||||
|
if (_config.RestartOnCrash)
|
||||||
|
{
|
||||||
|
var exe = typeof(Program).Assembly.Location;
|
||||||
|
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
Process.Start(exe, _config.ToString());
|
||||||
|
}
|
||||||
|
Process.GetCurrentProcess().Kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
Torch.Server/ListBoxExtensions.cs
Normal file
59
Torch.Server/ListBoxExtensions.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace Torch.Server
|
||||||
|
{
|
||||||
|
public static class ListBoxExtensions
|
||||||
|
{
|
||||||
|
//https://stackoverflow.com/questions/28689125/how-to-autoscroll-listbox-to-bottom-wpf-c
|
||||||
|
public static void ScrollToItem(this ListBox listBox, int index)
|
||||||
|
{
|
||||||
|
// Find a container
|
||||||
|
UIElement container = null;
|
||||||
|
for (int i = index; i > 0; i--)
|
||||||
|
{
|
||||||
|
container = listBox.ItemContainerGenerator.ContainerFromIndex(i) as UIElement;
|
||||||
|
if (container != null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (container == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find the ScrollContentPresenter
|
||||||
|
ScrollContentPresenter presenter = null;
|
||||||
|
for (Visual vis = container; vis != null && vis != listBox; vis = VisualTreeHelper.GetParent(vis) as Visual)
|
||||||
|
if ((presenter = vis as ScrollContentPresenter) != null)
|
||||||
|
break;
|
||||||
|
if (presenter == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find the IScrollInfo
|
||||||
|
var scrollInfo =
|
||||||
|
!presenter.CanContentScroll ? presenter :
|
||||||
|
presenter.Content as IScrollInfo ??
|
||||||
|
FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ??
|
||||||
|
presenter;
|
||||||
|
|
||||||
|
// Find the amount of items that is "Visible" in the ListBox
|
||||||
|
var height = (container as ListBoxItem).ActualHeight;
|
||||||
|
var lbHeight = listBox.ActualHeight;
|
||||||
|
var showCount = (int)Math.Floor(lbHeight / height) - 1;
|
||||||
|
|
||||||
|
//Set the scrollbar
|
||||||
|
if (scrollInfo.CanVerticallyScroll)
|
||||||
|
scrollInfo.SetVerticalOffset(index - showCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DependencyObject FirstVisualChild(Visual visual)
|
||||||
|
{
|
||||||
|
if (visual == null) return null;
|
||||||
|
if (VisualTreeHelper.GetChildrenCount(visual) == 0) return null;
|
||||||
|
return VisualTreeHelper.GetChild(visual, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,62 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Sandbox.Engine.Utils;
|
|
||||||
using Torch.API;
|
|
||||||
using Torch.API.Managers;
|
|
||||||
using Torch.Managers;
|
|
||||||
using Torch.Server.ViewModels;
|
|
||||||
using VRage.Game;
|
|
||||||
|
|
||||||
namespace Torch.Server.Managers
|
|
||||||
{
|
|
||||||
//TODO
|
|
||||||
public class ConfigManager : Manager
|
|
||||||
{
|
|
||||||
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
|
||||||
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
|
||||||
public TorchConfig TorchConfig { get; set; }
|
|
||||||
|
|
||||||
public ConfigManager(ITorchBase torchInstance) : base(torchInstance)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Init()
|
|
||||||
{
|
|
||||||
LoadInstance(Torch.Config.InstancePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadInstance(string path)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(path))
|
|
||||||
throw new FileNotFoundException($"Instance directory not found at '{path}'");
|
|
||||||
|
|
||||||
var configPath = Path.Combine(path, CONFIG_NAME);
|
|
||||||
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
|
|
||||||
config.Load();
|
|
||||||
DedicatedConfig = new ConfigDedicatedViewModel(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a skeleton of a DS instance folder at the given directory.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path"></param>
|
|
||||||
public void CreateInstance(string path)
|
|
||||||
{
|
|
||||||
if (Directory.Exists(path))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Directory.CreateDirectory(path);
|
|
||||||
var savesPath = Path.Combine(path, "Saves");
|
|
||||||
Directory.CreateDirectory(savesPath);
|
|
||||||
var modsPath = Path.Combine(path, "Mods");
|
|
||||||
Directory.CreateDirectory(modsPath);
|
|
||||||
LoadInstance(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
Torch.Server/Managers/InstanceManager.cs
Normal file
165
Torch.Server/Managers/InstanceManager.cs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Havok;
|
||||||
|
using NLog;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Engine.Utils;
|
||||||
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Managers;
|
||||||
|
using Torch.Server.ViewModels;
|
||||||
|
using VRage.FileSystem;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
|
|
||||||
|
namespace Torch.Server.Managers
|
||||||
|
{
|
||||||
|
public class InstanceManager : Manager
|
||||||
|
{
|
||||||
|
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
|
||||||
|
public ConfigDedicatedViewModel DedicatedConfig { get; set; }
|
||||||
|
private static readonly Logger Log = LogManager.GetLogger(nameof(InstanceManager));
|
||||||
|
[Dependency]
|
||||||
|
private FilesystemManager _filesystemManager;
|
||||||
|
|
||||||
|
public InstanceManager(ITorchBase torchInstance) : base(torchInstance)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadInstance(string path, bool validate = true)
|
||||||
|
{
|
||||||
|
if (validate)
|
||||||
|
ValidateInstance(path);
|
||||||
|
|
||||||
|
MyFileSystem.Reset();
|
||||||
|
MyFileSystem.Init("Content", path);
|
||||||
|
//Initializes saves path. Why this isn't in Init() we may never know.
|
||||||
|
MyFileSystem.InitUserSpecific(null);
|
||||||
|
|
||||||
|
var configPath = Path.Combine(path, CONFIG_NAME);
|
||||||
|
if (!File.Exists(configPath))
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to load dedicated config at {path}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
|
||||||
|
config.Load(configPath);
|
||||||
|
|
||||||
|
DedicatedConfig = new ConfigDedicatedViewModel(config);
|
||||||
|
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
|
||||||
|
|
||||||
|
foreach (var f in worldFolders)
|
||||||
|
DedicatedConfig.WorldPaths.Add(f);
|
||||||
|
|
||||||
|
if (DedicatedConfig.WorldPaths.Count == 0)
|
||||||
|
{
|
||||||
|
Log.Warn($"No worlds found in the current instance {path}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportWorldConfig();
|
||||||
|
|
||||||
|
/*
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
DedicatedConfig.LoadWorld = worldPath;
|
||||||
|
ImportWorldConfig(modsOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ImportWorldConfig(bool modsOnly = true)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(DedicatedConfig.LoadWorld))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sandboxPath = Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc");
|
||||||
|
|
||||||
|
if (!File.Exists(sandboxPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||||
|
if (checkpoint == null)
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var mod in checkpoint.Mods)
|
||||||
|
sb.AppendLine(mod.PublishedFileId.ToString());
|
||||||
|
|
||||||
|
DedicatedConfig.Mods = sb.ToString();
|
||||||
|
|
||||||
|
Log.Debug("Loaded mod list from world");
|
||||||
|
|
||||||
|
if (!modsOnly)
|
||||||
|
DedicatedConfig.SessionSettings = new SessionSettingsViewModel(checkpoint.Settings);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"Error loading mod list from world, verify that your mod list is accurate. '{DedicatedConfig.LoadWorld}'.");
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveConfig()
|
||||||
|
{
|
||||||
|
DedicatedConfig.Save(Path.Combine(Torch.Config.InstancePath, CONFIG_NAME));
|
||||||
|
Log.Info("Saved dedicated config.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MyObjectBuilderSerializer.DeserializeXML(Path.Combine(DedicatedConfig.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
||||||
|
if (checkpoint == null)
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to load {DedicatedConfig.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkpoint.Settings = DedicatedConfig.SessionSettings;
|
||||||
|
checkpoint.Mods.Clear();
|
||||||
|
foreach (var modId in DedicatedConfig.Model.Mods)
|
||||||
|
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
||||||
|
|
||||||
|
MyLocalCache.SaveCheckpoint(checkpoint, DedicatedConfig.LoadWorld);
|
||||||
|
Log.Info("Saved world config.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error("Failed to write sandbox config, changes will not appear on server");
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that the given path is a valid server instance.
|
||||||
|
/// </summary>
|
||||||
|
private void ValidateInstance(string path)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(path, "Saves"));
|
||||||
|
Directory.CreateDirectory(Path.Combine(path, "Mods"));
|
||||||
|
var configPath = Path.Combine(path, CONFIG_NAME);
|
||||||
|
if (File.Exists(configPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(configPath);
|
||||||
|
config.Save(configPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
234
Torch.Server/Managers/MultiplayerManagerDedicated.cs
Normal file
234
Torch.Server/Managers/MultiplayerManagerDedicated.cs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
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 Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
|
using Torch.ViewModels;
|
||||||
|
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>();
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
if (_gameOwnerIds.ContainsKey(steamId))
|
||||||
|
MyMultiplayer.Static.BanClient(_gameOwnerIds[steamId], banned);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsBanned(ulong steamId) => _isClientBanned.Invoke(MyMultiplayer.Static, steamId) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Banned.Contains(steamId);
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
_log.Debug($"ValidateAuthTicketResponse(user={steamID}, response={response}, owner={steamOwner}");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
MultiplayerManagerDedicatedEventShim.RaiseValidateAuthTicket(ref info);
|
||||||
|
|
||||||
|
if (info.FutureVerdict == null)
|
||||||
|
{
|
||||||
|
if (IsBanned(info.SteamOwner) || IsBanned(info.SteamID))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.BannedByAdmins);
|
||||||
|
else if (_isClientKicked(MyMultiplayer.Static, info.SteamID) ||
|
||||||
|
_isClientKicked(MyMultiplayer.Static, info.SteamOwner))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.KickedRecently);
|
||||||
|
else if (info.SteamResponse != JoinResult.OK)
|
||||||
|
CommitVerdict(info.SteamID, info.SteamResponse);
|
||||||
|
else if (MyMultiplayer.Static.MemberLimit > 0 &&
|
||||||
|
MyMultiplayer.Static.MemberCount + 1 > MyMultiplayer.Static.MemberLimit)
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.ServerFull);
|
||||||
|
else if (MySandboxGame.ConfigDedicated.GroupID == 0uL ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(info.SteamID.ToString()) ||
|
||||||
|
MySandboxGame.ConfigDedicated.Administrators.Contains(_convertSteamIDFrom64(info.SteamID)))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.OK);
|
||||||
|
else if (MySandboxGame.ConfigDedicated.GroupID == info.Group && (info.Member || info.Officer))
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.OK);
|
||||||
|
else
|
||||||
|
CommitVerdict(info.SteamID, JoinResult.NotInGroup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -23,7 +23,9 @@ using Torch.Server.Views;
|
|||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Security.Policy;
|
||||||
using Torch.Server.Managers;
|
using Torch.Server.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRageRender;
|
using VRageRender;
|
||||||
|
|
||||||
@@ -31,284 +33,35 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
private static ITorchServer _server;
|
/// <remarks>
|
||||||
private static Logger _log = LogManager.GetLogger("Torch");
|
|
||||||
private static bool _restartOnCrash;
|
|
||||||
public static bool IsManualInstall;
|
|
||||||
private static TorchCli _cli;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
|
/// This method must *NOT* load any types/assemblies from the vanilla game, otherwise automatic updates will fail.
|
||||||
/// </summary>
|
/// </remarks>
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
//Ensures that all the files are downloaded in the Torch directory.
|
//Ensures that all the files are downloaded in the Torch directory.
|
||||||
Directory.SetCurrentDirectory(new FileInfo(typeof(Program).Assembly.Location).Directory.ToString());
|
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory.ToString();
|
||||||
|
var binDir = Path.Combine(workingDir, "DedicatedServer64");
|
||||||
|
Directory.SetCurrentDirectory(workingDir);
|
||||||
|
|
||||||
IsManualInstall = File.Exists("SpaceEngineersDedicated.exe");
|
if (!TorchLauncher.IsTorchWrapped())
|
||||||
if (!IsManualInstall)
|
{
|
||||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
TorchLauncher.Launch(Assembly.GetEntryAssembly().FullName,args, binDir);
|
||||||
|
return;
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
}
|
||||||
|
|
||||||
if (!Environment.UserInteractive)
|
if (!Environment.UserInteractive)
|
||||||
{
|
{
|
||||||
using (var service = new TorchService())
|
using (var service = new TorchService())
|
||||||
{
|
|
||||||
ServiceBase.Run(service);
|
ServiceBase.Run(service);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configName = "TorchConfig.xml";
|
var initializer = new Initializer(workingDir);
|
||||||
var configPath = Path.Combine(Directory.GetCurrentDirectory(), configName);
|
if (!initializer.Initialize(args))
|
||||||
TorchConfig options;
|
|
||||||
if (File.Exists(configName))
|
|
||||||
{
|
|
||||||
_log.Info($"Loading config {configPath}");
|
|
||||||
options = TorchConfig.LoadFrom(configPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_log.Info($"Generating default config at {configPath}");
|
|
||||||
options = new TorchConfig();
|
|
||||||
|
|
||||||
if (!IsManualInstall)
|
|
||||||
{
|
|
||||||
//new ConfigManager().CreateInstance("Instance");
|
|
||||||
options.InstancePath = Path.GetFullPath("Instance");
|
|
||||||
|
|
||||||
_log.Warn("Would you like to enable automatic updates? (Y/n):");
|
|
||||||
|
|
||||||
var input = Console.ReadLine() ?? "";
|
|
||||||
var autoUpdate = !input.Equals("n", StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
options.AutomaticUpdates = autoUpdate;
|
|
||||||
if (autoUpdate)
|
|
||||||
{
|
|
||||||
_log.Info("Automatic updates enabled, updating server.");
|
|
||||||
RunSteamCmd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//var setupDialog = new FirstTimeSetup { DataContext = options };
|
|
||||||
//setupDialog.ShowDialog();
|
|
||||||
options.Save(configPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
_cli = new TorchCli { Config = options };
|
|
||||||
if (!_cli.Parse(args))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_log.Debug(_cli.ToString());
|
initializer.Run();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_cli.WaitForPID))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var pid = int.Parse(_cli.WaitForPID);
|
|
||||||
var waitProc = Process.GetProcessById(pid);
|
|
||||||
_log.Warn($"Waiting for process {pid} to exit.");
|
|
||||||
waitProc.WaitForExit();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_restartOnCrash = _cli.RestartOnCrash;
|
|
||||||
|
|
||||||
if (options.AutomaticUpdates || _cli.Update)
|
|
||||||
{
|
|
||||||
if (IsManualInstall)
|
|
||||||
_log.Warn("Detected manual install, won't attempt to update DS");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RunSteamCmd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RunServer(options, _cli);
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string STEAMCMD_DIR = "steamcmd";
|
|
||||||
private const string STEAMCMD_ZIP = "temp.zip";
|
|
||||||
private static readonly string STEAMCMD_PATH = $"{STEAMCMD_DIR}\\steamcmd.exe";
|
|
||||||
private static readonly string RUNSCRIPT_PATH = $"{STEAMCMD_DIR}\\runscript.txt";
|
|
||||||
private const string RUNSCRIPT = @"force_install_dir ../
|
|
||||||
login anonymous
|
|
||||||
app_update 298740
|
|
||||||
quit";
|
|
||||||
|
|
||||||
public static void RunSteamCmd()
|
|
||||||
{
|
|
||||||
var log = LogManager.GetLogger("SteamCMD");
|
|
||||||
|
|
||||||
if (!Directory.Exists(STEAMCMD_DIR))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(STEAMCMD_DIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(RUNSCRIPT_PATH))
|
|
||||||
File.WriteAllText(RUNSCRIPT_PATH, RUNSCRIPT);
|
|
||||||
|
|
||||||
if (!File.Exists(STEAMCMD_PATH))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log.Info("Downloading SteamCMD.");
|
|
||||||
using (var client = new WebClient())
|
|
||||||
client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
|
|
||||||
|
|
||||||
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
|
|
||||||
File.Delete(STEAMCMD_ZIP);
|
|
||||||
log.Info("SteamCMD downloaded successfully!");
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
log.Error("Failed to download SteamCMD, unable to update the DS.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Checking for DS updates.");
|
|
||||||
var steamCmdProc = new ProcessStartInfo(STEAMCMD_PATH, "+runscript runscript.txt")
|
|
||||||
{
|
|
||||||
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), STEAMCMD_DIR),
|
|
||||||
UseShellExecute = false,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
StandardOutputEncoding = Encoding.ASCII
|
|
||||||
};
|
|
||||||
var cmd = Process.Start(steamCmdProc);
|
|
||||||
|
|
||||||
// ReSharper disable once PossibleNullReferenceException
|
|
||||||
while (!cmd.HasExited)
|
|
||||||
{
|
|
||||||
log.Info(cmd.StandardOutput.ReadLine());
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RunServer(TorchConfig options, TorchCli cli)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (!parser.ParseArguments(args, options))
|
|
||||||
{
|
|
||||||
_log.Error($"Parsing arguments failed: {string.Join(" ", args)}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(options.Config) && File.Exists(options.Config))
|
|
||||||
{
|
|
||||||
options = ServerConfig.LoadFrom(options.Config);
|
|
||||||
parser.ParseArguments(args, options);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//RestartOnCrash autostart autosave=15
|
|
||||||
//gamepath ="C:\Program Files\Space Engineers DS" instance="Hydro Survival" instancepath="C:\ProgramData\SpaceEngineersDedicated\Hydro Survival"
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (options.InstallService)
|
|
||||||
{
|
|
||||||
var serviceName = $"\"Torch - {options.InstanceName}\"";
|
|
||||||
// Working on installing the service properly instead of with sc.exe
|
|
||||||
_log.Info($"Installing service '{serviceName}");
|
|
||||||
var exePath = $"\"{Assembly.GetExecutingAssembly().Location}\"";
|
|
||||||
var createInfo = new ServiceCreateInfo
|
|
||||||
{
|
|
||||||
Name = options.InstanceName,
|
|
||||||
BinaryPath = exePath,
|
|
||||||
};
|
|
||||||
_log.Info("Service Installed");
|
|
||||||
|
|
||||||
var runArgs = string.Join(" ", args.Skip(1));
|
|
||||||
_log.Info($"Installing Torch as a service with arguments '{runArgs}'");
|
|
||||||
var startInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "sc.exe",
|
|
||||||
Arguments = $"create Torch binPath=\"{Assembly.GetExecutingAssembly().Location} {runArgs}\"",
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "runas"
|
|
||||||
};
|
|
||||||
Process.Start(startInfo).WaitForExit();
|
|
||||||
_log.Info("Torch service installed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.UninstallService)
|
|
||||||
{
|
|
||||||
_log.Info("Uninstalling Torch service");
|
|
||||||
var startInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = "sc.exe",
|
|
||||||
Arguments = "delete Torch",
|
|
||||||
CreateNoWindow = true,
|
|
||||||
UseShellExecute = true,
|
|
||||||
Verb = "runas"
|
|
||||||
};
|
|
||||||
Process.Start(startInfo).WaitForExit();
|
|
||||||
_log.Info("Torch service uninstalled");
|
|
||||||
return;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
_server = new TorchServer(options);
|
|
||||||
_server.Init();
|
|
||||||
|
|
||||||
if (cli.NoGui || cli.Autostart)
|
|
||||||
{
|
|
||||||
new Thread(() => _server.Start()).Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cli.NoGui)
|
|
||||||
{
|
|
||||||
var ui = new TorchUI((TorchServer)_server);
|
|
||||||
ui.LoadConfig(options);
|
|
||||||
ui.ShowDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var basePath = Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location), "DedicatedServer64");
|
|
||||||
string asmPath = Path.Combine(basePath, new AssemblyName(args.Name).Name + ".dll");
|
|
||||||
if (File.Exists(asmPath))
|
|
||||||
return Assembly.LoadFrom(asmPath);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
var ex = (Exception)e.ExceptionObject;
|
|
||||||
_log.Fatal(ex);
|
|
||||||
Console.WriteLine("Exiting in 5 seconds.");
|
|
||||||
Thread.Sleep(5000);
|
|
||||||
if (_restartOnCrash)
|
|
||||||
{
|
|
||||||
/* Throws an exception somehow and I'm too lazy to debug it.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (MySession.Static != null && MySession.Static.AutoSaveInMinutes > 0)
|
|
||||||
MySession.Static.Save();
|
|
||||||
}
|
|
||||||
catch { }*/
|
|
||||||
|
|
||||||
var exe = typeof(Program).Assembly.Location;
|
|
||||||
_cli.WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
|
||||||
Process.Start(exe, _cli.ToString());
|
|
||||||
}
|
|
||||||
//1627 = Function failed during execution.
|
|
||||||
Environment.Exit(1627);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,17 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.182.329")]
|
[assembly: AssemblyTitle("Torch Server")]
|
||||||
[assembly: AssemblyFileVersion("1.0.182.329")]
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Torch")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Torch API 2017")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
[assembly: AssemblyConfiguration("Debug")]
|
||||||
|
#else
|
||||||
|
[assembly: AssemblyConfiguration("Release")]
|
||||||
|
#endif
|
@@ -1,16 +0,0 @@
|
|||||||
<#@ template debug="false" hostspecific="false" language="C#" #>
|
|
||||||
<#@ assembly name="System.Core" #>
|
|
||||||
<#@ import namespace="System.Linq" #>
|
|
||||||
<#@ import namespace="System.Text" #>
|
|
||||||
<#@ import namespace="System.Collections.Generic" #>
|
|
||||||
<#@ output extension=".cs" #>
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
<# var dt = DateTime.Now;
|
|
||||||
int major = 1;
|
|
||||||
int minor = 0;
|
|
||||||
int build = dt.DayOfYear;
|
|
||||||
int rev = (int)dt.TimeOfDay.TotalMinutes / 2;
|
|
||||||
#>
|
|
||||||
[assembly: AssemblyVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
||||||
[assembly: AssemblyFileVersion("<#= major #>.<#= minor #>.<#= build #>.<#= rev #>")]
|
|
@@ -2,8 +2,6 @@
|
|||||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}</ProjectGuid>
|
<ProjectGuid>{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}</ProjectGuid>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
@@ -15,10 +13,12 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
<OutputPath>$(SolutionDir)\bin\x64\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||||
<OutputPath>bin\x64\Release\</OutputPath>
|
<OutputPath>$(SolutionDir)\bin\x64\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
<DocumentationFile>bin\x64\Release\Torch.Server.xml</DocumentationFile>
|
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.Server.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<StartupObject>Torch.Server.Program</StartupObject>
|
<StartupObject>Torch.Server.Program</StartupObject>
|
||||||
@@ -59,8 +59,11 @@
|
|||||||
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
|
<HintPath>..\GameBinaries\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\NLog.4.4.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="Sandbox.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
@@ -122,6 +125,7 @@
|
|||||||
<Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
<Reference Include="VRage.Audio, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\GameBinaries\VRage.Audio.dll</HintPath>
|
<HintPath>..\GameBinaries\VRage.Audio.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="VRage.Dedicated, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
<Reference Include="VRage.Dedicated, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
@@ -178,20 +182,26 @@
|
|||||||
<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" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Managers\ConfigManager.cs" />
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
<Compile Include="TorchCli.cs" />
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
<Compile Include="NativeMethods.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<DependentUpon>AssemblyInfo.tt</DependentUpon>
|
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Properties\AssemblyInfo1.cs" />
|
<Compile Include="ListBoxExtensions.cs" />
|
||||||
|
<Compile Include="Managers\EntityControlManager.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicated.cs" />
|
||||||
|
<Compile Include="Managers\InstanceManager.cs" />
|
||||||
|
<Compile Include="Managers\MultiplayerManagerDedicatedEventShim.cs" />
|
||||||
|
<Compile Include="NativeMethods.cs" />
|
||||||
|
<Compile Include="Initializer.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServerStatistics.cs" />
|
<Compile Include="ServerStatistics.cs" />
|
||||||
<Compile Include="TorchConfig.cs" />
|
<Compile Include="TorchConfig.cs" />
|
||||||
<Compile Include="TorchService.cs">
|
<Compile Include="TorchService.cs">
|
||||||
@@ -206,6 +216,13 @@
|
|||||||
<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="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" />
|
||||||
@@ -288,15 +305,23 @@
|
|||||||
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
|
||||||
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||||
<Name>Torch.API</Name>
|
<Name>Torch.API</Name>
|
||||||
<Private>True</Private>
|
<Private>False</Private>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Torch\Torch.csproj">
|
<ProjectReference Include="..\Torch\Torch.csproj">
|
||||||
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
<Project>{7e01635c-3b67-472e-bcd6-c5539564f214}</Project>
|
||||||
<Name>Torch</Name>
|
<Name>Torch</Name>
|
||||||
<Private>True</Private>
|
<Private>False</Private>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<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>
|
||||||
@@ -325,6 +350,10 @@
|
|||||||
<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>
|
||||||
@@ -337,10 +366,6 @@
|
|||||||
<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\TorchUI.xaml">
|
<Page Include="Views\TorchUI.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
@@ -356,22 +381,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Properties\AssemblyInfo.tt">
|
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
|
||||||
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup />
|
<ItemGroup />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\TransformOnBuild.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
|
<PostBuildEvent>copy "$(SolutionDir)NLog.config" "$(TargetDir)"</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
|
||||||
<Target Name="BeforeBuild">
|
|
||||||
</Target>
|
|
||||||
<Target Name="AfterBuild">
|
|
||||||
</Target>
|
|
||||||
-->
|
|
||||||
</Project>
|
</Project>
|
@@ -1,41 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Torch.Server
|
|
||||||
{
|
|
||||||
public class TorchCli : CommandLine
|
|
||||||
{
|
|
||||||
public TorchConfig Config { get; set; }
|
|
||||||
|
|
||||||
[Arg("instancepath", "Server data folder where saves and mods are stored.")]
|
|
||||||
public string InstancePath { get => Config.InstancePath; set => Config.InstancePath = value; }
|
|
||||||
|
|
||||||
[Arg("noupdate", "Disable automatically downloading game and plugin updates.")]
|
|
||||||
public bool NoUpdate { get => !Config.AutomaticUpdates; set => Config.AutomaticUpdates = !value; }
|
|
||||||
|
|
||||||
[Arg("update", "Manually check for and install updates.")]
|
|
||||||
public bool Update { get; set; }
|
|
||||||
|
|
||||||
//TODO: backend code for this
|
|
||||||
//[Arg("worldpath", "Path to the game world folder to load.")]
|
|
||||||
public string WorldPath { get; set; }
|
|
||||||
|
|
||||||
[Arg("autostart", "Start the server immediately.")]
|
|
||||||
public bool Autostart { get; set; }
|
|
||||||
|
|
||||||
[Arg("restartoncrash", "Automatically restart the server if it crashes.")]
|
|
||||||
public bool RestartOnCrash { get => Config.RestartOnCrash; set => Config.RestartOnCrash = value; }
|
|
||||||
|
|
||||||
[Arg("nogui", "Do not show the Torch UI.")]
|
|
||||||
public bool NoGui { get; set; }
|
|
||||||
|
|
||||||
[Arg("silent", "Do not show the Torch UI or the command line.")]
|
|
||||||
public bool Silent { get; set; }
|
|
||||||
|
|
||||||
[Arg("waitforpid", "Makes Torch wait for another process to exit.")]
|
|
||||||
public string WaitForPID { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,56 +3,87 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
{
|
{
|
||||||
public class TorchConfig : ITorchConfig
|
// TODO: redesign this gerbage
|
||||||
|
public class TorchConfig : CommandLine, ITorchConfig
|
||||||
{
|
{
|
||||||
private static Logger _log = LogManager.GetLogger("Config");
|
private static Logger _log = LogManager.GetLogger("Config");
|
||||||
|
|
||||||
public string InstancePath { get; set; }
|
public bool ShouldUpdatePlugins => (GetPluginUpdates && !NoUpdate) || ForceUpdate;
|
||||||
|
public bool ShouldUpdateTorch => (GetTorchUpdates && !NoUpdate) || ForceUpdate;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[Arg("instancename", "The name of the Torch instance.")]
|
||||||
public string InstanceName { get; set; }
|
public string InstanceName { get; set; }
|
||||||
#warning World Path not implemented
|
|
||||||
public string WorldPath { get; set; }
|
/// <inheritdoc />
|
||||||
public bool AutomaticUpdates { get; set; } = true;
|
[Arg("instancepath", "Server data folder where saves and mods are stored.")]
|
||||||
public bool RedownloadPlugins { get; set; }
|
public string InstancePath { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[XmlIgnore, Arg("noupdate", "Disable automatically downloading game and plugin updates.")]
|
||||||
|
public bool NoUpdate { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[XmlIgnore, Arg("forceupdate", "Manually check for and install updates.")]
|
||||||
|
public bool ForceUpdate { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[Arg("autostart", "Start the server immediately.")]
|
||||||
|
public bool Autostart { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[Arg("restartoncrash", "Automatically restart the server if it crashes.")]
|
||||||
public bool RestartOnCrash { get; set; }
|
public bool RestartOnCrash { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// How long in seconds to wait before automatically resetting a frozen server.
|
/// <inheritdoc />
|
||||||
/// </summary>
|
[Arg("nogui", "Do not show the Torch UI.")]
|
||||||
|
public bool NoGui { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[XmlIgnore, Arg("waitforpid", "Makes Torch wait for another process to exit.")]
|
||||||
|
public string WaitForPID { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool GetTorchUpdates { get; set; } = true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool GetPluginUpdates { get; set; } = true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public int TickTimeout { get; set; } = 60;
|
public int TickTimeout { get; set; } = 60;
|
||||||
/// <summary>
|
|
||||||
/// A list of plugins to install or update. TODO
|
/// <inheritdoc />
|
||||||
/// </summary>
|
|
||||||
public List<string> Plugins { get; set; } = new List<string>();
|
public List<string> Plugins { get; set; } = new List<string>();
|
||||||
|
|
||||||
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();
|
||||||
[NonSerialized]
|
[XmlIgnore]
|
||||||
private string _path;
|
private string _path;
|
||||||
|
|
||||||
public TorchConfig() : this("Torch") { }
|
public TorchConfig() : this("Torch") { }
|
||||||
|
|
||||||
public TorchConfig(string instanceName = "Torch", string instancePath = null, int autosaveInterval = 5, bool autoRestart = false)
|
public TorchConfig(string instanceName = "Torch", string instancePath = null)
|
||||||
{
|
{
|
||||||
InstanceName = instanceName;
|
InstanceName = instanceName;
|
||||||
InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SpaceEngineersDedicated");
|
InstancePath = instancePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SpaceEngineersDedicated");
|
||||||
//Autosave = autosaveInterval;
|
|
||||||
//AutoRestart = autoRestart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TorchConfig LoadFrom(string path)
|
public static TorchConfig LoadFrom(string path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var serializer = new XmlSerializer(typeof(TorchConfig));
|
var ser = new XmlSerializer(typeof(TorchConfig));
|
||||||
TorchConfig config;
|
|
||||||
using (var f = File.OpenRead(path))
|
using (var f = File.OpenRead(path))
|
||||||
{
|
{
|
||||||
config = (TorchConfig)serializer.Deserialize(f);
|
var config = (TorchConfig)ser.Deserialize(f);
|
||||||
|
config._path = path;
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
config._path = path;
|
|
||||||
return config;
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -70,11 +101,9 @@ namespace Torch.Server
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var serializer = new XmlSerializer(typeof(TorchConfig));
|
var ser = new XmlSerializer(typeof(TorchConfig));
|
||||||
using (var f = File.Create(path))
|
using (var f = File.Create(path))
|
||||||
{
|
ser.Serialize(f, this);
|
||||||
serializer.Serialize(f, this);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@@ -3,28 +3,39 @@ using Sandbox.Engine.Utils;
|
|||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Xml.Serialization.GeneratedAssembly;
|
using Microsoft.Xml.Serialization.GeneratedAssembly;
|
||||||
|
using NLog;
|
||||||
using Sandbox.Engine.Analytics;
|
using Sandbox.Engine.Analytics;
|
||||||
using Sandbox.Game.Multiplayer;
|
using Sandbox.Game.Multiplayer;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SteamSDK;
|
using SteamSDK;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.API.Session;
|
||||||
|
using Torch.Managers;
|
||||||
|
using Torch.Server.Managers;
|
||||||
|
using Torch.Utils;
|
||||||
using VRage.Dedicated;
|
using VRage.Dedicated;
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
using VRage.Game.ObjectBuilder;
|
using VRage.Game.ObjectBuilder;
|
||||||
using VRage.Game.SessionComponents;
|
using VRage.Game.SessionComponents;
|
||||||
using VRage.Library;
|
using VRage.Library;
|
||||||
using VRage.ObjectBuilders;
|
using VRage.ObjectBuilders;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
|
|
||||||
#pragma warning disable 618
|
#pragma warning disable 618
|
||||||
|
|
||||||
namespace Torch.Server
|
namespace Torch.Server
|
||||||
@@ -32,139 +43,167 @@ namespace Torch.Server
|
|||||||
public class TorchServer : TorchBase, ITorchServer
|
public class TorchServer : TorchBase, ITorchServer
|
||||||
{
|
{
|
||||||
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
//public MyConfigDedicated<MyObjectBuilder_SessionSettings> DedicatedConfig { get; set; }
|
||||||
public float SimulationRatio { get => _simRatio; set { _simRatio = value; OnPropertyChanged(); } }
|
/// <inheritdoc />
|
||||||
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } }
|
public float SimulationRatio
|
||||||
|
{
|
||||||
|
get => _simRatio;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_simRatio = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public TimeSpan ElapsedPlayTime
|
||||||
|
{
|
||||||
|
get => _elapsedPlayTime;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_elapsedPlayTime = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public Thread GameThread { get; private set; }
|
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(); } }
|
/// <inheritdoc />
|
||||||
|
public ServerState State
|
||||||
|
{
|
||||||
|
get => _state;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_state = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsRunning
|
||||||
|
{
|
||||||
|
get => _isRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isRunning = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public InstanceManager DedicatedInstance { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string InstanceName => Config?.InstanceName;
|
public string InstanceName => Config?.InstanceName;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string InstancePath => Config?.InstancePath;
|
public string InstancePath => Config?.InstancePath;
|
||||||
|
|
||||||
private bool _isRunning;
|
private bool _isRunning;
|
||||||
private ServerState _state;
|
private ServerState _state;
|
||||||
private TimeSpan _elapsedPlayTime;
|
private TimeSpan _elapsedPlayTime;
|
||||||
private float _simRatio;
|
private float _simRatio;
|
||||||
private readonly AutoResetEvent _stopHandle = new AutoResetEvent(false);
|
|
||||||
private Timer _watchdog;
|
private Timer _watchdog;
|
||||||
|
private Stopwatch _uptime;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public TorchServer(TorchConfig config = null)
|
public TorchServer(TorchConfig config = null)
|
||||||
{
|
{
|
||||||
|
DedicatedInstance = new InstanceManager(this);
|
||||||
|
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/>
|
||||||
|
protected override uint SteamAppId => 244850;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override string SteamAppName => "SpaceEngineersDedicated";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void Init()
|
public override void Init()
|
||||||
{
|
{
|
||||||
|
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
||||||
|
Sandbox.Engine.Platform.Game.IsDedicated = true;
|
||||||
|
|
||||||
base.Init();
|
base.Init();
|
||||||
|
|
||||||
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
|
||||||
|
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
||||||
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;
|
|
||||||
|
|
||||||
MyObjectBuilderSerializer.RegisterFromAssembly(typeof(MyObjectBuilder_CheckpointSerializer).Assembly);
|
|
||||||
|
|
||||||
InvokeBeforeRun();
|
|
||||||
|
|
||||||
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
|
|
||||||
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
|
|
||||||
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
|
|
||||||
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
|
|
||||||
MyPlugins.Load();
|
|
||||||
MyGlobalTypeMetadata.Static.Init();
|
|
||||||
RuntimeHelpers.RunClassConstructor(typeof(MyObjectBuilder_Base).TypeHandle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InvokeBeforeRun()
|
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
|
||||||
{
|
{
|
||||||
|
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded)
|
||||||
var contentPath = "Content";
|
|
||||||
|
|
||||||
var privateContentPath = typeof(MyFileSystem).GetField("m_contentPath", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string;
|
|
||||||
if (privateContentPath != null)
|
|
||||||
Log.Debug("MyFileSystem already initialized");
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (Program.IsManualInstall)
|
_watchdog?.Dispose();
|
||||||
{
|
_watchdog = null;
|
||||||
var rootPath = new FileInfo(MyFileSystem.ExePath).Directory.FullName;
|
|
||||||
contentPath = Path.Combine(rootPath, "Content");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MyFileSystem.ExePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DedicatedServer64");
|
|
||||||
}
|
|
||||||
|
|
||||||
MyFileSystem.Init(contentPath, InstancePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Start server on the current thread.
|
|
||||||
/// </summary>
|
|
||||||
public override void Start()
|
public override void Start()
|
||||||
{
|
{
|
||||||
if (State != ServerState.Stopped)
|
if (State != ServerState.Stopped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
GameThread = Thread.CurrentThread;
|
|
||||||
Config.Save();
|
|
||||||
State = ServerState.Starting;
|
State = ServerState.Starting;
|
||||||
|
IsRunning = true;
|
||||||
Log.Info("Starting server.");
|
Log.Info("Starting server.");
|
||||||
|
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
|
||||||
|
|
||||||
var runInternal = typeof(DedicatedServer).GetMethod("RunInternal", BindingFlags.Static | BindingFlags.NonPublic);
|
DedicatedInstance.SaveConfig();
|
||||||
|
_uptime = Stopwatch.StartNew();
|
||||||
MySandboxGame.IsDedicated = true;
|
|
||||||
Environment.SetEnvironmentVariable("SteamAppId", MyPerServerSettings.AppId.ToString());
|
|
||||||
|
|
||||||
VRage.Service.ExitListenerSTA.OnExit += delegate { MySandboxGame.Static?.Exit(); };
|
|
||||||
|
|
||||||
base.Start();
|
base.Start();
|
||||||
runInternal.Invoke(null, null);
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Stop()
|
||||||
|
{
|
||||||
|
if (State == ServerState.Stopped)
|
||||||
|
Log.Error("Server is already stopped");
|
||||||
|
Log.Info("Stopping server.");
|
||||||
|
base.Stop();
|
||||||
|
Log.Info("Server stopped.");
|
||||||
|
|
||||||
MySandboxGame.Log.Close();
|
|
||||||
State = ServerState.Stopped;
|
State = ServerState.Stopped;
|
||||||
|
IsRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restart the program.
|
||||||
|
/// </summary>
|
||||||
|
public override void Restart()
|
||||||
|
{
|
||||||
|
Save(0).Wait();
|
||||||
|
Stop();
|
||||||
|
LogManager.Flush();
|
||||||
|
|
||||||
|
var exe = Assembly.GetExecutingAssembly().Location;
|
||||||
|
((TorchConfig)Config).WaitForPID = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
Config.Autostart = true;
|
||||||
|
Process.Start(exe, Config.ToString());
|
||||||
|
|
||||||
|
Process.GetCurrentProcess().Kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Init(object gameInstance)
|
public override void Init(object gameInstance)
|
||||||
{
|
{
|
||||||
base.Init(gameInstance);
|
base.Init(gameInstance);
|
||||||
State = ServerState.Running;
|
var game = gameInstance as MySandboxGame;
|
||||||
SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
if (game != null && MySession.Static != null)
|
||||||
|
{
|
||||||
|
State = ServerState.Running;
|
||||||
|
// SteamServerAPI.Instance.GameServer.SetKeyValue("SM", "Torch");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State = ServerState.Stopped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -172,54 +211,131 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
SimulationRatio = Sync.ServerSimulationRatio;
|
SimulationRatio = Sync.ServerSimulationRatio;
|
||||||
ElapsedPlayTime = MySession.Static?.ElapsedPlayTime ?? default(TimeSpan);
|
var elapsed = TimeSpan.FromSeconds(Math.Floor(_uptime.Elapsed.TotalSeconds));
|
||||||
|
ElapsedPlayTime = elapsed;
|
||||||
|
|
||||||
if (_watchdog == null && Instance.Config.TickTimeout > 0)
|
if (_watchdog == null && Config.TickTimeout > 0)
|
||||||
{
|
{
|
||||||
Log.Info("Starting server watchdog.");
|
Log.Info("Starting server watchdog.");
|
||||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(Instance.Config.TickTimeout));
|
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero,
|
||||||
|
TimeSpan.FromSeconds(Config.TickTimeout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Freeze Detection
|
||||||
|
|
||||||
private static void CheckServerResponding(object state)
|
private static void CheckServerResponding(object state)
|
||||||
{
|
{
|
||||||
var mre = new ManualResetEvent(false);
|
var mre = new ManualResetEvent(false);
|
||||||
((TorchServer)state).Invoke(() => mre.Set());
|
((TorchServer) state).Invoke(() => mre.Set());
|
||||||
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
||||||
{
|
{
|
||||||
var mainThread = MySandboxGame.Static.UpdateThread;
|
#if DEBUG
|
||||||
mainThread.Suspend();
|
Log.Error(
|
||||||
var stackTrace = new StackTrace(mainThread, true);
|
$"Server watchdog detected that the server was frozen for at least {((TorchServer) state).Config.TickTimeout} seconds.");
|
||||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {Instance.Config.TickTimeout} seconds.\n{stackTrace}");
|
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
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Debug("Server watchdog responded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task Save(long callerId)
|
||||||
|
{
|
||||||
|
return SaveGameAsync(statusCode => SaveCompleted(statusCode, callerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop the server.
|
/// Callback for when save has finished.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Stop()
|
/// <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)
|
||||||
{
|
{
|
||||||
if (State == ServerState.Stopped)
|
string response = null;
|
||||||
Log.Error("Server is already stopped");
|
switch (statusCode)
|
||||||
|
|
||||||
if (Thread.CurrentThread != MySandboxGame.Static.UpdateThread)
|
|
||||||
{
|
{
|
||||||
Log.Debug("Invoking server stop on game thread.");
|
case SaveGameStatus.Success:
|
||||||
Invoke(Stop);
|
Log.Info("Save completed.");
|
||||||
return;
|
response = "Saved game.";
|
||||||
|
break;
|
||||||
|
case SaveGameStatus.SaveInProgress:
|
||||||
|
Log.Error("Save failed, a save is already in progress.");
|
||||||
|
response = "Save failed, a save is already in progress.";
|
||||||
|
break;
|
||||||
|
case SaveGameStatus.GameNotReady:
|
||||||
|
Log.Error("Save failed, game was not ready.");
|
||||||
|
response = "Save failed, game was not ready.";
|
||||||
|
break;
|
||||||
|
case SaveGameStatus.TimedOut:
|
||||||
|
Log.Error("Save failed, save timed out.");
|
||||||
|
response = "Save failed, save timed out.";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (MySession.Static.Players.TryGetPlayerId(callerId, out MyPlayer.PlayerId result))
|
||||||
|
{
|
||||||
|
Managers.GetManager<IChatManagerServer>()?.SendMessageAsOther("Server", response,
|
||||||
|
statusCode == SaveGameStatus.Success ? MyFontEnum.Green : MyFontEnum.Red, result.SteamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info("Stopping server.");
|
|
||||||
|
|
||||||
//Unload all the static junk.
|
|
||||||
//TODO: Finish unloading all server data so it's in a completely clean state.
|
|
||||||
MySandboxGame.Static.Exit();
|
|
||||||
|
|
||||||
Log.Info("Server stopped.");
|
|
||||||
_stopHandle.Set();
|
|
||||||
State = ServerState.Stopped;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,13 +14,15 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
public const string Name = "Torch (SEDS)";
|
public const string Name = "Torch (SEDS)";
|
||||||
private TorchServer _server;
|
private TorchServer _server;
|
||||||
private static Logger _log = LogManager.GetLogger("Torch");
|
private Initializer _initializer;
|
||||||
|
|
||||||
public TorchService()
|
public TorchService()
|
||||||
{
|
{
|
||||||
ServiceName = Name;
|
var workingDir = new FileInfo(typeof(TorchService).Assembly.Location).Directory.ToString();
|
||||||
|
Directory.SetCurrentDirectory(workingDir);
|
||||||
|
_initializer = new Initializer(workingDir);
|
||||||
|
|
||||||
CanHandlePowerEvent = true;
|
ServiceName = Name;
|
||||||
CanHandleSessionChangeEvent = false;
|
CanHandleSessionChangeEvent = false;
|
||||||
CanPauseAndContinue = false;
|
CanPauseAndContinue = false;
|
||||||
CanStop = true;
|
CanStop = true;
|
||||||
@@ -31,17 +33,8 @@ namespace Torch.Server
|
|||||||
{
|
{
|
||||||
base.OnStart(args);
|
base.OnStart(args);
|
||||||
|
|
||||||
string configName = args.Length > 0 ? args[0] : "TorchConfig.xml";
|
_initializer.Initialize(args);
|
||||||
var options = new TorchConfig("Torch");
|
_initializer.Run();
|
||||||
if (File.Exists(configName))
|
|
||||||
options = TorchConfig.LoadFrom(configName);
|
|
||||||
else
|
|
||||||
options.Save(configName);
|
|
||||||
|
|
||||||
_server = new TorchServer(options);
|
|
||||||
_server.Init();
|
|
||||||
_server.RunArgs = args;
|
|
||||||
Task.Run(() => _server.Start());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -50,17 +43,5 @@ namespace Torch.Server
|
|||||||
_server.Stop();
|
_server.Stop();
|
||||||
base.OnStop();
|
base.OnStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnShutdown()
|
|
||||||
{
|
|
||||||
base.OnShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
|
|
||||||
{
|
|
||||||
return base.OnPowerEvent(powerStatus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ 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 VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ namespace Torch.Server.ViewModels
|
|||||||
{
|
{
|
||||||
private static readonly Logger Log = LogManager.GetLogger("Config");
|
private static readonly Logger Log = LogManager.GetLogger("Config");
|
||||||
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
private MyConfigDedicated<MyObjectBuilder_SessionSettings> _config;
|
||||||
|
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Model => _config;
|
||||||
|
|
||||||
public ConfigDedicatedViewModel() : this(new MyConfigDedicated<MyObjectBuilder_SessionSettings>(""))
|
public ConfigDedicatedViewModel() : this(new MyConfigDedicated<MyObjectBuilder_SessionSettings>(""))
|
||||||
{
|
{
|
||||||
@@ -24,6 +26,7 @@ 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);
|
Administrators = string.Join(Environment.NewLine, _config.Administrators);
|
||||||
Banned = string.Join(Environment.NewLine, _config.Banned);
|
Banned = string.Join(Environment.NewLine, _config.Banned);
|
||||||
@@ -51,12 +54,15 @@ namespace Torch.Server.ViewModels
|
|||||||
Log.Warn($"'{mod}' is not a valid mod ID.");
|
Log.Warn($"'{mod}' is not a valid mod ID.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Never ever
|
||||||
|
_config.IgnoreLastSession = true;
|
||||||
_config.Save(path);
|
_config.Save(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionSettingsViewModel SessionSettings { get; }
|
private SessionSettingsViewModel _sessionSettings;
|
||||||
|
public SessionSettingsViewModel SessionSettings { get => _sessionSettings; set { _sessionSettings = value; OnPropertyChanged(); } }
|
||||||
|
|
||||||
public ObservableCollection<string> WorldPaths { get; } = new ObservableCollection<string>();
|
public MtObservableList<string> WorldPaths { get; } = new MtObservableList<string>();
|
||||||
private string _administrators;
|
private string _administrators;
|
||||||
public string Administrators { get => _administrators; set { _administrators = value; OnPropertyChanged(); } }
|
public string Administrators { get => _administrators; set { _administrators = value; OnPropertyChanged(); } }
|
||||||
private string _banned;
|
private string _banned;
|
||||||
@@ -76,12 +82,6 @@ namespace Torch.Server.ViewModels
|
|||||||
set { _config.GroupID = value; OnPropertyChanged(); }
|
set { _config.GroupID = value; OnPropertyChanged(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IgnoreLastSession
|
|
||||||
{
|
|
||||||
get { return _config.IgnoreLastSession; }
|
|
||||||
set { _config.IgnoreLastSession = value; OnPropertyChanged(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string IP
|
public string IP
|
||||||
{
|
{
|
||||||
get { return _config.IP; }
|
get { return _config.IP; }
|
||||||
|
@@ -8,6 +8,7 @@ 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
|
||||||
@@ -15,7 +16,7 @@ namespace Torch.Server.ViewModels.Blocks
|
|||||||
public class BlockViewModel : EntityViewModel
|
public class BlockViewModel : EntityViewModel
|
||||||
{
|
{
|
||||||
public IMyTerminalBlock Block { get; }
|
public IMyTerminalBlock Block { get; }
|
||||||
public MTObservableCollection<PropertyViewModel> Properties { get; } = new MTObservableCollection<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}";
|
||||||
|
|
||||||
@@ -24,8 +25,11 @@ namespace Torch.Server.ViewModels.Blocks
|
|||||||
get => Block?.CustomName ?? "null";
|
get => Block?.CustomName ?? "null";
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TorchBase.Instance.InvokeBlocking(() => Block.CustomName = value);
|
TorchBase.Instance.Invoke(() =>
|
||||||
OnPropertyChanged();
|
{
|
||||||
|
Block.CustomName = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,13 +41,22 @@ namespace Torch.Server.ViewModels.Blocks
|
|||||||
get => ((MySlimBlock)Block.SlimBlock).BuiltBy;
|
get => ((MySlimBlock)Block.SlimBlock).BuiltBy;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TorchBase.Instance.InvokeBlocking(() => ((MySlimBlock)Block.SlimBlock).TransferAuthorship(value));
|
TorchBase.Instance.Invoke(() =>
|
||||||
OnPropertyChanged();
|
{
|
||||||
|
((MySlimBlock)Block.SlimBlock).TransferAuthorship(value);
|
||||||
|
OnPropertyChanged();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanStop => false;
|
public override bool CanStop => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Delete()
|
||||||
|
{
|
||||||
|
Block.CubeGrid.RazeBlock(Block.Position);
|
||||||
|
}
|
||||||
|
|
||||||
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
|
public BlockViewModel(IMyTerminalBlock block, EntityTreeViewModel tree) : base(block, tree)
|
||||||
{
|
{
|
||||||
Block = block;
|
Block = block;
|
||||||
|
@@ -16,17 +16,15 @@ namespace Torch.Server.ViewModels.Blocks
|
|||||||
|
|
||||||
public T Value
|
public T Value
|
||||||
{
|
{
|
||||||
get
|
get => _prop.GetValue(Block.Block);
|
||||||
{
|
|
||||||
var val = default(T);
|
|
||||||
TorchBase.Instance.InvokeBlocking(() => val = _prop.GetValue(Block.Block));
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
TorchBase.Instance.InvokeBlocking(() => _prop.SetValue(Block.Block, value));
|
TorchBase.Instance.Invoke(() =>
|
||||||
OnPropertyChanged();
|
{
|
||||||
Block.RefreshModel();
|
_prop.SetValue(Block.Block, value);
|
||||||
|
OnPropertyChanged();
|
||||||
|
Block.RefreshModel();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,8 @@
|
|||||||
using VRage.Game.ModAPI;
|
using System.Windows.Controls;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Collections;
|
||||||
|
using Torch.Server.Managers;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
using VRage.ModAPI;
|
using VRage.ModAPI;
|
||||||
using VRageMath;
|
using VRageMath;
|
||||||
|
|
||||||
@@ -7,9 +11,25 @@ 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;
|
||||||
@@ -37,9 +57,15 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
|
|
||||||
public virtual bool CanDelete => !(Entity is IMyCharacter);
|
public virtual bool CanDelete => !(Entity is IMyCharacter);
|
||||||
|
|
||||||
|
public virtual void Delete()
|
||||||
|
{
|
||||||
|
Entity.Close();
|
||||||
|
}
|
||||||
|
|
||||||
public EntityViewModel(IMyEntity entity, EntityTreeViewModel tree)
|
public EntityViewModel(IMyEntity entity, EntityTreeViewModel tree)
|
||||||
{
|
{
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
Tree = tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityViewModel()
|
public EntityViewModel()
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using System.Linq;
|
using System;
|
||||||
using NLog;
|
using System.Linq;
|
||||||
using Sandbox.Game.Entities;
|
using Sandbox.Game.Entities;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Collections;
|
||||||
using Torch.Server.ViewModels.Blocks;
|
using Torch.Server.ViewModels.Blocks;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels.Entities
|
namespace Torch.Server.ViewModels.Entities
|
||||||
@@ -9,17 +11,16 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
public class GridViewModel : EntityViewModel, ILazyLoad
|
public class GridViewModel : EntityViewModel, ILazyLoad
|
||||||
{
|
{
|
||||||
private MyCubeGrid Grid => (MyCubeGrid)Entity;
|
private MyCubeGrid Grid => (MyCubeGrid)Entity;
|
||||||
public MTObservableCollection<BlockViewModel> Blocks { get; } = new MTObservableCollection<BlockViewModel>();
|
public MtObservableList<BlockViewModel> Blocks { get; } = new MtObservableList<BlockViewModel>();
|
||||||
private static readonly Logger Log = LogManager.GetLogger(nameof(GridViewModel));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string DescriptiveName => $"{Name} ({Grid.BlocksCount} blocks)";
|
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)
|
||||||
{
|
{
|
||||||
Log.Debug($"Creating model {Grid.DisplayName}");
|
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
|
||||||
Blocks.Add(new BlockViewModel(null, Tree));
|
Blocks.Add(new BlockViewModel(null, Tree));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +29,6 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
if (obj.FatBlock != null)
|
if (obj.FatBlock != null)
|
||||||
Blocks.RemoveWhere(b => b.Block.EntityId == obj.FatBlock?.EntityId);
|
Blocks.RemoveWhere(b => b.Block.EntityId == obj.FatBlock?.EntityId);
|
||||||
|
|
||||||
Blocks.Sort(b => b.Block.GetType().AssemblyQualifiedName);
|
|
||||||
OnPropertyChanged(nameof(Name));
|
OnPropertyChanged(nameof(Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,6 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
if (block != null)
|
if (block != null)
|
||||||
Blocks.Add(new BlockViewModel(block, Tree));
|
Blocks.Add(new BlockViewModel(block, Tree));
|
||||||
|
|
||||||
Blocks.Sort(b => b.Block.GetType().AssemblyQualifiedName);
|
|
||||||
OnPropertyChanged(nameof(Name));
|
OnPropertyChanged(nameof(Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,20 +47,23 @@ namespace Torch.Server.ViewModels.Entities
|
|||||||
if (_load)
|
if (_load)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log.Debug($"Loading model {Grid.DisplayName}");
|
|
||||||
_load = true;
|
_load = true;
|
||||||
Blocks.Clear();
|
Blocks.Clear();
|
||||||
TorchBase.Instance.InvokeBlocking(() =>
|
TorchBase.Instance.Invoke(() =>
|
||||||
{
|
{
|
||||||
foreach (var block in Grid.GetFatBlocks().Where(b => b is IMyTerminalBlock))
|
foreach (var block in Grid.GetFatBlocks().Where(b => b is IMyTerminalBlock))
|
||||||
{
|
{
|
||||||
Blocks.Add(new BlockViewModel((IMyTerminalBlock)block, Tree));
|
Blocks.Add(new BlockViewModel((IMyTerminalBlock)block, Tree));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
Blocks.Sort(b => b.Block.GetType().AssemblyQualifiedName);
|
|
||||||
|
|
||||||
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 MTObservableCollection<GridViewModel> AttachedGrids { get; } = new MTObservableCollection<GridViewModel>();
|
public MtObservableList<GridViewModel> AttachedGrids { get; } = new MtObservableList<GridViewModel>();
|
||||||
|
|
||||||
public async Task UpdateAttachedGrids()
|
public async Task UpdateAttachedGrids()
|
||||||
{
|
{
|
||||||
|
@@ -3,30 +3,44 @@ 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.Controls;
|
||||||
using Sandbox.Game.Entities;
|
using Sandbox.Game.Entities;
|
||||||
using Sandbox.Game.Entities.Character;
|
using Sandbox.Game.Entities.Character;
|
||||||
using Torch.Server.ViewModels.Entities;
|
using Torch.Server.ViewModels.Entities;
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
using VRage.ModAPI;
|
using VRage.ModAPI;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using NLog;
|
||||||
|
using Torch.Collections;
|
||||||
|
|
||||||
namespace Torch.Server.ViewModels
|
namespace Torch.Server.ViewModels
|
||||||
{
|
{
|
||||||
public class EntityTreeViewModel : ViewModel
|
public class EntityTreeViewModel : ViewModel
|
||||||
{
|
{
|
||||||
public MTObservableCollection<GridViewModel> Grids { get; set; } = new MTObservableCollection<GridViewModel>();
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
public MTObservableCollection<CharacterViewModel> Characters { get; set; } = new MTObservableCollection<CharacterViewModel>();
|
|
||||||
public MTObservableCollection<EntityViewModel> FloatingObjects { get; set; } = new MTObservableCollection<EntityViewModel>();
|
//TODO: these should be sorted sets for speed
|
||||||
public MTObservableCollection<VoxelMapViewModel> VoxelMaps { get; set; } = new MTObservableCollection<VoxelMapViewModel>();
|
public MtObservableList<GridViewModel> Grids { get; set; } = new MtObservableList<GridViewModel>();
|
||||||
|
public MtObservableList<CharacterViewModel> Characters { get; set; } = new MtObservableList<CharacterViewModel>();
|
||||||
|
public MtObservableList<EntityViewModel> FloatingObjects { get; set; } = new MtObservableList<EntityViewModel>();
|
||||||
|
public MtObservableList<VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableList<VoxelMapViewModel>();
|
||||||
|
public Dispatcher ControlDispatcher => _control.Dispatcher;
|
||||||
|
|
||||||
private EntityViewModel _currentEntity;
|
private EntityViewModel _currentEntity;
|
||||||
|
private UserControl _control;
|
||||||
|
|
||||||
public EntityViewModel CurrentEntity
|
public EntityViewModel CurrentEntity
|
||||||
{
|
{
|
||||||
get => _currentEntity;
|
get => _currentEntity;
|
||||||
set { _currentEntity = value; OnPropertyChanged(); }
|
set { _currentEntity = value; OnPropertyChanged(nameof(CurrentEntity)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityTreeViewModel()
|
public EntityTreeViewModel(UserControl control)
|
||||||
|
{
|
||||||
|
_control = control;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init()
|
||||||
{
|
{
|
||||||
MyEntities.OnEntityAdd += MyEntities_OnEntityAdd;
|
MyEntities.OnEntityAdd += MyEntities_OnEntityAdd;
|
||||||
MyEntities.OnEntityRemove += MyEntities_OnEntityRemove;
|
MyEntities.OnEntityRemove += MyEntities_OnEntityRemove;
|
||||||
@@ -34,43 +48,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.RemoveWhere(m => m.Id == grid.EntityId);
|
||||||
Characters.RemoveWhere(m => m.Id == character.EntityId);
|
break;
|
||||||
break;
|
case MyCharacter character:
|
||||||
case MyFloatingObject floating:
|
Characters.RemoveWhere(m => m.Id == character.EntityId);
|
||||||
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
|
break;
|
||||||
break;
|
case MyFloatingObject floating:
|
||||||
case MyVoxelBase voxel:
|
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
|
||||||
VoxelMaps.RemoveWhere(m => m.Id == voxel.EntityId);
|
break;
|
||||||
break;
|
case MyVoxelBase voxel:
|
||||||
|
VoxelMaps.RemoveWhere(m => m.Id == 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)
|
||||||
if (Grids.All(g => g.Entity.EntityId != obj.EntityId))
|
{
|
||||||
|
case MyCubeGrid grid:
|
||||||
Grids.Add(new GridViewModel(grid, this));
|
Grids.Add(new GridViewModel(grid, this));
|
||||||
break;
|
break;
|
||||||
case MyCharacter character:
|
case MyCharacter character:
|
||||||
if (Characters.All(g => g.Entity.EntityId != obj.EntityId))
|
|
||||||
Characters.Add(new CharacterViewModel(character, this));
|
Characters.Add(new CharacterViewModel(character, this));
|
||||||
break;
|
break;
|
||||||
case MyFloatingObject floating:
|
case MyFloatingObject floating:
|
||||||
if (FloatingObjects.All(g => g.Entity.EntityId != obj.EntityId))
|
|
||||||
FloatingObjects.Add(new FloatingObjectViewModel(floating, this));
|
FloatingObjects.Add(new FloatingObjectViewModel(floating, this));
|
||||||
break;
|
break;
|
||||||
case MyVoxelBase voxel:
|
case MyVoxelBase voxel:
|
||||||
if (VoxelMaps.All(g => g.Entity.EntityId != obj.EntityId))
|
|
||||||
VoxelMaps.Add(new VoxelMapViewModel(voxel, this));
|
VoxelMaps.Add(new VoxelMapViewModel(voxel, this));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.Error(e);
|
||||||
|
// ignore error "it's only UI"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,28 +6,31 @@ 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 MTObservableCollection<PluginViewModel> Plugins { get; } = new MTObservableCollection<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() { }
|
||||||
|
|
||||||
public PluginManagerViewModel(IPluginManager pluginManager)
|
public PluginManagerViewModel(IPluginManager pluginManager)
|
||||||
{
|
{
|
||||||
|
foreach (var plugin in pluginManager)
|
||||||
|
Plugins.Add(new PluginViewModel(plugin));
|
||||||
pluginManager.PluginsLoaded += PluginManager_PluginsLoaded;
|
pluginManager.PluginsLoaded += PluginManager_PluginsLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PluginManager_PluginsLoaded(List<ITorchPlugin> obj)
|
private void PluginManager_PluginsLoaded(IReadOnlyCollection<ITorchPlugin> obj)
|
||||||
{
|
{
|
||||||
Plugins.Clear();
|
Plugins.Clear();
|
||||||
foreach (var plugin in obj)
|
foreach (var plugin in obj)
|
||||||
|
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SharpDX.Toolkit.Collections;
|
using SharpDX.Toolkit.Collections;
|
||||||
|
using Torch.Collections;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Library.Utils;
|
using VRage.Library.Utils;
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ namespace Torch.Server.ViewModels
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
|
public SessionSettingsViewModel() : this(new MyObjectBuilder_SessionSettings())
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -35,7 +36,7 @@ namespace Torch.Server.ViewModels
|
|||||||
BlockLimits.Add(new BlockLimitViewModel(this, limit.Key, limit.Value));
|
BlockLimits.Add(new BlockLimitViewModel(this, limit.Key, limit.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MTObservableCollection<BlockLimitViewModel> BlockLimits { get; } = new MTObservableCollection<BlockLimitViewModel>();
|
public MtObservableList<BlockLimitViewModel> BlockLimits { get; } = new MtObservableList<BlockLimitViewModel>();
|
||||||
|
|
||||||
#region Multipliers
|
#region Multipliers
|
||||||
|
|
||||||
@@ -74,6 +75,12 @@ namespace Torch.Server.ViewModels
|
|||||||
{
|
{
|
||||||
get => _settings.HackSpeedMultiplier; set { _settings.HackSpeedMultiplier = value; OnPropertyChanged(); }
|
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
|
#endregion
|
||||||
|
|
||||||
#region NPCs
|
#region NPCs
|
||||||
@@ -356,6 +363,19 @@ namespace Torch.Server.ViewModels
|
|||||||
get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
get => _settings.WorldSizeKm; set { _settings.WorldSizeKm = value; OnPropertyChanged(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ProceduralDensity"/>
|
||||||
|
public float ProceduralDensity
|
||||||
|
{
|
||||||
|
get => _settings.ProceduralDensity; set { _settings.ProceduralDensity = value; OnPropertyChanged(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MyObjectBuilder_SessionSettings.ProceduralSeed"/>
|
||||||
|
public int ProceduralSeed
|
||||||
|
{
|
||||||
|
get => _settings.ProceduralSeed;
|
||||||
|
set { _settings.ProceduralSeed = value; OnPropertyChanged(); }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary />
|
/// <summary />
|
||||||
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
|
public static implicit operator MyObjectBuilder_SessionSettings(SessionSettingsViewModel viewModel)
|
||||||
{
|
{
|
||||||
|
@@ -5,23 +5,21 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Torch.Server"
|
xmlns:local="clr-namespace:Torch.Server"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<DockPanel>
|
<Grid>
|
||||||
<DockPanel DockPanel.Dock="Bottom">
|
<Grid.RowDefinitions>
|
||||||
<Button x:Name="Send" Content="Send" DockPanel.Dock="Right" Width="50" Margin="5,5,5,5" Click="SendButton_Click"></Button>
|
<RowDefinition/>
|
||||||
<TextBox x:Name="Message" DockPanel.Dock="Left" Margin="5,5,5,5" KeyDown="Message_OnKeyDown"></TextBox>
|
<RowDefinition Height="Auto"/>
|
||||||
</DockPanel>
|
</Grid.RowDefinitions>
|
||||||
<ListView 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">
|
||||||
<ListView.ItemTemplate>
|
<TextBlock x:Name="ChatItems" />
|
||||||
<DataTemplate>
|
</ScrollViewer>
|
||||||
<WrapPanel>
|
<Grid Grid.Row="1">
|
||||||
<TextBlock Text="{Binding Timestamp}"/>
|
<Grid.ColumnDefinitions>
|
||||||
<TextBlock Text=" "/>
|
<ColumnDefinition/>
|
||||||
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
<TextBlock Text=": "/>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Text="{Binding Message}"/>
|
<Button Grid.Column="1" x:Name="Send" Content="Send" DockPanel.Dock="Right" Width="50" Margin="5,5,5,5" Click="SendButton_Click"></Button>
|
||||||
</WrapPanel>
|
<TextBox Grid.Column="0" x:Name="Message" DockPanel.Dock="Left" Margin="5,5,5,5" KeyDown="Message_OnKeyDown"></TextBox>
|
||||||
</DataTemplate>
|
</Grid>
|
||||||
</ListView.ItemTemplate>
|
</Grid>
|
||||||
</ListView>
|
|
||||||
</DockPanel>
|
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -19,7 +21,11 @@ using Sandbox.Engine.Multiplayer;
|
|||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using SteamSDK;
|
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,7 +35,6 @@ namespace Torch.Server
|
|||||||
public partial class ChatControl : UserControl
|
public partial class ChatControl : UserControl
|
||||||
{
|
{
|
||||||
private TorchBase _server;
|
private TorchBase _server;
|
||||||
private MultiplayerManager _multiplayer;
|
|
||||||
|
|
||||||
public ChatControl()
|
public ChatControl()
|
||||||
{
|
{
|
||||||
@@ -39,8 +44,75 @@ namespace Torch.Server
|
|||||||
public void BindServer(ITorchServer server)
|
public void BindServer(ITorchServer server)
|
||||||
{
|
{
|
||||||
_server = (TorchBase)server;
|
_server = (TorchBase)server;
|
||||||
_multiplayer = (MultiplayerManager)server.Multiplayer;
|
Dispatcher.InvokeAsync(() =>
|
||||||
DataContext = _multiplayer;
|
{
|
||||||
|
ChatItems.Inlines.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
var sessionManager = server.Managers.GetManager<ITorchSessionManager>();
|
||||||
|
if (sessionManager != null)
|
||||||
|
sessionManager.SessionStateChanged += SessionStateChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SessionStateChanged(ITorchSession session, TorchSessionState state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case TorchSessionState.Loading:
|
||||||
|
Dispatcher.InvokeAsync(() => ChatItems.Inlines.Clear());
|
||||||
|
break;
|
||||||
|
case TorchSessionState.Loaded:
|
||||||
|
{
|
||||||
|
var chatMgr = session.Managers.GetManager<IChatManagerClient>();
|
||||||
|
if (chatMgr != null)
|
||||||
|
chatMgr.MessageRecieved += OnMessageRecieved;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TorchSessionState.Unloading:
|
||||||
|
{
|
||||||
|
var chatMgr = session.Managers.GetManager<IChatManagerClient>();
|
||||||
|
if (chatMgr != null)
|
||||||
|
chatMgr.MessageRecieved -= OnMessageRecieved;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TorchSessionState.Unloaded:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMessageRecieved(TorchChatMessage msg, ref bool consumed)
|
||||||
|
{
|
||||||
|
InsertMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, Brush> _brushes = new Dictionary<string, Brush>();
|
||||||
|
private static Brush LookupBrush(string font)
|
||||||
|
{
|
||||||
|
if (_brushes.TryGetValue(font, out Brush result))
|
||||||
|
return result;
|
||||||
|
Brush brush = typeof(Brushes).GetField(font, BindingFlags.Static)?.GetValue(null) as Brush ?? Brushes.Blue;
|
||||||
|
_brushes.Add(font, brush);
|
||||||
|
return brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InsertMessage(TorchChatMessage msg)
|
||||||
|
{
|
||||||
|
if (Dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
bool atBottom = ChatScroller.VerticalOffset + 8 > ChatScroller.ScrollableHeight;
|
||||||
|
var span = new Span();
|
||||||
|
span.Inlines.Add($"{msg.Timestamp} ");
|
||||||
|
span.Inlines.Add(new Run(msg.Author) { Foreground = LookupBrush(msg.Font) });
|
||||||
|
span.Inlines.Add($": {msg.Message}");
|
||||||
|
span.Inlines.Add(new LineBreak());
|
||||||
|
ChatItems.Inlines.Add(span);
|
||||||
|
if (atBottom)
|
||||||
|
ChatScroller.ScrollToBottom();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Dispatcher.InvokeAsync(() => InsertMessage(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendButton_Click(object sender, RoutedEventArgs e)
|
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -61,22 +133,21 @@ 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>();
|
||||||
string response = null;
|
if (commands != null && commands.IsCommand(text))
|
||||||
if (commands.IsCommand(text))
|
|
||||||
{
|
{
|
||||||
_multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", text));
|
InsertMessage(new TorchChatMessage("Server", text, MyFontEnum.DarkBlue));
|
||||||
_server.InvokeBlocking(() =>
|
_server.Invoke(() =>
|
||||||
{
|
{
|
||||||
response = commands.HandleCommandFromServer(text);
|
string response = commands.HandleCommandFromServer(text);
|
||||||
|
if (!string.IsNullOrWhiteSpace(response))
|
||||||
|
InsertMessage(new TorchChatMessage("Server", response, MyFontEnum.Blue));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_server.Multiplayer.SendMessage(text);
|
_server.CurrentSession?.Managers.GetManager<IChatManagerClient>().SendMessageAsSelf(text);
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(response))
|
|
||||||
_multiplayer.ChatHistory.Add(new ChatMessage(DateTime.Now, 0, "Server", response));
|
|
||||||
Message.Text = "";
|
Message.Text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,15 +10,27 @@
|
|||||||
<UserControl.DataContext>
|
<UserControl.DataContext>
|
||||||
<viewModels:ConfigDedicatedViewModel />
|
<viewModels:ConfigDedicatedViewModel />
|
||||||
</UserControl.DataContext>
|
</UserControl.DataContext>
|
||||||
<DockPanel>
|
<Grid>
|
||||||
<DockPanel DockPanel.Dock="Top">
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<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="New World" Margin="3" DockPanel.Dock="Right" Click="NewWorld_OnClick"/>
|
||||||
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" SelectionChanged="Selector_OnSelectionChanged"/>
|
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" SelectionChanged="Selector_OnSelectionChanged"/>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<DockPanel DockPanel.Dock="Bottom">
|
<Grid Grid.Row="1">
|
||||||
<StackPanel DockPanel.Dock="Left">
|
<Grid.ColumnDefinitions>
|
||||||
<ScrollViewer IsEnabled="True">
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid Grid.Column="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ScrollViewer Grid.Row="0" Margin="3">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<StackPanel Margin="3" DockPanel.Dock="Left">
|
<StackPanel Margin="3" DockPanel.Dock="Left">
|
||||||
<Label Content="Server Name" />
|
<Label Content="Server Name" />
|
||||||
@@ -33,28 +45,9 @@
|
|||||||
<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" />
|
|
||||||
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
<CheckBox IsChecked="{Binding PauseGameWhenEmpty}" Content="Pause When Empty" Margin="3" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="3">
|
<StackPanel Margin="3">
|
||||||
<!--
|
|
||||||
<ListBox ItemsSource="{Binding Banned}" Width="130" Margin="3,0,3,3" Height="100"
|
|
||||||
MouseDoubleClick="Banned_OnMouseDoubleClick" /> -->
|
|
||||||
<!--
|
|
||||||
<ListBox ItemsSource="{Binding Administrators}" Width="130" Margin="3,0,3,3" Height="100"
|
|
||||||
MouseDoubleClick="Administrators_OnMouseDoubleClick">
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<TextBox Text="{Binding SteamId}" Width="100"/>
|
|
||||||
<Label Content="{Binding Name}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListBox.ItemTemplate>
|
|
||||||
</ListBox> -->
|
|
||||||
<!--
|
|
||||||
<ListBox ItemsSource="{Binding Mods}" Width="130" Margin="3,0,3,3" Height="100"
|
|
||||||
MouseDoubleClick="Mods_OnMouseDoubleClick" /> -->
|
|
||||||
<Label Content="Mods" />
|
<Label Content="Mods" />
|
||||||
<TextBox Text="{Binding Mods}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
<TextBox Text="{Binding Mods}" Margin="3" Height="100" AcceptsReturn="true" VerticalScrollBarVisibility="Auto"/>
|
||||||
<Label Content="Administrators" />
|
<Label Content="Administrators" />
|
||||||
@@ -64,23 +57,23 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<Button Content="Save Config" Margin="3" Click="Save_OnClick" />
|
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
|
||||||
</StackPanel>
|
</Grid>
|
||||||
<ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True">
|
<ScrollViewer Grid.Column="1" Margin="3">
|
||||||
<StackPanel DataContext="{Binding SessionSettings}">
|
<StackPanel DataContext="{Binding SessionSettings}">
|
||||||
<Expander Header="Block Limits">
|
<Expander Header="Block Limits">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxBlocksPerPlayer}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Blocks Per Player" />
|
<Label Content="Max Blocks Per Player" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxGridSize}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Grid Size" />
|
<Label Content="Max Grid Size" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
|
<Button Content="Add" Margin="3" Click="AddLimit_OnClick" />
|
||||||
<ListView ItemsSource="{Binding BlockLimits}" Margin="3">
|
<ListView ItemsSource="{Binding BlockLimits}" Margin="3">
|
||||||
<ListBox.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding BlockType}" Width="150" Margin="3" />
|
<TextBox Text="{Binding BlockType}" Width="150" Margin="3" />
|
||||||
@@ -88,36 +81,40 @@
|
|||||||
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
|
<Button Content=" X " Margin="3" Click="RemoveLimit_OnClick" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListView.ItemTemplate>
|
||||||
</ListView>
|
</ListView>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="Multipliers">
|
<Expander Header="Multipliers">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding InventorySizeMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding InventorySizeMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Inventory Size" />
|
<Label Content="Inventory Size" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding RefinerySpeedMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding RefinerySpeedMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Refinery Speed" />
|
<Label Content="Refinery Speed" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding AssemblerEfficiencyMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding AssemblerEfficiencyMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Assembler Efficiency" />
|
<Label Content="Assembler Efficiency" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding AssemblerSpeedMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding AssemblerSpeedMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Assembler Speed" />
|
<Label Content="Assembler Speed" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<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" />
|
<TextBox Text="{Binding GrinderSpeedMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Grinder Speed" />
|
<Label Content="Grinder Speed" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding HackSpeedMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding HackSpeedMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Hacking Speed" />
|
<Label Content="Hacking Speed" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="NPCs">
|
<Expander Header="NPCs">
|
||||||
@@ -131,14 +128,14 @@
|
|||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="Environment">
|
<Expander Header="Environment">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
<DockPanel ToolTip="Increases physics precision at the cost of performance.">
|
<StackPanel Orientation="Horizontal" ToolTip="Increases physics precision at the cost of performance.">
|
||||||
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
|
<TextBox Text="{Binding PhysicsIterations}" Margin="3" Width="70" />
|
||||||
<Label Content="Physics Iterations" />
|
<Label Content="Physics Iterations" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxFloatingObjects}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Floating Objects" />
|
<Label Content="Max Floating Objects" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
|
<CheckBox IsChecked="{Binding EnableRealisticSound}" Content="Enable Realistic Sound"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableAirtightness}" Content="Enable Airtightness" Margin="3" />
|
||||||
@@ -149,41 +146,49 @@
|
|||||||
<CheckBox IsChecked="{Binding EnableVoxelDestruction}" Content="Enable Voxel Destruction"
|
<CheckBox IsChecked="{Binding EnableVoxelDestruction}" Content="Enable Voxel Destruction"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableSunRotation}" Content="Enable Sun Rotation" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableSunRotation}" Content="Enable Sun Rotation" Margin="3" />
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding SunRotationInterval}" Margin="3" Width="70" />
|
<TextBox Text="{Binding SunRotationInterval}" Margin="3" Width="70" />
|
||||||
<Label Content="Sun Rotation Interval (mins)" />
|
<Label Content="Sun Rotation Interval (mins)" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableFlora}" Content="Enable Flora" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableFlora}" Content="Enable Flora" Margin="3" />
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding FloraDensity}" Margin="3" Width="70" />
|
<TextBox Text="{Binding FloraDensity}" Margin="3" Width="70" />
|
||||||
<Label Content="Flora Density" />
|
<Label Content="Flora Density" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding FloraDensityMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding FloraDensityMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Flora Density Multiplier" />
|
<Label Content="Flora Density Multiplier" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding ViewDistance}" Margin="3" Width="70" />
|
<TextBox Text="{Binding ViewDistance}" Margin="3" Width="70" />
|
||||||
<Label Content="View Distance (meters)" />
|
<Label Content="View Distance (meters)" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding WorldSize}" Margin="3" Width="70" />
|
<TextBox Text="{Binding WorldSize}" Margin="3" Width="70" />
|
||||||
<Label Content="World Size (km)" />
|
<Label Content="World Size (km)" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<ComboBox SelectedItem="{Binding EnvironmentHostility}"
|
<ComboBox SelectedItem="{Binding EnvironmentHostility}"
|
||||||
ItemsSource="{Binding EnvironmentHostilityValues}" Margin="3" Width="100"
|
ItemsSource="{Binding EnvironmentHostilityValues}" Margin="3" Width="100"
|
||||||
DockPanel.Dock="Left" />
|
DockPanel.Dock="Left" />
|
||||||
<Label Content="Environment Hostility" />
|
<Label Content="Environment Hostility" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBox Text="{Binding ProceduralDensity}" Margin="3" Width="70" />
|
||||||
|
<Label Content="Procedural Density" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBox Text="{Binding ProceduralSeed}" Margin="3" Width="70" />
|
||||||
|
<Label Content="Procedural Seed" />
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="Players">
|
<Expander Header="Players">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxPlayers}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Players" />
|
<Label Content="Max Players" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
|
<CheckBox IsChecked="{Binding EnableThirdPerson}" Content="Enable 3rd Person Camera"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableJetpack}" Content="Enable Jetpack" Margin="3" />
|
||||||
@@ -191,20 +196,20 @@
|
|||||||
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableCopyPaste}" Content="Enable Copy/Paste" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
|
<CheckBox IsChecked="{Binding ShowPlayerNamesOnHud}" Content="Show Player Names on HUD"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
|
<TextBox Text="{Binding SpawnTimeMultiplier}" Margin="3" Width="70" />
|
||||||
<Label Content="Respawn Time Multiplier" />
|
<Label Content="Respawn Time Multiplier" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
<CheckBox IsChecked="{Binding ResetOwnership}" Content="Reset Ownership" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
<CheckBox IsChecked="{Binding SpawnWithTools}" Content="Spawn With Tools" Margin="3" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
<Expander Header="Miscellaneous">
|
<Expander Header="Miscellaneous">
|
||||||
<StackPanel Margin="10,0,0,0">
|
<StackPanel Margin="10,0,0,0">
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
|
<TextBox Text="{Binding AutosaveInterval}" Margin="3" Width="70" />
|
||||||
<Label Content="Autosave Interval (minutes)" />
|
<Label Content="Autosave Interval (minutes)" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
|
<CheckBox IsChecked="{Binding EnableConvertToStation}" Content="Enable Convert to Station"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
|
|
||||||
@@ -223,19 +228,19 @@
|
|||||||
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
|
<CheckBox IsChecked="{Binding EnableWeapons}" Content="Enable Weapons" Margin="3" />
|
||||||
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
|
<CheckBox IsChecked="{Binding EnableIngameScripts}" Content="Enable Ingame Scripts"
|
||||||
Margin="3" />
|
Margin="3" />
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
|
<ComboBox SelectedItem="{Binding GameMode}" ItemsSource="{Binding GameModeValues}"
|
||||||
Margin="3" Width="100" DockPanel.Dock="Left" />
|
Margin="3" Width="100" DockPanel.Dock="Left" />
|
||||||
<Label Content="Game Mode" />
|
<Label Content="Game Mode" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
<DockPanel>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
|
<TextBox Text="{Binding MaxBackupSaves}" Margin="3" Width="70" />
|
||||||
<Label Content="Max Backup Saves" />
|
<Label Content="Max Backup Saves" />
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</DockPanel>
|
</Grid>
|
||||||
</DockPanel>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -1,33 +1,8 @@
|
|||||||
using System;
|
using System.Windows;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Data;
|
using Torch.API.Managers;
|
||||||
using System.Windows.Documents;
|
using Torch.Server.Managers;
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
using System.Windows.Navigation;
|
|
||||||
using System.Windows.Shapes;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using NLog;
|
|
||||||
using Sandbox;
|
|
||||||
using Sandbox.Engine.Networking;
|
|
||||||
using Sandbox.Engine.Utils;
|
|
||||||
using Torch.Server.ViewModels;
|
using Torch.Server.ViewModels;
|
||||||
using Torch.Views;
|
|
||||||
using VRage;
|
|
||||||
using VRage.Dedicated;
|
|
||||||
using VRage.Game;
|
|
||||||
using VRage.ObjectBuilders;
|
|
||||||
using Path = System.IO.Path;
|
|
||||||
|
|
||||||
namespace Torch.Server.Views
|
namespace Torch.Server.Views
|
||||||
{
|
{
|
||||||
@@ -36,110 +11,29 @@ namespace Torch.Server.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ConfigControl : UserControl
|
public partial class ConfigControl : UserControl
|
||||||
{
|
{
|
||||||
private readonly Logger Log = LogManager.GetLogger("Config");
|
private InstanceManager _instanceManager;
|
||||||
public MyConfigDedicated<MyObjectBuilder_SessionSettings> Config { get; set; }
|
|
||||||
private ConfigDedicatedViewModel _viewModel;
|
|
||||||
private string _configPath;
|
|
||||||
private TorchConfig _torchConfig;
|
|
||||||
|
|
||||||
public ConfigControl()
|
public ConfigControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
_instanceManager = TorchBase.Instance.Managers.GetManager<InstanceManager>();
|
||||||
|
DataContext = _instanceManager.DedicatedConfig;
|
||||||
public void SaveConfig()
|
|
||||||
{
|
|
||||||
_viewModel.Save(_configPath);
|
|
||||||
Log.Info("Saved DS config.");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//var checkpoint = MyLocalCache.LoadCheckpoint(Config.LoadWorld, out _);
|
|
||||||
MyObjectBuilderSerializer.DeserializeXML(Path.Combine(Config.LoadWorld, "Sandbox.sbc"), out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
|
||||||
if (checkpoint == null)
|
|
||||||
{
|
|
||||||
Log.Error($"Failed to load {Config.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
checkpoint.Settings = Config.SessionSettings;
|
|
||||||
checkpoint.Mods.Clear();
|
|
||||||
foreach (var modId in Config.Mods)
|
|
||||||
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
|
|
||||||
|
|
||||||
MyLocalCache.SaveCheckpoint(checkpoint, Config.LoadWorld);
|
|
||||||
Log.Info("Saved world config.");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error("Failed to write sandbox config, changes will not appear on server");
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadDedicatedConfig(TorchConfig torchConfig)
|
|
||||||
{
|
|
||||||
_torchConfig = torchConfig;
|
|
||||||
DataContext = null;
|
|
||||||
MySandboxGame.Config = new MyConfig(MyPerServerSettings.GameNameSafe + ".cfg");
|
|
||||||
var path = Path.Combine(torchConfig.InstancePath, "SpaceEngineers-Dedicated.cfg");
|
|
||||||
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
Log.Error($"Failed to load dedicated config at {path}");
|
|
||||||
DataContext = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Config = new MyConfigDedicated<MyObjectBuilder_SessionSettings>(path);
|
|
||||||
Config.Load(path);
|
|
||||||
_configPath = path;
|
|
||||||
|
|
||||||
_viewModel = new ConfigDedicatedViewModel(Config);
|
|
||||||
var worldFolders = Directory.EnumerateDirectories(Path.Combine(torchConfig.InstancePath, "Saves"));
|
|
||||||
|
|
||||||
foreach (var f in worldFolders)
|
|
||||||
_viewModel.WorldPaths.Add(f);
|
|
||||||
|
|
||||||
LoadWorldMods();
|
|
||||||
DataContext = _viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadWorldMods()
|
|
||||||
{
|
|
||||||
var sandboxPath = Path.Combine(Config.LoadWorld, "Sandbox.sbc");
|
|
||||||
|
|
||||||
if (!File.Exists(sandboxPath))
|
|
||||||
return;
|
|
||||||
|
|
||||||
MyObjectBuilderSerializer.DeserializeXML(sandboxPath, out MyObjectBuilder_Checkpoint checkpoint, out ulong sizeInBytes);
|
|
||||||
if (checkpoint == null)
|
|
||||||
{
|
|
||||||
Log.Error($"Failed to load {Config.LoadWorld}, checkpoint null ({sizeInBytes} bytes, instance {TorchBase.Instance.Config.InstancePath})");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
foreach (var mod in checkpoint.Mods)
|
|
||||||
sb.AppendLine(mod.PublishedFileId.ToString());
|
|
||||||
|
|
||||||
_viewModel.Mods = sb.ToString();
|
|
||||||
|
|
||||||
Log.Info("Loaded mod list from world");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Save_OnClick(object sender, RoutedEventArgs e)
|
private void Save_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SaveConfig();
|
_instanceManager.SaveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
|
private void RemoveLimit_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
|
var vm = (BlockLimitViewModel)((Button)sender).DataContext;
|
||||||
_viewModel.SessionSettings.BlockLimits.Remove(vm);
|
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Remove(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
|
private void AddLimit_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_viewModel.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_viewModel.SessionSettings, "", 0));
|
_instanceManager.DedicatedConfig.SessionSettings.BlockLimits.Add(new BlockLimitViewModel(_instanceManager.DedicatedConfig.SessionSettings, "", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
|
private void NewWorld_OnClick(object sender, RoutedEventArgs e)
|
||||||
@@ -152,8 +46,7 @@ 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)
|
||||||
{
|
{
|
||||||
Config.LoadWorld = (string)e.AddedItems[0];
|
_instanceManager.SelectWorld((string)e.AddedItems[0]);
|
||||||
LoadWorldMods();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,12 +5,19 @@
|
|||||||
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 />
|
||||||
</UserControl.DataContext>
|
</UserControl.DataContext>
|
||||||
<DockPanel x:Name="Stack" Margin="3">
|
<Grid Margin="3">
|
||||||
<StackPanel DockPanel.Dock="Top">
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Grid.Row="0">
|
||||||
<Label Content="{Binding FullName}" FontSize="16" />
|
<Label Content="{Binding FullName}" FontSize="16" />
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Label Content="Built By: "/>
|
<Label Content="Built By: "/>
|
||||||
@@ -18,22 +25,27 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Label Content="Properties"/>
|
<Label Content="Properties"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ListView ItemsSource="{Binding Properties}" Margin="3" IsEnabled="True" DockPanel.Dock="Bottom">
|
<Expander Grid.Row="1" Header="Block Properties">
|
||||||
<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>
|
||||||
</DockPanel>
|
</Expander>
|
||||||
|
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
|
||||||
|
<entities:EntityControlsView DataContext="{Binding}"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -10,13 +10,17 @@
|
|||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:StringIdConverter x:Key="StringIdConverter"/>
|
<converters:StringIdConverter x:Key="StringIdConverter"/>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<DockPanel x:Name="Dock">
|
<Grid>
|
||||||
<Label x:Name="Label" Width="150" VerticalAlignment="Center" DockPanel.Dock="Left">
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label Grid.Column="0" x:Name="Label" Width="150" VerticalAlignment="Center">
|
||||||
<Label.Content>
|
<Label.Content>
|
||||||
<TextBlock Text="{Binding Name, StringFormat={}{0}: }" />
|
<TextBlock Text="{Binding Name, StringFormat={}{0}: }" />
|
||||||
</Label.Content>
|
</Label.Content>
|
||||||
</Label>
|
</Label>
|
||||||
<Frame x:Name="Frame" DockPanel.Dock="Right" NavigationUIVisibility="Hidden"/>
|
<Frame Grid.Column="1" x:Name="Frame" NavigationUIVisibility="Hidden"/>
|
||||||
</DockPanel>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
||||||
|
@@ -32,10 +32,10 @@ namespace Torch.Server.Views.Blocks
|
|||||||
{
|
{
|
||||||
switch (args.NewValue)
|
switch (args.NewValue)
|
||||||
{
|
{
|
||||||
case PropertyViewModel<bool> vmBool:
|
case PropertyViewModel<bool> _:
|
||||||
InitBool();
|
InitBool();
|
||||||
break;
|
break;
|
||||||
case PropertyViewModel<StringBuilder> vmSb:
|
case PropertyViewModel<StringBuilder> _:
|
||||||
InitStringBuilder();
|
InitStringBuilder();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
8
Torch.Server/Views/Entities/EntityControlHost.xaml
Normal file
8
Torch.Server/Views/Entities/EntityControlHost.xaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<UserControl x:Class="Torch.Server.Views.Entities.EntityControlHost"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
|
</UserControl>
|
72
Torch.Server/Views/Entities/EntityControlHost.xaml.cs
Normal file
72
Torch.Server/Views/Entities/EntityControlHost.xaml.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Torch.Server.Managers;
|
||||||
|
using Torch.API.Managers;
|
||||||
|
using Torch.Server.ViewModels.Entities;
|
||||||
|
|
||||||
|
namespace Torch.Server.Views.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for EntityControlHost.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class EntityControlHost : UserControl
|
||||||
|
{
|
||||||
|
public EntityControlHost()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContextChanged += OnDataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.OldValue is ViewModel vmo)
|
||||||
|
{
|
||||||
|
vmo.PropertyChanged -= DataContext_OnPropertyChanged;
|
||||||
|
}
|
||||||
|
if (e.NewValue is ViewModel vmn)
|
||||||
|
{
|
||||||
|
vmn.PropertyChanged += DataContext_OnPropertyChanged;
|
||||||
|
}
|
||||||
|
RefreshControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataContext_OnPropertyChanged(object sender, PropertyChangedEventArgs pa)
|
||||||
|
{
|
||||||
|
if (pa.PropertyName.Equals(EntityControlViewModel.SignalPropertyInvalidateControl))
|
||||||
|
RefreshControl();
|
||||||
|
else if (pa.PropertyName.Equals(nameof(EntityControlViewModel.Hide)))
|
||||||
|
RefreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Control _currentControl;
|
||||||
|
|
||||||
|
private void RefreshControl()
|
||||||
|
{
|
||||||
|
if (Dispatcher.Thread != Thread.CurrentThread)
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(RefreshControl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentControl = DataContext is EntityControlViewModel ecvm
|
||||||
|
? TorchBase.Instance?.Managers.GetManager<EntityControlManager>()?.CreateControl(ecvm)
|
||||||
|
: null;
|
||||||
|
Content = _currentControl;
|
||||||
|
RefreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshVisibility()
|
||||||
|
{
|
||||||
|
if (Dispatcher.Thread != Thread.CurrentThread)
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(RefreshVisibility);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Visibility = (DataContext is EntityControlViewModel ecvm) && !ecvm.Hide && _currentControl != null
|
||||||
|
? Visibility.Visible
|
||||||
|
: Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Torch.Server/Views/Entities/EntityControlsView.xaml
Normal file
31
Torch.Server/Views/Entities/EntityControlsView.xaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<ItemsControl x:Class="Torch.Server.Views.Entities.EntityControlsView"
|
||||||
|
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.Views.Entities"
|
||||||
|
xmlns:modelsEntities="clr-namespace:Torch.Server.ViewModels.Entities"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
ItemsSource="{Binding EntityControls}">
|
||||||
|
<ItemsControl.DataContext>
|
||||||
|
<modelsEntities:EntityViewModel/>
|
||||||
|
</ItemsControl.DataContext>
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Orientation="Vertical" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<entities:EntityControlHost DataContext="{Binding}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
<ItemsControl.ItemContainerStyle>
|
||||||
|
<Style>
|
||||||
|
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="Control.VerticalContentAlignment" Value="Stretch"/>
|
||||||
|
</Style>
|
||||||
|
</ItemsControl.ItemContainerStyle>
|
||||||
|
</ItemsControl>
|
15
Torch.Server/Views/Entities/EntityControlsView.xaml.cs
Normal file
15
Torch.Server/Views/Entities/EntityControlsView.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Torch.Server.Views.Entities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for EntityControlsView.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class EntityControlsView : ItemsControl
|
||||||
|
{
|
||||||
|
public EntityControlsView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,20 +3,28 @@
|
|||||||
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.Entities"
|
|
||||||
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
|
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
|
||||||
|
xmlns:local="clr-namespace:Torch.Server.Views.Entities"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.DataContext>
|
<UserControl.DataContext>
|
||||||
<entities:GridViewModel />
|
<entities:GridViewModel />
|
||||||
</UserControl.DataContext>
|
</UserControl.DataContext>
|
||||||
<StackPanel>
|
<Grid>
|
||||||
<StackPanel Orientation="Horizontal">
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||||
<Label Content="Name" Width="100"/>
|
<Label Content="Name" Width="100"/>
|
||||||
<TextBox Text="{Binding Name}" Margin="3"/>
|
<TextBox Text="{Binding Name}" Margin="3"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||||
<Label Content="Position" Width="100"/>
|
<Label Content="Position" Width="100"/>
|
||||||
<TextBox Text="{Binding Position}" Margin="3" />
|
<TextBox Text="{Binding Position}" Margin="3" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
<ScrollViewer Grid.Row="2" Margin="3" VerticalScrollBarVisibility="Auto">
|
||||||
|
<local:EntityControlsView DataContext="{Binding}"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -9,14 +9,23 @@
|
|||||||
<UserControl.DataContext>
|
<UserControl.DataContext>
|
||||||
<entities:VoxelMapViewModel/>
|
<entities:VoxelMapViewModel/>
|
||||||
</UserControl.DataContext>
|
</UserControl.DataContext>
|
||||||
<StackPanel>
|
<Grid>
|
||||||
<Label Content="Attached Grids"></Label>
|
<Grid.RowDefinitions>
|
||||||
<ListView ItemsSource="{Binding AttachedGrids}" Margin="3">
|
<RowDefinition Height="Auto"/>
|
||||||
<ListView.ItemTemplate>
|
<RowDefinition Height="*"/>
|
||||||
<DataTemplate>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<Expander Grid.Row="0" Header="Attached Grids">
|
||||||
</DataTemplate>
|
<ListView ItemsSource="{Binding AttachedGrids}" Margin="3">
|
||||||
</ListView.ItemTemplate>
|
<ListView.ItemTemplate>
|
||||||
</ListView>
|
<DataTemplate>
|
||||||
</StackPanel>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</Expander>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" Margin="3" VerticalScrollBarVisibility="Auto">
|
||||||
|
<local:EntityControlsView DataContext="{Binding}"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@@ -8,17 +8,17 @@
|
|||||||
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
|
xmlns:entities="clr-namespace:Torch.Server.ViewModels.Entities"
|
||||||
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.Blocks"
|
xmlns:blocks="clr-namespace:Torch.Server.ViewModels.Blocks"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.DataContext>
|
<Grid>
|
||||||
<viewModels:EntityTreeViewModel />
|
<Grid.ColumnDefinitions>
|
||||||
</UserControl.DataContext>
|
<ColumnDefinition/>
|
||||||
<DockPanel>
|
<ColumnDefinition MinWidth="300" Width="Auto"/>
|
||||||
<DockPanel DockPanel.Dock="Left">
|
</Grid.ColumnDefinitions>
|
||||||
<StackPanel DockPanel.Dock="Bottom">
|
<Grid Grid.Column="0">
|
||||||
<Button Content="Delete" Click="Delete_OnClick" IsEnabled="{Binding CurrentEntity.CanDelete}"
|
<Grid.RowDefinitions>
|
||||||
Margin="3" />
|
<RowDefinition/>
|
||||||
<Button Content="Stop" Click="Stop_OnClick" IsEnabled="{Binding CurrentEntity.CanStop}" Margin="3" />
|
<RowDefinition Height="Auto"/>
|
||||||
</StackPanel>
|
</Grid.RowDefinitions>
|
||||||
<TreeView Width="300" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged" TreeViewItem.Expanded="TreeViewItem_OnExpanded">
|
<TreeView Grid.Row="0" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged" TreeViewItem.Expanded="TreeViewItem_OnExpanded">
|
||||||
<TreeView.Resources>
|
<TreeView.Resources>
|
||||||
<HierarchicalDataTemplate DataType="{x:Type entities:GridViewModel}" ItemsSource="{Binding Blocks}">
|
<HierarchicalDataTemplate DataType="{x:Type entities:GridViewModel}" ItemsSource="{Binding Blocks}">
|
||||||
<TextBlock Text="{Binding DescriptiveName}" />
|
<TextBlock Text="{Binding DescriptiveName}" />
|
||||||
@@ -66,7 +66,12 @@
|
|||||||
</TreeViewItem.ItemTemplate>
|
</TreeViewItem.ItemTemplate>
|
||||||
</TreeViewItem>
|
</TreeViewItem>
|
||||||
</TreeView>
|
</TreeView>
|
||||||
</DockPanel>
|
<StackPanel Grid.Row="1" DockPanel.Dock="Bottom">
|
||||||
<Frame x:Name="EditorFrame" Margin="3" NavigationUIVisibility="Hidden" />
|
<Button Content="Delete" Click="Delete_OnClick" IsEnabled="{Binding CurrentEntity.CanDelete}"
|
||||||
</DockPanel>
|
Margin="3" />
|
||||||
|
<Button Content="Stop" Click="Stop_OnClick" IsEnabled="{Binding CurrentEntity.CanStop}" Margin="3" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<Frame Grid.Column="1" x:Name="EditorFrame" Margin="3" NavigationUIVisibility="Hidden" />
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
@@ -27,12 +27,14 @@ namespace Torch.Server.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class EntitiesControl : UserControl
|
public partial class EntitiesControl : UserControl
|
||||||
{
|
{
|
||||||
public EntityTreeViewModel Entities { get; set; } = new EntityTreeViewModel();
|
public EntityTreeViewModel Entities { get; set; }
|
||||||
|
|
||||||
public EntitiesControl()
|
public EntitiesControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
Entities = new EntityTreeViewModel(this);
|
||||||
DataContext = Entities;
|
DataContext = Entities;
|
||||||
|
Entities.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||||
@@ -58,7 +60,7 @@ namespace Torch.Server.Views
|
|||||||
{
|
{
|
||||||
if (Entities.CurrentEntity?.Entity is IMyCharacter)
|
if (Entities.CurrentEntity?.Entity is IMyCharacter)
|
||||||
return;
|
return;
|
||||||
TorchBase.Instance.Invoke(() => Entities.CurrentEntity?.Entity.Close());
|
TorchBase.Instance.Invoke(() => Entities.CurrentEntity?.Delete());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Stop_OnClick(object sender, RoutedEventArgs e)
|
private void Stop_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user