From e4ffbe07abce1cd3b0a12692b4f9e81943823fb5 Mon Sep 17 00:00:00 2001
From: crowbarmaster <69368213+crowbarmaster@users.noreply.github.com>
Date: Thu, 16 Sep 2021 14:17:51 -0400
Subject: [PATCH] Initial commit
---
.gitattributes | 63 +++
.gitignore | 361 ++++++++++++++++
BedrockService/BedrockService.sln | 37 ++
BedrockService/Client/App.config | 6 +
.../Client/BedrockService.Client.csproj | 147 +++++++
BedrockService/Client/Configs/Config.conf | 2 +
.../Client/Forms/EditSrv.Designer.cs | 116 ++++++
BedrockService/Client/Forms/EditSrv.cs | 54 +++
BedrockService/Client/Forms/EditSrv.resx | 126 ++++++
.../Client/Forms/MainWindow.Designer.cs | 380 +++++++++++++++++
BedrockService/Client/Forms/MainWindow.cs | 285 +++++++++++++
BedrockService/Client/Forms/MainWindow.resx | 120 ++++++
.../Forms/PlayerManagerForm.Designer.cs | 122 ++++++
.../Client/Forms/PlayerManagerForm.cs | 60 +++
.../Client/Forms/PlayerManagerForm.resx | 120 ++++++
.../Client/Management/ConfigManager.cs | 86 ++++
.../Client/Management/FormManager.cs | 35 ++
.../Client/Management/LogManager.cs | 126 ++++++
BedrockService/Client/Networking/TCPClient.cs | 264 ++++++++++++
.../Client/Properties/AssemblyInfo.cs | 35 ++
.../Client/Properties/Resources.Designer.cs | 63 +++
.../Client/Properties/Resources.resx | 117 ++++++
.../Client/Properties/Settings.Designer.cs | 26 ++
.../Client/Properties/Settings.settings | 7 +
BedrockService/Client/Utilities/Utilities.cs | 22 +
BedrockService/Client/packages.config | 4 +
.../Service/Batch/BS_InstallStart.bat | 2 +
BedrockService/Service/Batch/BS_Restart.bat | 2 +
BedrockService/Service/Batch/BS_Start.bat | 1 +
BedrockService/Service/Batch/BS_Stop.bat | 1 +
.../Service/Batch/BS_StopUninstall.bat | 2 +
.../Service/BedrockService.Service.csproj | 159 +++++++
BedrockService/Service/BedrockService.cs | 389 ++++++++++++++++++
BedrockService/Service/Configs/Default.conf | 36 ++
.../Service/Configs/Default.players | 5 +
BedrockService/Service/Configs/Globals.conf | 7 +
BedrockService/Service/InstanceProvider.cs | 148 +++++++
.../Service/Logging/ServiceLogger.cs | 57 +++
.../Service/Management/ConfigManager.cs | 339 +++++++++++++++
BedrockService/Service/Networking/Enums.cs | 35 ++
.../Service/Networking/TCPListener.cs | 366 ++++++++++++++++
BedrockService/Service/Networking/Updater.cs | 136 ++++++
BedrockService/Service/Program.cs | 71 ++++
.../Service/Properties/AssemblyInfo.cs | 35 ++
.../Service/Server/BedrockServer.cs | 384 +++++++++++++++++
.../Server/HostInfoClasses/HostInfo.cs | 78 ++++
.../Service/Server/HostInfoClasses/Player.cs | 53 +++
.../Server/HostInfoClasses/Property.cs | 19 +
.../Server/HostInfoClasses/ServerInfo.cs | 104 +++++
.../Server/HostInfoClasses/StartCmdEntry.cs | 13 +
.../Service/Server/Logging/ServerLogger.cs | 89 ++++
.../Server/Management/PlayerManager.cs | 104 +++++
.../Service/Utilities/JsonParser.cs | 27 ++
BedrockService/Service/packages.config | 4 +
BedrockService/packages.config | 10 +
LICENSE | 201 +++++++++
README.md | 234 +++++++++++
57 files changed, 5895 insertions(+)
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 BedrockService/BedrockService.sln
create mode 100644 BedrockService/Client/App.config
create mode 100644 BedrockService/Client/BedrockService.Client.csproj
create mode 100644 BedrockService/Client/Configs/Config.conf
create mode 100644 BedrockService/Client/Forms/EditSrv.Designer.cs
create mode 100644 BedrockService/Client/Forms/EditSrv.cs
create mode 100644 BedrockService/Client/Forms/EditSrv.resx
create mode 100644 BedrockService/Client/Forms/MainWindow.Designer.cs
create mode 100644 BedrockService/Client/Forms/MainWindow.cs
create mode 100644 BedrockService/Client/Forms/MainWindow.resx
create mode 100644 BedrockService/Client/Forms/PlayerManagerForm.Designer.cs
create mode 100644 BedrockService/Client/Forms/PlayerManagerForm.cs
create mode 100644 BedrockService/Client/Forms/PlayerManagerForm.resx
create mode 100644 BedrockService/Client/Management/ConfigManager.cs
create mode 100644 BedrockService/Client/Management/FormManager.cs
create mode 100644 BedrockService/Client/Management/LogManager.cs
create mode 100644 BedrockService/Client/Networking/TCPClient.cs
create mode 100644 BedrockService/Client/Properties/AssemblyInfo.cs
create mode 100644 BedrockService/Client/Properties/Resources.Designer.cs
create mode 100644 BedrockService/Client/Properties/Resources.resx
create mode 100644 BedrockService/Client/Properties/Settings.Designer.cs
create mode 100644 BedrockService/Client/Properties/Settings.settings
create mode 100644 BedrockService/Client/Utilities/Utilities.cs
create mode 100644 BedrockService/Client/packages.config
create mode 100644 BedrockService/Service/Batch/BS_InstallStart.bat
create mode 100644 BedrockService/Service/Batch/BS_Restart.bat
create mode 100644 BedrockService/Service/Batch/BS_Start.bat
create mode 100644 BedrockService/Service/Batch/BS_Stop.bat
create mode 100644 BedrockService/Service/Batch/BS_StopUninstall.bat
create mode 100644 BedrockService/Service/BedrockService.Service.csproj
create mode 100644 BedrockService/Service/BedrockService.cs
create mode 100644 BedrockService/Service/Configs/Default.conf
create mode 100644 BedrockService/Service/Configs/Default.players
create mode 100644 BedrockService/Service/Configs/Globals.conf
create mode 100644 BedrockService/Service/InstanceProvider.cs
create mode 100644 BedrockService/Service/Logging/ServiceLogger.cs
create mode 100644 BedrockService/Service/Management/ConfigManager.cs
create mode 100644 BedrockService/Service/Networking/Enums.cs
create mode 100644 BedrockService/Service/Networking/TCPListener.cs
create mode 100644 BedrockService/Service/Networking/Updater.cs
create mode 100644 BedrockService/Service/Program.cs
create mode 100644 BedrockService/Service/Properties/AssemblyInfo.cs
create mode 100644 BedrockService/Service/Server/BedrockServer.cs
create mode 100644 BedrockService/Service/Server/HostInfoClasses/HostInfo.cs
create mode 100644 BedrockService/Service/Server/HostInfoClasses/Player.cs
create mode 100644 BedrockService/Service/Server/HostInfoClasses/Property.cs
create mode 100644 BedrockService/Service/Server/HostInfoClasses/ServerInfo.cs
create mode 100644 BedrockService/Service/Server/HostInfoClasses/StartCmdEntry.cs
create mode 100644 BedrockService/Service/Server/Logging/ServerLogger.cs
create mode 100644 BedrockService/Service/Server/Management/PlayerManager.cs
create mode 100644 BedrockService/Service/Utilities/JsonParser.cs
create mode 100644 BedrockService/Service/packages.config
create mode 100644 BedrockService/packages.config
create mode 100644 LICENSE
create mode 100644 README.md
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..1ff0c423
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..fcae411f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,361 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+/Configs/Test2.playerdb
+/Configs/Default.playerdb.bak
+/Configs/Default.playerdb
+/Configs/Test2.players
+/Configs/Test2.conf
+/Configs
+/BedrockService/Service/Configs/Test2.players
+/BedrockService/Service/Configs/Test2.playerdb
+/BedrockService/Service/Configs/Test2.conf
+/BedrockService/Service/Configs/Default.playerdb.bak
+/BedrockService/Service/Configs/Default.playerdb
diff --git a/BedrockService/BedrockService.sln b/BedrockService/BedrockService.sln
new file mode 100644
index 00000000..e095d019
--- /dev/null
+++ b/BedrockService/BedrockService.sln
@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29709.97
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockService.Service", "Service\BedrockService.Service.csproj", "{13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{83D2040C-760E-4475-ACBE-587536C90911}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockService.Client", "Client\BedrockService.Client.csproj", "{A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C} = {83D2040C-760E-4475-ACBE-587536C90911}
+ {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740} = {83D2040C-760E-4475-ACBE-587536C90911}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {7A40EC18-E15E-44CE-87EE-61F71CBC3FB6}
+ EndGlobalSection
+EndGlobal
diff --git a/BedrockService/Client/App.config b/BedrockService/Client/App.config
new file mode 100644
index 00000000..56efbc7b
--- /dev/null
+++ b/BedrockService/Client/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BedrockService/Client/BedrockService.Client.csproj b/BedrockService/Client/BedrockService.Client.csproj
new file mode 100644
index 00000000..b45fa06b
--- /dev/null
+++ b/BedrockService/Client/BedrockService.Client.csproj
@@ -0,0 +1,147 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}
+ WinExe
+ BedrockService.Client
+ BedrockService.Client
+ v4.7.2
+ 512
+ true
+ true
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+ x86
+ true
+ full
+ false
+ ..\bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ BedrockService.Client.Forms.MainWindow
+
+
+
+ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ PlayerManagerForm.cs
+
+
+
+
+
+ Form
+
+
+ EditSrv.cs
+
+
+ Form
+
+
+ MainWindow.cs
+
+
+
+
+
+ PlayerManagerForm.cs
+
+
+ EditSrv.cs
+
+
+ MainWindow.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.7.2 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+ {13b2b5a8-71e9-49f4-9bfb-3c3b3c0a054c}
+ BedrockService.Service
+
+
+
+
\ No newline at end of file
diff --git a/BedrockService/Client/Configs/Config.conf b/BedrockService/Client/Configs/Config.conf
new file mode 100644
index 00000000..86398eeb
--- /dev/null
+++ b/BedrockService/Client/Configs/Config.conf
@@ -0,0 +1,2 @@
+[Hosts]
+host1=127.0.0.1:19134
diff --git a/BedrockService/Client/Forms/EditSrv.Designer.cs b/BedrockService/Client/Forms/EditSrv.Designer.cs
new file mode 100644
index 00000000..86c8b203
--- /dev/null
+++ b/BedrockService/Client/Forms/EditSrv.Designer.cs
@@ -0,0 +1,116 @@
+
+namespace BedrockService.Client.Forms
+{
+ partial class EditSrv
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.CancelBtn = new System.Windows.Forms.Button();
+ this.SaveBtn = new System.Windows.Forms.Button();
+ this.gridView = new System.Windows.Forms.DataGridView();
+ this.EntryKey = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.EntryData = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ ((System.ComponentModel.ISupportInitialize)(this.gridView)).BeginInit();
+ this.SuspendLayout();
+ //
+ // CancelBtn
+ //
+ this.CancelBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+ this.CancelBtn.Location = new System.Drawing.Point(96, 390);
+ this.CancelBtn.Name = "CancelBtn";
+ this.CancelBtn.Size = new System.Drawing.Size(200, 23);
+ this.CancelBtn.TabIndex = 48;
+ this.CancelBtn.Text = "Cancel";
+ this.CancelBtn.UseVisualStyleBackColor = true;
+ this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
+ //
+ // SaveBtn
+ //
+ this.SaveBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
+ this.SaveBtn.Location = new System.Drawing.Point(428, 390);
+ this.SaveBtn.Name = "SaveBtn";
+ this.SaveBtn.Size = new System.Drawing.Size(200, 23);
+ this.SaveBtn.TabIndex = 49;
+ this.SaveBtn.Text = "Save";
+ this.SaveBtn.UseVisualStyleBackColor = true;
+ this.SaveBtn.Click += new System.EventHandler(this.SaveBtn_Click);
+ //
+ // gridView
+ //
+ this.gridView.AllowUserToAddRows = false;
+ this.gridView.AllowUserToDeleteRows = false;
+ this.gridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.gridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+ this.EntryKey,
+ this.EntryData});
+ this.gridView.Location = new System.Drawing.Point(13, 13);
+ this.gridView.MultiSelect = false;
+ this.gridView.Name = "gridView";
+ this.gridView.Size = new System.Drawing.Size(775, 371);
+ this.gridView.TabIndex = 1;
+ //
+ // EntryKey
+ //
+ this.EntryKey.HeaderText = "Entry:";
+ this.EntryKey.MinimumWidth = 20;
+ this.EntryKey.Name = "EntryKey";
+ this.EntryKey.ReadOnly = true;
+ this.EntryKey.Width = 300;
+ //
+ // EntryData
+ //
+ this.EntryData.HeaderText = "Value:";
+ this.EntryData.Name = "EntryData";
+ this.EntryData.Width = 600;
+ //
+ // EditSrv
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 450);
+ this.Controls.Add(this.gridView);
+ this.Controls.Add(this.SaveBtn);
+ this.Controls.Add(this.CancelBtn);
+ this.Name = "EditSrv";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Form1";
+ ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Button CancelBtn;
+ private System.Windows.Forms.Button SaveBtn;
+ private System.Windows.Forms.DataGridView gridView;
+ private System.Windows.Forms.DataGridViewTextBoxColumn EntryKey;
+ private System.Windows.Forms.DataGridViewTextBoxColumn EntryData;
+ }
+}
\ No newline at end of file
diff --git a/BedrockService/Client/Forms/EditSrv.cs b/BedrockService/Client/Forms/EditSrv.cs
new file mode 100644
index 00000000..56d51979
--- /dev/null
+++ b/BedrockService/Client/Forms/EditSrv.cs
@@ -0,0 +1,54 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace BedrockService.Client.Forms
+{
+ public partial class EditSrv : Form
+ {
+ DataGridView dataGrid;
+ public List workingProps;
+
+
+ public EditSrv()
+ {
+ InitializeComponent();
+ dataGrid = gridView;
+ }
+
+ public void PopulateBoxes(List propList)
+ {
+ workingProps = propList;
+ foreach (Property prop in workingProps)
+ {
+ dataGrid.Rows.Add(new string[] { prop.KeyName, prop.Value });
+ }
+ }
+
+ private void CancelBtn_Click(object sender, EventArgs e)
+ {
+ Close();
+ Dispose();
+ }
+
+ private void SaveBtn_Click(object sender, EventArgs e)
+ {
+ foreach (DataGridViewRow row in dataGrid.Rows)
+ {
+ foreach (Property prop in workingProps)
+ {
+ if ((string)row.Cells[0].Value == prop.KeyName)
+ {
+ if (prop.KeyName != "server-name")
+ {
+ prop.Value = (string)row.Cells[1].Value;
+ }
+ }
+ }
+ }
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+ }
+}
diff --git a/BedrockService/Client/Forms/EditSrv.resx b/BedrockService/Client/Forms/EditSrv.resx
new file mode 100644
index 00000000..cef55d1e
--- /dev/null
+++ b/BedrockService/Client/Forms/EditSrv.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ True
+
+
+ True
+
+
\ No newline at end of file
diff --git a/BedrockService/Client/Forms/MainWindow.Designer.cs b/BedrockService/Client/Forms/MainWindow.Designer.cs
new file mode 100644
index 00000000..b144d98e
--- /dev/null
+++ b/BedrockService/Client/Forms/MainWindow.Designer.cs
@@ -0,0 +1,380 @@
+namespace BedrockService.Client.Forms
+{
+ partial class MainWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.Connect = new System.Windows.Forms.Button();
+ this.HostInfoLabel = new System.Windows.Forms.Label();
+ this.HostListBox = new System.Windows.Forms.ComboBox();
+ this.LogBox = new System.Windows.Forms.TextBox();
+ this.ServerSelectBox = new System.Windows.Forms.ListBox();
+ this.Disconn = new System.Windows.Forms.Button();
+ this.StopStartSvc = new System.Windows.Forms.Button();
+ this.RestartSvc = new System.Windows.Forms.Button();
+ this.EditGlobals = new System.Windows.Forms.Button();
+ this.EditService = new System.Windows.Forms.Button();
+ this.ChkUpdates = new System.Windows.Forms.Button();
+ this.GlobBackup = new System.Windows.Forms.Button();
+ this.cmdTextBox = new System.Windows.Forms.TextBox();
+ this.SendCmd = new System.Windows.Forms.Button();
+ this.EditCfg = new System.Windows.Forms.Button();
+ this.PlayerManagerBtn = new System.Windows.Forms.Button();
+ this.EditStCmd = new System.Windows.Forms.Button();
+ this.ManPacks = new System.Windows.Forms.Button();
+ this.SingBackup = new System.Windows.Forms.Button();
+ this.RestartSrv = new System.Windows.Forms.Button();
+ this.Rollbackup = new System.Windows.Forms.Button();
+ this.SvcLog = new System.Windows.Forms.CheckBox();
+ this.ServerInfoBox = new System.Windows.Forms.TextBox();
+ this.SuspendLayout();
+ //
+ // Connect
+ //
+ this.Connect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.Connect.Location = new System.Drawing.Point(601, 73);
+ this.Connect.Name = "Connect";
+ this.Connect.Size = new System.Drawing.Size(170, 25);
+ this.Connect.TabIndex = 0;
+ this.Connect.Text = "Connect";
+ this.Connect.UseVisualStyleBackColor = true;
+ this.Connect.Click += new System.EventHandler(this.Connect_Click);
+ //
+ // HostInfoLabel
+ //
+ this.HostInfoLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.HostInfoLabel.AutoSize = true;
+ this.HostInfoLabel.Location = new System.Drawing.Point(598, 28);
+ this.HostInfoLabel.Name = "HostInfoLabel";
+ this.HostInfoLabel.Size = new System.Drawing.Size(87, 13);
+ this.HostInfoLabel.TabIndex = 1;
+ this.HostInfoLabel.Text = "HostConnectInfo";
+ //
+ // HostListBox
+ //
+ this.HostListBox.AllowDrop = true;
+ this.HostListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.HostListBox.FormattingEnabled = true;
+ this.HostListBox.Location = new System.Drawing.Point(601, 44);
+ this.HostListBox.Name = "HostListBox";
+ this.HostListBox.Size = new System.Drawing.Size(355, 21);
+ this.HostListBox.TabIndex = 2;
+ //
+ // LogBox
+ //
+ this.LogBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.LogBox.Location = new System.Drawing.Point(12, 44);
+ this.LogBox.Multiline = true;
+ this.LogBox.Name = "LogBox";
+ this.LogBox.ReadOnly = true;
+ this.LogBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.LogBox.Size = new System.Drawing.Size(559, 341);
+ this.LogBox.TabIndex = 3;
+ this.LogBox.WordWrap = false;
+ //
+ // ServerSelectBox
+ //
+ this.ServerSelectBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.ServerSelectBox.FormattingEnabled = true;
+ this.ServerSelectBox.Location = new System.Drawing.Point(786, 112);
+ this.ServerSelectBox.Name = "ServerSelectBox";
+ this.ServerSelectBox.Size = new System.Drawing.Size(170, 95);
+ this.ServerSelectBox.TabIndex = 4;
+ this.ServerSelectBox.SelectedIndexChanged += new System.EventHandler(this.ServerSelectBox_SelectedIndexChanged);
+ //
+ // Disconn
+ //
+ this.Disconn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.Disconn.Enabled = false;
+ this.Disconn.Location = new System.Drawing.Point(786, 73);
+ this.Disconn.Name = "Disconn";
+ this.Disconn.Size = new System.Drawing.Size(170, 25);
+ this.Disconn.TabIndex = 5;
+ this.Disconn.Text = "Disconnect";
+ this.Disconn.UseVisualStyleBackColor = true;
+ this.Disconn.Click += new System.EventHandler(this.Disconn_Click);
+ //
+ // StopStartSvc
+ //
+ this.StopStartSvc.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.StopStartSvc.Enabled = false;
+ this.StopStartSvc.Location = new System.Drawing.Point(786, 229);
+ this.StopStartSvc.Name = "StopStartSvc";
+ this.StopStartSvc.Size = new System.Drawing.Size(75, 23);
+ this.StopStartSvc.TabIndex = 6;
+ this.StopStartSvc.Text = "StopStart";
+ this.StopStartSvc.UseVisualStyleBackColor = true;
+ this.StopStartSvc.Click += new System.EventHandler(this.StopStartSvc_Click);
+ //
+ // RestartSvc
+ //
+ this.RestartSvc.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.RestartSvc.Enabled = false;
+ this.RestartSvc.Location = new System.Drawing.Point(881, 229);
+ this.RestartSvc.Name = "RestartSvc";
+ this.RestartSvc.Size = new System.Drawing.Size(75, 23);
+ this.RestartSvc.TabIndex = 7;
+ this.RestartSvc.Text = "RestartSvc";
+ this.RestartSvc.UseVisualStyleBackColor = true;
+ //
+ // EditGlobals
+ //
+ this.EditGlobals.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.EditGlobals.Enabled = false;
+ this.EditGlobals.Location = new System.Drawing.Point(786, 258);
+ this.EditGlobals.Name = "EditGlobals";
+ this.EditGlobals.Size = new System.Drawing.Size(75, 23);
+ this.EditGlobals.TabIndex = 8;
+ this.EditGlobals.Text = "EditGlobals";
+ this.EditGlobals.UseVisualStyleBackColor = true;
+ this.EditGlobals.Click += new System.EventHandler(this.EditGlobals_Click);
+ //
+ // EditService
+ //
+ this.EditService.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.EditService.Enabled = false;
+ this.EditService.Location = new System.Drawing.Point(881, 258);
+ this.EditService.Name = "EditService";
+ this.EditService.Size = new System.Drawing.Size(75, 23);
+ this.EditService.TabIndex = 9;
+ this.EditService.Text = "EditService";
+ this.EditService.UseVisualStyleBackColor = true;
+ //
+ // ChkUpdates
+ //
+ this.ChkUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.ChkUpdates.Enabled = false;
+ this.ChkUpdates.Location = new System.Drawing.Point(786, 287);
+ this.ChkUpdates.Name = "ChkUpdates";
+ this.ChkUpdates.Size = new System.Drawing.Size(75, 23);
+ this.ChkUpdates.TabIndex = 10;
+ this.ChkUpdates.Text = "ChkUpdates";
+ this.ChkUpdates.UseVisualStyleBackColor = true;
+ //
+ // GlobBackup
+ //
+ this.GlobBackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.GlobBackup.Enabled = false;
+ this.GlobBackup.Location = new System.Drawing.Point(881, 287);
+ this.GlobBackup.Name = "GlobBackup";
+ this.GlobBackup.Size = new System.Drawing.Size(75, 23);
+ this.GlobBackup.TabIndex = 11;
+ this.GlobBackup.Text = "GlobBckup";
+ this.GlobBackup.UseVisualStyleBackColor = true;
+ this.GlobBackup.Click += new System.EventHandler(this.GlobBackup_Click);
+ //
+ // cmdTextBox
+ //
+ this.cmdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.cmdTextBox.Enabled = false;
+ this.cmdTextBox.Location = new System.Drawing.Point(601, 391);
+ this.cmdTextBox.Name = "cmdTextBox";
+ this.cmdTextBox.Size = new System.Drawing.Size(355, 20);
+ this.cmdTextBox.TabIndex = 12;
+ //
+ // SendCmd
+ //
+ this.SendCmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.SendCmd.Enabled = false;
+ this.SendCmd.Location = new System.Drawing.Point(601, 362);
+ this.SendCmd.Name = "SendCmd";
+ this.SendCmd.Size = new System.Drawing.Size(170, 23);
+ this.SendCmd.TabIndex = 13;
+ this.SendCmd.Text = "SendCmd";
+ this.SendCmd.UseVisualStyleBackColor = true;
+ this.SendCmd.Click += new System.EventHandler(this.SendCmd_Click);
+ //
+ // EditCfg
+ //
+ this.EditCfg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.EditCfg.Enabled = false;
+ this.EditCfg.Location = new System.Drawing.Point(601, 229);
+ this.EditCfg.Name = "EditCfg";
+ this.EditCfg.Size = new System.Drawing.Size(75, 23);
+ this.EditCfg.TabIndex = 15;
+ this.EditCfg.Text = "EditCfg";
+ this.EditCfg.UseVisualStyleBackColor = true;
+ this.EditCfg.Click += new System.EventHandler(this.EditCfg_Click);
+ //
+ // EditPerms
+ //
+ this.PlayerManagerBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.PlayerManagerBtn.Enabled = false;
+ this.PlayerManagerBtn.Location = new System.Drawing.Point(601, 316);
+ this.PlayerManagerBtn.Name = "EditPerms";
+ this.PlayerManagerBtn.Size = new System.Drawing.Size(170, 23);
+ this.PlayerManagerBtn.TabIndex = 16;
+ this.PlayerManagerBtn.Text = "PlayerManager";
+ this.PlayerManagerBtn.UseVisualStyleBackColor = true;
+ this.PlayerManagerBtn.Click += new System.EventHandler(this.PlayerManager_Click);
+ //
+ // EditStCmd
+ //
+ this.EditStCmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.EditStCmd.Enabled = false;
+ this.EditStCmd.Location = new System.Drawing.Point(696, 229);
+ this.EditStCmd.Name = "EditStCmd";
+ this.EditStCmd.Size = new System.Drawing.Size(75, 23);
+ this.EditStCmd.TabIndex = 18;
+ this.EditStCmd.Text = "EditStCmd";
+ this.EditStCmd.UseVisualStyleBackColor = true;
+ //
+ // ManPacks
+ //
+ this.ManPacks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.ManPacks.Enabled = false;
+ this.ManPacks.Location = new System.Drawing.Point(601, 287);
+ this.ManPacks.Name = "ManPacks";
+ this.ManPacks.Size = new System.Drawing.Size(75, 23);
+ this.ManPacks.TabIndex = 19;
+ this.ManPacks.Text = "ManPacks";
+ this.ManPacks.UseVisualStyleBackColor = true;
+ //
+ // SingBackup
+ //
+ this.SingBackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.SingBackup.Enabled = false;
+ this.SingBackup.Location = new System.Drawing.Point(696, 258);
+ this.SingBackup.Name = "SingBackup";
+ this.SingBackup.Size = new System.Drawing.Size(75, 23);
+ this.SingBackup.TabIndex = 20;
+ this.SingBackup.Text = "SingBackup";
+ this.SingBackup.UseVisualStyleBackColor = true;
+ this.SingBackup.Click += new System.EventHandler(this.SingBackup_Click);
+ //
+ // RestartSrv
+ //
+ this.RestartSrv.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.RestartSrv.Enabled = false;
+ this.RestartSrv.Location = new System.Drawing.Point(696, 287);
+ this.RestartSrv.Name = "RestartSrv";
+ this.RestartSrv.Size = new System.Drawing.Size(75, 23);
+ this.RestartSrv.TabIndex = 21;
+ this.RestartSrv.Text = "RestartSvr";
+ this.RestartSrv.UseVisualStyleBackColor = true;
+ this.RestartSrv.Click += new System.EventHandler(this.RestartSrv_Click);
+ //
+ // Rollbackup
+ //
+ this.Rollbackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.Rollbackup.Enabled = false;
+ this.Rollbackup.Location = new System.Drawing.Point(601, 258);
+ this.Rollbackup.Name = "Rollbackup";
+ this.Rollbackup.Size = new System.Drawing.Size(75, 23);
+ this.Rollbackup.TabIndex = 22;
+ this.Rollbackup.Text = "RollBackup";
+ this.Rollbackup.UseVisualStyleBackColor = true;
+ //
+ // SvcLog
+ //
+ this.SvcLog.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.SvcLog.Enabled = false;
+ this.SvcLog.Location = new System.Drawing.Point(225, 391);
+ this.SvcLog.Name = "SvcLog";
+ this.SvcLog.RightToLeft = System.Windows.Forms.RightToLeft.No;
+ this.SvcLog.Size = new System.Drawing.Size(137, 23);
+ this.SvcLog.TabIndex = 24;
+ this.SvcLog.Text = "Switch to service logs";
+ this.SvcLog.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.SvcLog.UseVisualStyleBackColor = true;
+ //
+ // ServerInfoBox
+ //
+ this.ServerInfoBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.ServerInfoBox.Location = new System.Drawing.Point(601, 112);
+ this.ServerInfoBox.Multiline = true;
+ this.ServerInfoBox.Name = "ServerInfoBox";
+ this.ServerInfoBox.ReadOnly = true;
+ this.ServerInfoBox.Size = new System.Drawing.Size(170, 95);
+ this.ServerInfoBox.TabIndex = 25;
+ //
+ // MainWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(984, 460);
+ this.Controls.Add(this.ServerInfoBox);
+ this.Controls.Add(this.SvcLog);
+ this.Controls.Add(this.Rollbackup);
+ this.Controls.Add(this.RestartSrv);
+ this.Controls.Add(this.SingBackup);
+ this.Controls.Add(this.ManPacks);
+ this.Controls.Add(this.EditStCmd);
+ this.Controls.Add(this.PlayerManagerBtn);
+ this.Controls.Add(this.EditCfg);
+ this.Controls.Add(this.SendCmd);
+ this.Controls.Add(this.cmdTextBox);
+ this.Controls.Add(this.GlobBackup);
+ this.Controls.Add(this.ChkUpdates);
+ this.Controls.Add(this.EditService);
+ this.Controls.Add(this.EditGlobals);
+ this.Controls.Add(this.RestartSvc);
+ this.Controls.Add(this.StopStartSvc);
+ this.Controls.Add(this.Disconn);
+ this.Controls.Add(this.ServerSelectBox);
+ this.Controls.Add(this.LogBox);
+ this.Controls.Add(this.HostListBox);
+ this.Controls.Add(this.HostInfoLabel);
+ this.Controls.Add(this.Connect);
+ this.Name = "MainWindow";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Bedrock Service Management";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button Connect;
+ private System.Windows.Forms.Label HostInfoLabel;
+ public System.Windows.Forms.TextBox LogBox;
+ private System.Windows.Forms.ListBox ServerSelectBox;
+ private System.Windows.Forms.Button Disconn;
+ private System.Windows.Forms.Button StopStartSvc;
+ private System.Windows.Forms.Button RestartSvc;
+ private System.Windows.Forms.Button EditGlobals;
+ private System.Windows.Forms.Button EditService;
+ private System.Windows.Forms.Button ChkUpdates;
+ private System.Windows.Forms.Button GlobBackup;
+ private System.Windows.Forms.TextBox cmdTextBox;
+ private System.Windows.Forms.Button SendCmd;
+ private System.Windows.Forms.Button EditCfg;
+ private System.Windows.Forms.Button PlayerManagerBtn;
+ private System.Windows.Forms.Button EditStCmd;
+ private System.Windows.Forms.Button ManPacks;
+ private System.Windows.Forms.Button SingBackup;
+ private System.Windows.Forms.Button RestartSrv;
+ private System.Windows.Forms.Button Rollbackup;
+ private System.Windows.Forms.CheckBox SvcLog;
+ private System.Windows.Forms.TextBox ServerInfoBox;
+ public System.Windows.Forms.ComboBox HostListBox;
+ }
+}
+
diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs
new file mode 100644
index 00000000..1a535eff
--- /dev/null
+++ b/BedrockService/Client/Forms/MainWindow.cs
@@ -0,0 +1,285 @@
+using BedrockService.Client.Management;
+using BedrockService.Client.Utilities;
+using BedrockService.Service.Networking;
+using BedrockService.Service.Server.HostInfoClasses;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace BedrockService.Client.Forms
+{
+ public partial class MainWindow : Form
+ {
+ public static HostInfo connectedHost;
+ public static ServerInfo selectedServer;
+ public static int ConnectTimeout;
+ public static bool ShowsSvcLog = false;
+ public static readonly int ConnectTimeoutLimit = 100;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ InitForm();
+ SvcLog.CheckedChanged += SvcLog_CheckedChanged;
+ }
+
+ private void SvcLog_CheckedChanged(object sender, EventArgs e)
+ {
+ ShowsSvcLog = SvcLog.Checked;
+ }
+
+ [STAThread]
+ static void Main()
+ {
+ ConfigManager.LoadConfigs();
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(FormManager.GetMainWindow);
+ }
+
+ public void InitForm()
+ {
+ foreach (HostInfo host in ConfigManager.HostConnectList)
+ {
+ HostListBox.Items.Add(host.HostName);
+ }
+ HostListBox.Refresh();
+ FormClosing += MainWindow_FormClosing;
+ }
+
+ public void HeartbeatFailDisconnect()
+ {
+ Disconn_Click(null, null);
+ try
+ {
+ HostInfoLabel.Invoke((MethodInvoker)delegate { HostInfoLabel.Text = "Lost connection to host!"; });
+ ServerInfoBox.Invoke((MethodInvoker)delegate { ServerInfoBox.Text = "Lost connection to host!"; });
+ }
+ catch (InvalidOperationException) { }
+
+ selectedServer = null;
+ connectedHost = null;
+ }
+
+
+ private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ Console.WriteLine("Stopping log thread...");
+ if (LogManager.StopLogThread())
+ {
+ Console.WriteLine("Sending disconnect msg...");
+ if (FormManager.GetTCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect))
+ {
+ Console.WriteLine("Closing connection...");
+ FormManager.GetTCPClient.CloseConnection();
+ selectedServer = null;
+ connectedHost = null;
+ }
+ }
+ }
+
+ private void Connect_Click(object sender, EventArgs e)
+ {
+ if (HostListBox.SelectedIndex != -1)
+ {
+ try
+ {
+ if (FormManager.GetTCPClient.ConnectHost(ConfigManager.HostConnectList.FirstOrDefault(host => host.HostName == (string)HostListBox.SelectedItem)))
+ {
+ while (connectedHost == null && FormManager.GetTCPClient.Connected)
+ {
+ Thread.Sleep(100);
+ ConnectTimeout++;
+ if (ConnectTimeout > ConnectTimeoutLimit)
+ {
+ FormManager.GetTCPClient.CloseConnection();
+ ConnectTimeout = 0;
+ }
+ }
+ ConnectTimeout = 0;
+ if (!FormManager.GetTCPClient.Connected)
+ {
+ HostInfoLabel.Text = $"Failed to connect to host!";
+ return;
+ }
+ LogManager.InitLogThread(connectedHost);
+ HostInfoLabel.Text = $"Connected to host:";
+ foreach (ServerInfo server in connectedHost.GetServerInfos())
+ {
+ ServerSelectBox.Items.Add(server.ServerName);
+ }
+ ServerSelectBox.Refresh();
+ ServerSelectBox.SelectedIndex = 0;
+ selectedServer = connectedHost.GetServerInfo((string)ServerSelectBox.SelectedItem);
+ LogManager.StartLogThread();
+ ComponentEnableManager();
+ }
+ else
+ {
+ HostInfoLabel.Text = $"Failed to connect to host!";
+ return;
+ }
+ }
+ catch (ServerConnectException ex)
+ {
+ HostInfoLabel.Text = ex.Message;
+ }
+ }
+ }
+
+ private void ServerSelectBox_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (connectedHost != null)
+ {
+ foreach (ServerInfo server in connectedHost.GetServerInfos())
+ {
+ if (ServerSelectBox.SelectedItem != null && ServerSelectBox.SelectedItem.ToString() == server.ServerName)
+ {
+ selectedServer = server;
+ ServerInfoBox.Text = server.ServerName;
+ LogBox.Text = selectedServer.ConsoleBuffer.ToString();
+ LogBox.Select(LogBox.Text.Length, 0);
+ ComponentEnableManager();
+ }
+ }
+ }
+ }
+
+ private void SingBackup_Click(object sender, EventArgs e)
+ {
+ byte[] serverName = Encoding.UTF8.GetBytes(selectedServer.ServerName);
+ FormManager.GetTCPClient.SendData(serverName, NetworkMessageSource.Client, NetworkMessageDestination.Server, NetworkMessageTypes.Backup);
+ Thread.Sleep(500);
+ }
+
+ private void EditCfg_Click(object sender, EventArgs e)
+ {
+ EditSrv editSrvDialog = new EditSrv();
+ editSrvDialog.PopulateBoxes(selectedServer.ServerPropList);
+ if (editSrvDialog.ShowDialog() == DialogResult.OK)
+ {
+ JsonUtilities.SendJsonMsg>(editSrvDialog.workingProps, NetworkMessageDestination.Service, NetworkMessageTypes.PropUpdate);
+ selectedServer.ServerPropList = editSrvDialog.workingProps;
+ editSrvDialog.Close();
+ editSrvDialog.Dispose();
+ RestartSrv_Click(null, null);
+ }
+ }
+
+ private void RestartSrv_Click(object sender, EventArgs e)
+ {
+ byte[] serverName = Encoding.UTF8.GetBytes(selectedServer.ServerName);
+ FormManager.GetTCPClient.SendData(serverName, NetworkMessageSource.Client, NetworkMessageDestination.Server, NetworkMessageTypes.Restart);
+ }
+
+ private void StopStartSvc_Click(object sender, EventArgs e)
+ {
+
+ }
+
+ private void EditGlobals_Click(object sender, EventArgs e)
+ {
+ EditSrv editSrvDialog = new EditSrv();
+ editSrvDialog.PopulateBoxes(connectedHost.GetGlobals());
+ if (editSrvDialog.ShowDialog() == DialogResult.OK)
+ {
+ JsonUtilities.SendJsonMsg>(editSrvDialog.workingProps, NetworkMessageDestination.Service, NetworkMessageTypes.PropUpdate);
+ connectedHost.SetGlobals(editSrvDialog.workingProps);
+ editSrvDialog.Close();
+ editSrvDialog.Dispose();
+ RestartSrv_Click(null, null);
+ }
+ }
+
+ private void GlobBackup_Click(object sender, EventArgs e)
+ {
+
+ }
+
+ private void PlayerManager_Click(object sender, EventArgs e)
+ {
+ PlayerManagerForm form = new PlayerManagerForm(selectedServer);
+ form.Show();
+ }
+
+ private void Disconn_Click(object sender, EventArgs e)
+ {
+ if (LogManager.StopLogThread())
+ {
+ try
+ {
+ if (FormManager.GetTCPClient.Connected && FormManager.GetTCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect))
+ {
+ Thread.Sleep(500);
+ FormManager.GetTCPClient.CloseConnection();
+ }
+ selectedServer = null;
+ connectedHost = null;
+ LogBox.Invoke((MethodInvoker)delegate { LogBox.Text = ""; });
+ FormManager.GetMainWindow.Invoke((MethodInvoker)delegate
+ {
+ ComponentEnableManager();
+ ServerSelectBox.Items.Clear();
+ ServerSelectBox.SelectedIndex = -1;
+ ServerInfoBox.Text = "";
+ HostInfoLabel.Text = $"Select a host below:";
+ });
+ }
+ catch (Exception) { }
+
+ }
+ }
+
+ private void SendCmd_Click(object sender, EventArgs e)
+ {
+ if (cmdTextBox.Text.Length > 0)
+ {
+ byte[] msg = Encoding.UTF8.GetBytes($"{selectedServer.ServerName};{cmdTextBox.Text}");
+ FormManager.GetTCPClient.SendData(msg, NetworkMessageSource.Client, NetworkMessageDestination.Server, NetworkMessageTypes.Command);
+ }
+ }
+
+ private void ComponentEnableManager()
+ {
+ Connect.Enabled = connectedHost == null;
+ Disconn.Enabled = connectedHost != null;
+ //StopStartSvc.Enabled = connectedHost != null;
+ //RestartSvc.Enabled = connectedHost != null;
+ EditGlobals.Enabled = connectedHost != null;
+ //EditService.Enabled = connectedHost != null;
+ //ChkUpdates.Enabled = connectedHost != null;
+ //GlobBackup.Enabled = connectedHost != null;
+ EditCfg.Enabled = (connectedHost != null && selectedServer != null);
+ PlayerManagerBtn.Enabled = (connectedHost != null && selectedServer != null);
+ //EditStCmd.Enabled = (connectedHost != null && selectedServer != null);
+ //ManPacks.Enabled = (connectedHost != null && selectedServer != null);
+ SingBackup.Enabled = (connectedHost != null && selectedServer != null);
+ //Rollbackup.Enabled = (connectedHost != null && selectedServer != null);
+ RestartSrv.Enabled = (connectedHost != null && selectedServer != null);
+ ServerInfoBox.Enabled = (connectedHost != null && selectedServer != null);
+ SendCmd.Enabled = (connectedHost != null && selectedServer != null);
+ cmdTextBox.Enabled = (connectedHost != null && selectedServer != null);
+ SvcLog.Enabled = (connectedHost != null && selectedServer != null);
+ }
+
+ private class ServerConnectException : Exception
+ {
+ public ServerConnectException() { }
+
+ public ServerConnectException(string message)
+ : base(message)
+ {
+
+ }
+
+ public ServerConnectException(string message, Exception inner)
+ : base(message, inner)
+ {
+
+ }
+ }
+ }
+}
diff --git a/BedrockService/Client/Forms/MainWindow.resx b/BedrockService/Client/Forms/MainWindow.resx
new file mode 100644
index 00000000..1af7de15
--- /dev/null
+++ b/BedrockService/Client/Forms/MainWindow.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs b/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs
new file mode 100644
index 00000000..09d9401d
--- /dev/null
+++ b/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs
@@ -0,0 +1,122 @@
+
+namespace BedrockService.Client.Forms
+{
+ partial class PlayerManagerForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.tabControl = new System.Windows.Forms.TabControl();
+ this.tabPage1 = new System.Windows.Forms.TabPage();
+ this.gridView = new System.Windows.Forms.DataGridView();
+ this.tabPage2 = new System.Windows.Forms.TabPage();
+ this.gridViewKnown = new System.Windows.Forms.DataGridView();
+ this.tabControl.SuspendLayout();
+ this.tabPage1.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.gridView)).BeginInit();
+ this.tabPage2.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.gridViewKnown)).BeginInit();
+ this.SuspendLayout();
+ //
+ // tabControl
+ //
+ this.tabControl.Controls.Add(this.tabPage1);
+ this.tabControl.Controls.Add(this.tabPage2);
+ this.tabControl.Location = new System.Drawing.Point(12, 12);
+ this.tabControl.Name = "tabControl";
+ this.tabControl.SelectedIndex = 0;
+ this.tabControl.Size = new System.Drawing.Size(991, 364);
+ this.tabControl.TabIndex = 1;
+ //
+ // tabPage1
+ //
+ this.tabPage1.Controls.Add(this.gridView);
+ this.tabPage1.Location = new System.Drawing.Point(4, 22);
+ this.tabPage1.Name = "tabPage1";
+ this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPage1.Size = new System.Drawing.Size(983, 338);
+ this.tabPage1.TabIndex = 0;
+ this.tabPage1.Text = "tabPage1";
+ this.tabPage1.UseVisualStyleBackColor = true;
+ //
+ // gridView
+ //
+ this.gridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
+ this.gridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
+ this.gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.gridView.Location = new System.Drawing.Point(0, 3);
+ this.gridView.Name = "gridView";
+ this.gridView.Size = new System.Drawing.Size(983, 335);
+ this.gridView.TabIndex = 3;
+ //
+ // tabPage2
+ //
+ this.tabPage2.Controls.Add(this.gridViewKnown);
+ this.tabPage2.Location = new System.Drawing.Point(4, 22);
+ this.tabPage2.Name = "tabPage2";
+ this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
+ this.tabPage2.Size = new System.Drawing.Size(983, 338);
+ this.tabPage2.TabIndex = 1;
+ this.tabPage2.Text = "tabPage2";
+ this.tabPage2.UseVisualStyleBackColor = true;
+ //
+ // gridViewKnown
+ //
+ this.gridViewKnown.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
+ this.gridViewKnown.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
+ this.gridViewKnown.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.gridViewKnown.Location = new System.Drawing.Point(0, 0);
+ this.gridViewKnown.Name = "gridViewKnown";
+ this.gridViewKnown.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders;
+ this.gridViewKnown.Size = new System.Drawing.Size(983, 338);
+ this.gridViewKnown.TabIndex = 0;
+ //
+ // PlayerManagerForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(1038, 423);
+ this.Controls.Add(this.tabControl);
+ this.Margin = new System.Windows.Forms.Padding(2);
+ this.Name = "PlayerManagerForm";
+ this.Text = "Player Manager";
+ this.tabControl.ResumeLayout(false);
+ this.tabPage1.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit();
+ this.tabPage2.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.gridViewKnown)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ private System.Windows.Forms.TabControl tabControl;
+ private System.Windows.Forms.TabPage tabPage1;
+ private System.Windows.Forms.TabPage tabPage2;
+ private System.Windows.Forms.DataGridView gridView;
+ private System.Windows.Forms.DataGridView gridViewKnown;
+ }
+}
\ No newline at end of file
diff --git a/BedrockService/Client/Forms/PlayerManagerForm.cs b/BedrockService/Client/Forms/PlayerManagerForm.cs
new file mode 100644
index 00000000..f236cfce
--- /dev/null
+++ b/BedrockService/Client/Forms/PlayerManagerForm.cs
@@ -0,0 +1,60 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using System;
+using System.Windows.Forms;
+
+namespace BedrockService.Client.Forms
+{
+ public partial class PlayerManagerForm : Form
+ {
+ private readonly ServerInfo Server;
+ private readonly string[] RegisteredPlayerColumnArray = new string[] { "XUID:", "Username:", "Permission:", "Whitelisted:", "Ignores max players:", "First connected on:", "Last connected on:", "Time spent in game:" };
+ private readonly string[] KnownPlayerColumnArray = new string[] { "XUID:", "Username:", "Registered status:", "First connected on:", "Last connected on:", "Time spent in game:" };
+
+ public PlayerManagerForm(ServerInfo server)
+ {
+ InitializeComponent();
+ Server = server;
+ tabPage1.Text = "Registered players";
+ tabPage2.Text = "Known players";
+ tabControl.SelectedTab = tabPage1;
+
+ tabPage1.Enter += TabPage1_Enter;
+ tabPage2.Enter += TabPage2_Enter;
+ }
+
+ private void TabPage1_Enter(object sender, EventArgs e)
+ {
+ gridView.Columns.Clear();
+ gridView.Rows.Clear();
+ foreach (string s in RegisteredPlayerColumnArray)
+ {
+ gridView.Columns.Add(s.Replace(" ", "").Replace(":", ""), s);
+ }
+ foreach (Player player in Server.KnownPlayers)
+ {
+ TimeSpan timeSpent = TimeSpan.FromTicks(long.Parse(player.LastDisconnectTime) - long.Parse(player.LastConnectedTime));
+ string[] list = new string[] { player.XUID, player.Username, player.PermissionLevel, player.Whitelisted.ToString(), player.IgnorePlayerLimits.ToString(), player.FirstConnectedTime, player.LastConnectedTime, timeSpent.ToString("hhmmss") };
+ if (player.FromConfig)
+ gridView.Rows.Add(list);
+ }
+ gridView.Refresh();
+ }
+
+ private void TabPage2_Enter(object sender, EventArgs e)
+ {
+ gridViewKnown.Columns.Clear();
+ gridViewKnown.Rows.Clear();
+ foreach (string s in KnownPlayerColumnArray)
+ {
+ gridViewKnown.Columns.Add(s.Replace(" ", "").Replace(":", ""), s);
+ }
+ foreach (Player player in Server.KnownPlayers)
+ {
+ TimeSpan timeSpent = TimeSpan.FromTicks(long.Parse(player.LastDisconnectTime) - long.Parse(player.LastConnectedTime));
+ string[] list = new string[] { player.XUID, player.Username, player.FromConfig.ToString(), player.FirstConnectedTime, player.LastConnectedTime, timeSpent.ToString("hhmmss") };
+ gridViewKnown.Rows.Add(list);
+ }
+ gridViewKnown.Refresh();
+ }
+ }
+}
diff --git a/BedrockService/Client/Forms/PlayerManagerForm.resx b/BedrockService/Client/Forms/PlayerManagerForm.resx
new file mode 100644
index 00000000..1af7de15
--- /dev/null
+++ b/BedrockService/Client/Forms/PlayerManagerForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/BedrockService/Client/Management/ConfigManager.cs b/BedrockService/Client/Management/ConfigManager.cs
new file mode 100644
index 00000000..6597e25a
--- /dev/null
+++ b/BedrockService/Client/Management/ConfigManager.cs
@@ -0,0 +1,86 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace BedrockService.Client.Management
+{
+ class ConfigManager
+ {
+ public static string ConfigDir = $@"{Directory.GetCurrentDirectory()}\Client\Configs";
+ public static string ConfigFile = $@"{ConfigDir}\Config.conf";
+ public static List HostConnectList = new List();
+
+ public static void LoadConfigs()
+ {
+ if (!Directory.Exists(ConfigDir))
+ {
+ Directory.CreateDirectory(ConfigDir);
+ }
+ string[] files = Directory.GetFiles(ConfigDir, "*.conf");
+ string subPattern = @"^\[(\w*)\]$";
+ Regex regex = new Regex(subPattern);
+
+ if (files.Length > 0)
+ {
+ foreach (string file in files)
+ {
+ string[] lines = File.ReadAllLines(file);
+ foreach (string line in lines)
+ {
+ if (regex.Match(line).Groups[1].Value != "Hosts" || !string.IsNullOrEmpty(line) || !line.StartsWith("#"))
+ {
+ string[] split = line.Split('=');
+ if (split.Length == 2)
+ {
+ HostInfo hostToList = new HostInfo();
+ hostToList.SetGlobalsDefault();
+ string[] split2 = split[1].Split(':');
+ hostToList.HostName = split[0];
+ hostToList.Address = split2[0];
+ hostToList.SetGlobalProperty("ClientPort", split2[1]);
+ hostToList.HostDisplayName = $@"Host {split[0]} @ {split2[0]}:{split2[1]}";
+ HostConnectList.Add(hostToList);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Console.WriteLine("Config file missing! Regenerating default file...");
+ CreateDefaultConfig();
+ LoadConfigs();
+ }
+ }
+
+ public static void CreateDefaultConfig()
+ {
+ string[] Config = new string[]
+ {
+ "host1=127.0.0.1:19134"
+ };
+ StringBuilder builder = new StringBuilder();
+ builder.Append("[Hosts]\n");
+ foreach (string entry in Config)
+ {
+ builder.Append($"{entry}\n");
+ }
+ File.WriteAllText(ConfigFile, builder.ToString());
+ }
+
+ public static int[] GetPorts(string input)
+ {
+ string[] StrArr = input.Split(';');
+ int[] Output = new int[StrArr.Length];
+
+ for (int i = 0; i < StrArr.Length; i++)
+ {
+ Output[i] = Convert.ToInt32(StrArr[i]);
+ }
+ return Output;
+ }
+ }
+}
diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs
new file mode 100644
index 00000000..50d113a9
--- /dev/null
+++ b/BedrockService/Client/Management/FormManager.cs
@@ -0,0 +1,35 @@
+
+using BedrockService.Client.Forms;
+using BedrockService.Client.Networking;
+
+namespace BedrockService.Client.Management
+{
+ public sealed class FormManager
+ {
+ private static MainWindow main;
+ private static TCPClient client;
+ public static MainWindow GetMainWindow
+ {
+ get
+ {
+ if (main == null || main.IsDisposed)
+ {
+ main = new MainWindow();
+ }
+ return main;
+ }
+ }
+
+ public static TCPClient GetTCPClient
+ {
+ get
+ {
+ if (client == null)
+ {
+ client = new TCPClient();
+ }
+ return client;
+ }
+ }
+ }
+}
diff --git a/BedrockService/Client/Management/LogManager.cs b/BedrockService/Client/Management/LogManager.cs
new file mode 100644
index 00000000..9ed8d39b
--- /dev/null
+++ b/BedrockService/Client/Management/LogManager.cs
@@ -0,0 +1,126 @@
+using BedrockService.Client.Forms;
+using BedrockService.Service.Server.HostInfoClasses;
+using BedrockService.Service.Server.Logging;
+using System;
+using System.Text;
+using System.Threading;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace BedrockService.Client.Management
+{
+ class LogManager
+ {
+ public static Thread LogThread;
+ public static bool EnableFlag;
+ public static bool Working = false;
+ public static List ServiceLogs = new List();
+ private static HostInfo connectedHost;
+
+ private static void LogManagerTask()
+ {
+ while (FormManager.GetTCPClient.Connected)
+ {
+ try
+ {
+ Working = true;
+ StringBuilder sendString = new StringBuilder();
+ foreach (ServerInfo server in connectedHost.GetServerInfos())
+ {
+ server.ConsoleBuffer = server.ConsoleBuffer ?? new ServerLogger(server.ServerName);
+ sendString.Append($"{server.ServerName};{server.ConsoleBuffer.Count()}|");
+ }
+ sendString.Append($"Service;{connectedHost.ServiceLog.Count}");
+ byte[] stringsToBytes = Encoding.UTF8.GetBytes(sendString.ToString());
+ FormManager.GetTCPClient.SendData(stringsToBytes, Service.Networking.NetworkMessageSource.Client, Service.Networking.NetworkMessageDestination.Service, Service.Networking.NetworkMessageTypes.ConsoleLogUpdate);
+ Thread.Sleep(200);
+ MainWindow mainWindow = FormManager.GetMainWindow;
+
+ if (MainWindow.ShowsSvcLog)
+ {
+ mainWindow.LogBox.Invoke((MethodInvoker)delegate
+ {
+ mainWindow.LogBox.Text = connectedHost.ServiceLogToString();
+ mainWindow.LogBox.Select(mainWindow.LogBox.Text.Length - 1, 0);
+ mainWindow.LogBox.Refresh();
+
+ });
+ }
+ else
+ {
+ if (MainWindow.selectedServer == null)
+ {
+ mainWindow.Invoke((MethodInvoker)delegate
+ {
+ mainWindow.LogBox.Text = "";
+ mainWindow.LogBox.Refresh();
+ });
+ return;
+ }
+ else if (MainWindow.selectedServer.ConsoleBuffer != null && MainWindow.selectedServer.ConsoleBuffer.Count() != 0)
+ {
+
+ mainWindow.LogBox.Invoke((MethodInvoker)delegate
+ {
+ mainWindow.LogBox.Text = MainWindow.selectedServer.ConsoleBuffer.ToString();
+ mainWindow.LogBox.Select(mainWindow.LogBox.Text.Length - 1, 0);
+ mainWindow.LogBox.Refresh();
+
+ });
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"LogManager Error! Stacetrace: {e.StackTrace}");
+ }
+ }
+ }
+
+ public static bool InitLogThread(HostInfo host)
+ {
+ connectedHost = host;
+ return StartLogThread();
+ }
+
+ public static bool StartLogThread()
+ {
+ try
+ {
+ if (LogThread != null || (LogThread != null && LogThread.IsAlive))
+ {
+ return false;
+ }
+ LogThread = new Thread(new ThreadStart(LogManagerTask));
+ EnableFlag = true;
+ LogThread.Start();
+ Console.WriteLine("LogThread started");
+ return true;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Error starting LogThread: {e.StackTrace}");
+ }
+ return false;
+ }
+
+ public static bool StopLogThread()
+ {
+ if (LogThread == null)
+ {
+ return true;
+ }
+ try
+ {
+ LogThread.Abort();
+ }
+ catch (ThreadAbortException e)
+ {
+ Console.WriteLine(e.StackTrace);
+ }
+ Console.WriteLine("LogThread stopped");
+ LogThread = null;
+ return true;
+ }
+ }
+}
diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs
new file mode 100644
index 00000000..f9980b4e
--- /dev/null
+++ b/BedrockService/Client/Networking/TCPClient.cs
@@ -0,0 +1,264 @@
+using BedrockService.Client.Forms;
+using BedrockService.Client.Management;
+using BedrockService.Service.Networking;
+using BedrockService.Service.Server.HostInfoClasses;
+using BedrockService.Service.Server.Logging;
+using BedrockService.Utilities;
+using System;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+namespace BedrockService.Client.Networking
+{
+ public class TCPClient
+ {
+ public TcpClient OpenedTcpClient;
+ public string ClientName;
+ public NetworkStream stream;
+ public bool Connected;
+ public bool EnableRead;
+ public Thread ClientReciever;
+ public Thread HeartbeatThread;
+ private int heartbeatFailTimeout;
+ private int heartbeatFailTimeoutLimit = 200;
+ private bool heartbeatRecieved;
+
+ public bool ConnectHost(HostInfo host)
+ {
+ if (EstablishConnection(host.Address, int.Parse(host.GetGlobalValue("ClientPort"))))
+ {
+ SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Connect);
+ return true;
+ }
+ return false;
+ }
+
+ public bool EstablishConnection(string addr, int port)
+ {
+ Console.WriteLine("Connecting to Server");
+ try
+ {
+ EnableRead = false;
+ OpenedTcpClient = new TcpClient(addr, port);
+ stream = OpenedTcpClient.GetStream();
+ Connected = true;
+ ClientReciever = new Thread(new ThreadStart(ReceiveListener));
+ ClientReciever.Start();
+ }
+ catch
+ {
+ Console.WriteLine("Could not connect to Server");
+ return false;
+ }
+ return Connected;
+ }
+
+ public void CloseConnection()
+ {
+ try
+ {
+ stream.Dispose();
+ stream = null;
+ Connected = false;
+ }
+ catch (NullReferenceException)
+ {
+ Connected = false;
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Error closing connection: {e.StackTrace}");
+ }
+ }
+
+ public void ReceiveListener()
+ {
+ while (Connected)
+ {
+ try
+ {
+ byte[] buffer = new byte[4];
+ while (OpenedTcpClient != null && OpenedTcpClient.Client.Available != 0)
+ {
+ int byteCount = stream.Read(buffer, 0, 4);
+ int expectedLen = BitConverter.ToInt32(buffer, 0);
+ buffer = new byte[expectedLen];
+ byteCount = stream.Read(buffer, 0, expectedLen);
+ NetworkMessageSource source = (NetworkMessageSource)buffer[0];
+ NetworkMessageDestination destination = (NetworkMessageDestination)buffer[1];
+ NetworkMessageTypes msgType = (NetworkMessageTypes)buffer[2];
+ NetworkMessageStatus msgStatus = (NetworkMessageStatus)buffer[3];
+ string data = GetString(buffer);
+ if (destination != NetworkMessageDestination.Client)
+ continue;
+ int srvCurLen = 0;
+ switch (source)
+ {
+ case NetworkMessageSource.Service:
+ switch (msgType)
+ {
+ case NetworkMessageTypes.Connect:
+ try
+ {
+ JsonParser message = JsonParser.Deserialize(data);
+ if (message.Type == typeof(HostInfo))
+ {
+ Console.WriteLine($"{message.Value}");
+ Console.WriteLine("Connection to Host successful!");
+ MainWindow.connectedHost = message.Value.ToObject();
+ heartbeatFailTimeout = 0;
+ HeartbeatThread = new Thread(new ThreadStart(SendHeatbeatSignal))
+ {
+ IsBackground = true,
+ Name = "HeartBeatThread"
+ };
+ HeartbeatThread.Start();
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Error: ConnectMan reported error: {e.Message}\n{e.StackTrace}");
+ }
+ break;
+ case NetworkMessageTypes.Disconnect:
+ break;
+ case NetworkMessageTypes.Heartbeat:
+ heartbeatRecieved = true;
+ if (!HeartbeatThread.IsAlive)
+ {
+ HeartbeatThread = new Thread(new ThreadStart(SendHeatbeatSignal));
+ HeartbeatThread.IsBackground = true;
+ HeartbeatThread.Name = "HeartBeatThread";
+ HeartbeatThread.Start();
+ Thread.Sleep(500);
+ }
+ break;
+
+ }
+ break;
+ case NetworkMessageSource.Server:
+ switch (msgType)
+ {
+ case NetworkMessageTypes.ConsoleLogUpdate:
+ string[] strings = data.Split('|');
+ for (int i = 0; i < strings.Length; i++)
+ {
+ string[] srvSplit = strings[i].Split(';');
+ string srvName = srvSplit[0];
+ string srvText = srvSplit[1];
+ srvCurLen = int.Parse(srvSplit[2]);
+ if(srvName != "Service")
+ {
+ ServerInfo bedrockServer = MainWindow.connectedHost.GetServerInfos().First(srv => srv.ServerName == srvName);
+ bedrockServer.ConsoleBuffer = bedrockServer.ConsoleBuffer ?? new ServerLogger(bedrockServer.ServerName);
+ int curCount = bedrockServer.ConsoleBuffer.Count();
+ if (curCount == srvCurLen)
+ {
+ bedrockServer.ConsoleBuffer.Append(srvText);
+ }
+ }
+ else
+ {
+ int curCount = MainWindow.connectedHost.ServiceLog.Count;
+ if (curCount == srvCurLen)
+ {
+ MainWindow.connectedHost.ServiceLog.Add(srvText);
+ }
+ }
+ }
+ break;
+ case NetworkMessageTypes.Backup:
+ Console.WriteLine(msgStatus.ToString());
+ break;
+ }
+ break;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"TCPClient error! Stacktrace: {e.Message}\n{e.StackTrace}");
+ }
+ Thread.Sleep(200);
+ }
+ }
+
+ private string GetString(byte[] array) => Encoding.UTF8.GetString(array, 4, array.Length - 4);
+
+ public void SendHeatbeatSignal()
+ {
+ Console.WriteLine("HeartbeatThread started.");
+ while (Connected)
+ {
+ heartbeatRecieved = false;
+ SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat);
+ while (!heartbeatRecieved)
+ {
+ Thread.Sleep(100);
+ heartbeatFailTimeout++;
+ if (heartbeatFailTimeout > heartbeatFailTimeoutLimit)
+ {
+ FormManager.GetMainWindow.HeartbeatFailDisconnect();
+ HeartbeatThread.Abort();
+ heartbeatFailTimeout = 0;
+ }
+ }
+ // Console.WriteLine("ThumpThump");
+ heartbeatRecieved = false;
+ heartbeatFailTimeout = 0;
+ Thread.Sleep(3000);
+ }
+ }
+
+ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageStatus status)
+ {
+ byte[] compiled = new byte[8 + bytes.Length];
+ byte[] len = BitConverter.GetBytes(4 + bytes.Length);
+ Buffer.BlockCopy(len, 0, compiled, 0, 4);
+ compiled[4] = (byte)source;
+ compiled[5] = (byte)destination;
+ compiled[6] = (byte)type;
+ compiled[7] = (byte)status;
+ Buffer.BlockCopy(bytes, 0, compiled, 8, bytes.Length);
+ if (Connected)
+ {
+ try
+ {
+ stream.Write(compiled, 0, compiled.Length);
+ stream.Flush();
+ return true;
+
+ }
+ catch
+ {
+ Console.WriteLine("Error writing to network stream!");
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public bool SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type)
+ {
+ if (SendData(new byte[0], source, destination, type, NetworkMessageStatus.None))
+ return true;
+ return false;
+ }
+
+ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type)
+ {
+ if (SendData(bytes, source, destination, type, NetworkMessageStatus.None))
+ return true;
+ return false;
+ }
+
+ public bool SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageStatus status)
+ {
+ if (SendData(new byte[0], source, destination, type, status))
+ return true;
+ return false;
+ }
+ }
+}
diff --git a/BedrockService/Client/Properties/AssemblyInfo.cs b/BedrockService/Client/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..83c81492
--- /dev/null
+++ b/BedrockService/Client/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BedrockClientGUI")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BedrockClientGUI")]
+[assembly: AssemblyCopyright("Copyright © 2021")]
+[assembly: AssemblyTrademark("")]
+[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)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a2daf70a-925a-4f4b-b7ee-caace75d6740")]
+
+// Version information for an assembly consists of the following four values:
+//
+// 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")]
diff --git a/BedrockService/Client/Properties/Resources.Designer.cs b/BedrockService/Client/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..2764b906
--- /dev/null
+++ b/BedrockService/Client/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace BedrockService.Client.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BedrockService.Client.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/BedrockService/Client/Properties/Resources.resx b/BedrockService/Client/Properties/Resources.resx
new file mode 100644
index 00000000..af7dbebb
--- /dev/null
+++ b/BedrockService/Client/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/BedrockService/Client/Properties/Settings.Designer.cs b/BedrockService/Client/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..609bb426
--- /dev/null
+++ b/BedrockService/Client/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace BedrockService.Client.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/BedrockService/Client/Properties/Settings.settings b/BedrockService/Client/Properties/Settings.settings
new file mode 100644
index 00000000..39645652
--- /dev/null
+++ b/BedrockService/Client/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/BedrockService/Client/Utilities/Utilities.cs b/BedrockService/Client/Utilities/Utilities.cs
new file mode 100644
index 00000000..6a7e86fa
--- /dev/null
+++ b/BedrockService/Client/Utilities/Utilities.cs
@@ -0,0 +1,22 @@
+using BedrockService.Client.Management;
+using BedrockService.Service.Networking;
+using BedrockService.Utilities;
+using System;
+using System.Text;
+
+namespace BedrockService.Client.Utilities
+{
+ public static class JsonUtilities
+ {
+ public static string GetJsonString(object obj)
+ {
+ return JsonParser.Serialize(JsonParser.FromValue((T)obj));
+ }
+
+ public static bool SendJsonMsg(object obj, NetworkMessageDestination destination, NetworkMessageTypes type)
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(JsonParser.Serialize(JsonParser.FromValue((T)obj)));
+ return FormManager.GetTCPClient.SendData(bytes, NetworkMessageSource.Client, destination, type);
+ }
+ }
+}
diff --git a/BedrockService/Client/packages.config b/BedrockService/Client/packages.config
new file mode 100644
index 00000000..5eaa239b
--- /dev/null
+++ b/BedrockService/Client/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/BedrockService/Service/Batch/BS_InstallStart.bat b/BedrockService/Service/Batch/BS_InstallStart.bat
new file mode 100644
index 00000000..52247b3f
--- /dev/null
+++ b/BedrockService/Service/Batch/BS_InstallStart.bat
@@ -0,0 +1,2 @@
+bedrockservice install
+bedrockservice start
\ No newline at end of file
diff --git a/BedrockService/Service/Batch/BS_Restart.bat b/BedrockService/Service/Batch/BS_Restart.bat
new file mode 100644
index 00000000..91259374
--- /dev/null
+++ b/BedrockService/Service/Batch/BS_Restart.bat
@@ -0,0 +1,2 @@
+bedrockservice stop
+bedrockservice start
\ No newline at end of file
diff --git a/BedrockService/Service/Batch/BS_Start.bat b/BedrockService/Service/Batch/BS_Start.bat
new file mode 100644
index 00000000..df8c99ab
--- /dev/null
+++ b/BedrockService/Service/Batch/BS_Start.bat
@@ -0,0 +1 @@
+bedrockservice start
\ No newline at end of file
diff --git a/BedrockService/Service/Batch/BS_Stop.bat b/BedrockService/Service/Batch/BS_Stop.bat
new file mode 100644
index 00000000..e86f8e36
--- /dev/null
+++ b/BedrockService/Service/Batch/BS_Stop.bat
@@ -0,0 +1 @@
+bedrockservice stop
\ No newline at end of file
diff --git a/BedrockService/Service/Batch/BS_StopUninstall.bat b/BedrockService/Service/Batch/BS_StopUninstall.bat
new file mode 100644
index 00000000..edd78918
--- /dev/null
+++ b/BedrockService/Service/Batch/BS_StopUninstall.bat
@@ -0,0 +1,2 @@
+bedrockservice stop
+bedrockservice uninstall
\ No newline at end of file
diff --git a/BedrockService/Service/BedrockService.Service.csproj b/BedrockService/Service/BedrockService.Service.csproj
new file mode 100644
index 00000000..c6ff8bab
--- /dev/null
+++ b/BedrockService/Service/BedrockService.Service.csproj
@@ -0,0 +1,159 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}
+ Exe
+ BedrockService.Service
+ BedrockService.Service
+ v4.7.2
+ 512
+ true
+ true
+ false
+ C:\Users\brend_rxz2yiy\source\repos\BedrockService\Releases\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ true
+ 2
+ 1.0.0.%2a
+ false
+ true
+ true
+
+
+ x86
+ true
+ full
+ false
+ ..\bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ EE2403F7477A35F08B98B0A8FB2404C95BE04FEB
+
+
+ BedrockService_TemporaryKey.pfx
+
+
+ true
+
+
+ false
+
+
+
+ ..\packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll
+
+
+ ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll
+
+
+ ..\packages\NCrontab.Signed.3.3.2\lib\net35\NCrontab.Signed.dll
+
+
+ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+ ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
+ True
+ True
+
+
+
+
+
+
+
+
+
+
+
+ ..\packages\Topshelf.4.2.1\lib\net452\Topshelf.dll
+
+
+ ..\packages\Topshelf.Log4Net.4.2.1\lib\net452\Topshelf.Log4Net.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+ False
+ Microsoft .NET Framework 4.7.2 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BedrockService/Service/BedrockService.cs b/BedrockService/Service/BedrockService.cs
new file mode 100644
index 00000000..495233ba
--- /dev/null
+++ b/BedrockService/Service/BedrockService.cs
@@ -0,0 +1,389 @@
+using BedrockService.Service.Networking;
+using BedrockService.Service.Server;
+using BedrockService.Service.Server.HostInfoClasses;
+using NCrontab;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Timers;
+using Topshelf;
+using Topshelf.Logging;
+
+namespace BedrockService.Service
+{
+ public class BedrockService : HostInfo, ServiceControl
+ {
+ public List bedrockServers = new List();
+ readonly LogWriter _log = HostLogger.Get();
+ public HostControl _hostControl;
+
+ public enum ServiceStatus
+ {
+ Stopped,
+ Starting,
+ Started,
+ Stopping
+ }
+
+ public ServiceStatus CurrentServiceStatus;
+ private System.Timers.Timer updaterTimer;
+ private System.Timers.Timer cronTimer;
+ readonly CrontabSchedule shed;
+ Thread TCPServerThread;
+ private CrontabSchedule updater;
+ readonly TCPListener ClientHost = new TCPListener();
+
+ public BedrockService()
+ {
+ CurrentServiceStatus = ServiceStatus.Starting;
+ if (LoadInit())
+ {
+ //Program.ServiceLogger = new Program.ServiceLogger(localHost);
+ TCPServerThread = new Thread(new ThreadStart(ClientHostThread));
+ TCPServerThread.Start();
+ try
+ {
+ foreach (ServerInfo Server in InstanceProvider.GetHostInfo().GetServerInfos())
+ {
+ BedrockServer srv = new BedrockServer(Server);
+ shed = CrontabSchedule.TryParse(InstanceProvider.GetHostInfo().GetGlobalValue("BackupCron"));
+ if (InstanceProvider.GetHostInfo().GetGlobalValue("BackupEnabled") == "true" && shed != null)
+ {
+ cronTimer = new System.Timers.Timer((shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds);
+ cronTimer.Elapsed += CronTimer_Elapsed;
+ cronTimer.Start();
+ }
+
+ updater = CrontabSchedule.TryParse(InstanceProvider.GetHostInfo().GetGlobalValue("UpdateCron"));
+ if (InstanceProvider.GetHostInfo().GetGlobalValue("CheckUpdates") == "true" && updater != null)
+ {
+ updaterTimer = new System.Timers.Timer((updater.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds);
+ updaterTimer.Elapsed += UpdateTimer_Elapsed;
+ InstanceProvider.GetServiceLogger().AppendLine($"Updates Enabled, will be checked in: {((float)updaterTimer.Interval / 1000)} seconds.");
+ updaterTimer.Start();
+ }
+ bedrockServers.Add(srv);
+ }
+ CurrentServiceStatus = ServiceStatus.Started;
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error Instantiating BedrockServiceWrapper: {e.StackTrace}");
+ }
+ }
+ }
+
+ ///
+ /// A utility class to determine a process parent.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ParentProcessUtilities
+ {
+ // These members must match PROCESS_BASIC_INFORMATION
+ internal IntPtr Reserved1;
+ internal IntPtr PebBaseAddress;
+ internal IntPtr Reserved2_0;
+ internal IntPtr Reserved2_1;
+ internal IntPtr UniqueProcessId;
+ internal IntPtr InheritedFromUniqueProcessId;
+
+ [DllImport("ntdll.dll")]
+ private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
+
+ ///
+ /// Gets the parent process of the current process.
+ ///
+ /// An instance of the Process class.
+ public static Process GetParentProcess()
+ {
+ return GetParentProcess(Process.GetCurrentProcess().Handle);
+ }
+
+ ///
+ /// Gets the parent process of specified process.
+ ///
+ /// The process id.
+ /// An instance of the Process class.
+ public static Process GetParentProcess(int id)
+ {
+ Process process = Process.GetProcessById(id);
+ return GetParentProcess(process.Handle);
+ }
+
+ ///
+ /// Gets the parent process of a specified process.
+ ///
+ /// The process handle.
+ /// An instance of the Process class.
+ public static Process GetParentProcess(IntPtr handle)
+ {
+ ParentProcessUtilities pbi = new ParentProcessUtilities();
+ int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out int returnLength);
+ if (status != 0)
+ throw new Win32Exception(status);
+
+ try
+ {
+ return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
+ }
+ catch (ArgumentException)
+ {
+ // not found
+ return null;
+ }
+ }
+ }
+
+ private void ClientHostThread()
+ {
+ try
+ {
+ ClientHost.StartListening(int.Parse((string)InstanceProvider.GetHostInfo().GetGlobalValue("ClientPort")));
+ InstanceProvider.GetServiceLogger().AppendLine("Before process.WaitForExit()");
+ ParentProcessUtilities.GetParentProcess().WaitForExit();
+ InstanceProvider.GetServiceLogger().AppendLine("After process.WaitForExit()");
+
+ InstanceProvider.GetServiceLogger().AppendLine("Stop socket service");
+ ClientHost.client.Close();
+ GC.Collect();
+ }
+ catch (ThreadAbortException abort)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"WCF Thread reports {abort.Message}");
+ }
+ }
+
+ private void CronTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ try
+ {
+ if (cronTimer != null)
+ {
+ cronTimer.Stop();
+ cronTimer = null;
+ }
+ if ((string)InstanceProvider.GetHostInfo().GetGlobalValue("BackupEnabled") == "true" && shed != null)
+ {
+ Backup();
+
+ cronTimer = new System.Timers.Timer((shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds);
+ cronTimer.Elapsed += CronTimer_Elapsed;
+ cronTimer.Start();
+ }
+ }
+ catch (Exception ex)
+ {
+ _log.Error("Error in BackupTimer_Elapsed", ex);
+ }
+ }
+
+ private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ try
+ {
+ if (updaterTimer != null)
+ {
+ updaterTimer.Stop();
+ updaterTimer = null;
+ }
+ Task task = Updater.CheckUpdates();
+ task.Wait();
+ if (InstanceProvider.GetHostInfo().GetGlobalValue("CheckUpdates") == "true" && updater != null && task.Result)
+ {
+ if (Updater.VersionChanged)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Version change detected! Restarting server(s) to apply update...");
+ if (Stop(_hostControl))
+ {
+ Start(_hostControl);
+ }
+ }
+
+ updaterTimer = new System.Timers.Timer((updater.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds);
+ updaterTimer.Elapsed += UpdateTimer_Elapsed;
+ updaterTimer.Start();
+ }
+ }
+ catch (Exception ex)
+ {
+ _log.Error("Error in UpdateTimer_Elapsed", ex);
+ }
+ }
+
+ private void Backup()
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Service started backup manager.");
+ foreach (var brs in bedrockServers.OrderByDescending(t => t.serverInfo.Primary).ToList())
+ {
+ brs.CurrentServerStatus = BedrockServer.ServerStatus.Stopping;
+ while (brs.CurrentServerStatus == BedrockServer.ServerStatus.Stopping)
+ {
+ Thread.Sleep(100);
+ }
+ }
+
+ foreach (var brs in bedrockServers.OrderByDescending(t => t.serverInfo.Primary).ToList())
+ {
+ if (brs.CurrentServerStatus == BedrockServer.ServerStatus.Stopped) brs.Backup();
+
+ }
+ foreach (var brs in bedrockServers.OrderByDescending(t => t.serverInfo.Primary).ToList())
+ {
+
+ brs.StartControl(_hostControl);
+ Thread.Sleep(2000);
+
+ }
+ InstanceProvider.GetServiceLogger().AppendLine("Backups have been completed.");
+ }
+
+ public bool Stop(HostControl hostControl)
+ {
+
+ CurrentServiceStatus = ServiceStatus.Stopping;
+ _hostControl = hostControl;
+ try
+ {
+ foreach (var brs in bedrockServers)
+ {
+ brs.CurrentServerStatus = BedrockServer.ServerStatus.Stopping;
+ while (brs.CurrentServerStatus == BedrockServer.ServerStatus.Stopping)
+ {
+ Thread.Sleep(100);
+ }
+ }
+ return true;
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error Stopping BedrockServiceWrapper {e.StackTrace}");
+ return false;
+ }
+ }
+
+ public BedrockServer GetBedrockServer(string name)
+ {
+ return bedrockServers.FirstOrDefault(brs => brs.serverInfo.ServerName == name);
+ }
+
+ public bool Start(HostControl hostControl)
+ {
+ _hostControl = hostControl;
+ try
+ {
+ ValidSettingsCheck();
+
+ foreach (var brs in bedrockServers.OrderByDescending(t => t.serverInfo.Primary).ToList())
+ {
+ _hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30));
+ brs.CurrentServerStatus = BedrockServer.ServerStatus.Starting;
+ brs.StartWatchdog(hostControl);
+ Thread.Sleep(2000);
+ }
+ return true;
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error Starting BedrockServiceWrapper {e.StackTrace}");
+ return false;
+ }
+ }
+
+ private void ValidSettingsCheck()
+ {
+ bool validating = true;
+ while (validating)
+ {
+ if (InstanceProvider.GetHostInfo().GetServerInfos().Count() < 1)
+ {
+ throw new Exception("No Servers Configured");
+ }
+ else
+ {
+ foreach (ServerInfo server in InstanceProvider.GetHostInfo().GetServerInfos())
+ {
+ foreach (ServerInfo compareServer in InstanceProvider.GetHostInfo().GetServerInfos())
+ {
+ if (server != compareServer)
+ {
+ if (server.ServerExeName.Equals(compareServer.ServerExeName) || server.ServerPath.Equals(compareServer.ServerPath) || server.GetServerProp("server-port").Value.Equals(compareServer.GetServerProp("server-port").Value) || server.GetServerProp("server-portv6").Value.Equals(compareServer.GetServerProp("server-portv6").Value) || server.GetServerProp("server-name").Value.Equals(compareServer.GetServerProp("server-name").Value))
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Duplicate server settings detected for: {server.ServerName}");
+ }
+ }
+ }
+ }
+ foreach (var server in InstanceProvider.GetHostInfo().GetServerInfos())
+ {
+ if (Updater.VersionChanged)
+ {
+ ReplaceBuild(server);
+ }
+ if (server.ServerExeName.Value != "bedrock_server.exe" && File.Exists(server.ServerPath.Value + "\\bedrock_server.exe") && !File.Exists(server.ServerPath.Value + "\\" + server.ServerExeName.Value))
+ {
+ File.Copy(server.ServerPath.Value + "\\bedrock_server.exe", server.ServerPath.Value + "\\" + server.ServerExeName.Value);
+ }
+
+ if (!File.Exists(server.ServerPath.Value + "\\" + server.ServerExeName.Value))
+ {
+ ReplaceBuild(server);
+ }
+
+
+ }
+ if (Updater.VersionChanged)
+ Updater.VersionChanged = false;
+ else
+ validating = false;
+ }
+ }
+
+ }
+
+ private static void ReplaceBuild(ServerInfo server)
+ {
+ try
+ {
+ if (Directory.Exists(server.ServerPath.Value))
+ DeleteFilesRecursively(new DirectoryInfo(server.ServerPath.Value));
+ ZipFile.ExtractToDirectory($@"{Program.ServiceDirectory}\Server\MCSFiles\Update.zip", server.ServerPath.Value);
+ if (server.ServerExeName.Value != "bedrock_server.exe")
+ File.Copy(server.ServerPath.Value + "\\bedrock_server.exe", server.ServerPath.Value + "\\" + server.ServerExeName.Value);
+ InstanceProvider.GetConfigManager().WriteJSONFiles(server);
+ InstanceProvider.GetConfigManager().SaveServerProps(server, false);
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"ERROR: Got an exception deleting entire directory! {e.Message}");
+ }
+ }
+
+ private static void DeleteFilesRecursively(DirectoryInfo source)
+ {
+ try
+ {
+ source.Delete(true);
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($@"Error Deleting Dir: {e.StackTrace}");
+ }
+ }
+
+ private bool LoadInit()
+ {
+ if (InstanceProvider.GetConfigManager().LoadConfigs())
+ {
+ _ = Updater.CheckUpdates();
+ }
+ return true;
+ }
+ }
+}
diff --git a/BedrockService/Service/Configs/Default.conf b/BedrockService/Service/Configs/Default.conf
new file mode 100644
index 00000000..51416cf3
--- /dev/null
+++ b/BedrockService/Service/Configs/Default.conf
@@ -0,0 +1,36 @@
+# Service
+ServerPath=C:\Program Files (x86)\Minecraft Bedrock Server Launcher\Servers\Server
+ServerExeName=bedrock_server.exe
+BackupPath=Default
+AdvancedBackup=false
+MaxBackupCount=25
+LogToFile=false
+
+# Server
+server-name=Default
+gamemode=creative
+difficulty=easy
+allow-cheats=false
+max-players=10
+online-mode=true
+white-list=false
+server-port=19132
+server-portv6=19133
+view-distance=32
+tick-distance=4
+player-idle-timeout=30
+max-threads=8
+level-name=Bedrock level
+level-seed=
+default-player-permission-level=member
+texturepack-required=false
+content-log-file-enabled=false
+compression-threshold=1
+server-authoritative-movement=server-auth
+player-movement-score-threshold=20
+player-movement-distance-threshold=0.3
+player-movement-duration-threshold-in-ms=500
+correct-player-movement=false
+
+# StartCmds
+AddStartCmd=help 1
\ No newline at end of file
diff --git a/BedrockService/Service/Configs/Default.players b/BedrockService/Service/Configs/Default.players
new file mode 100644
index 00000000..b2ddaf2f
--- /dev/null
+++ b/BedrockService/Service/Configs/Default.players
@@ -0,0 +1,5 @@
+# Registered player list
+# Register player entries: xuid,username,permission,isWhitelisted,ignoreMaxPlayers
+# Example: TestUser,555111222333444,visitor,false,false
+
+1234111222333444,TestUser,visitor,false,false
\ No newline at end of file
diff --git a/BedrockService/Service/Configs/Globals.conf b/BedrockService/Service/Configs/Globals.conf
new file mode 100644
index 00000000..1e5b74ee
--- /dev/null
+++ b/BedrockService/Service/Configs/Globals.conf
@@ -0,0 +1,7 @@
+#Globals
+BackupEnabled=false
+BackupCron=0 1 * * *
+AcceptedMojangLic=false
+CheckUpdates=true
+UpdateCron=38 19 * * *
+ClientPort=19134
diff --git a/BedrockService/Service/InstanceProvider.cs b/BedrockService/Service/InstanceProvider.cs
new file mode 100644
index 00000000..59a610e8
--- /dev/null
+++ b/BedrockService/Service/InstanceProvider.cs
@@ -0,0 +1,148 @@
+using BedrockService.Service.Logging;
+using BedrockService.Service.Management;
+using BedrockService.Service.Server;
+using BedrockService.Service.Server.HostInfoClasses;
+using BedrockService.Service.Server.Management;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace BedrockService.Service
+{
+ public static class InstanceProvider
+ {
+ private static BedrockService bedrockService;
+ private static ConfigManager configManager;
+ private static ServiceLogger serviceLogger;
+ private static Thread clientservice;
+ private static Thread heartbeatThread;
+ private static TcpListener tcpListener;
+ private static IPAddress storedAddress;
+ private static HostInfo hostInfo;
+ private static PlayerManager playerManager;
+ private static int storedPort;
+
+ public static BedrockService GetBedrockService()
+ {
+ if (bedrockService == null)
+ {
+ bedrockService = new BedrockService();
+ }
+ return bedrockService;
+ }
+
+ public static BedrockServer GetBedrockServer(string serverName) => GetBedrockService().bedrockServers.FirstOrDefault(srv => srv.serverInfo.ServerName == serverName);
+
+ public static HostInfo GetHostInfo()
+ {
+ if (hostInfo == null)
+ {
+ hostInfo = new HostInfo();
+ }
+ return hostInfo;
+ }
+
+ public static PlayerManager GetPlayerManager()
+ {
+ if (playerManager == null)
+ {
+ playerManager = new PlayerManager();
+ }
+ return playerManager;
+ }
+
+ public static ConfigManager GetConfigManager()
+ {
+ if (configManager == null)
+ {
+ configManager = new ConfigManager();
+ }
+ return configManager;
+ }
+
+ public static ServiceLogger GetServiceLogger()
+ {
+ if (serviceLogger == null)
+ {
+ serviceLogger = new ServiceLogger(true);
+ }
+ return serviceLogger;
+ }
+
+ public static TcpListener InitTCPListener(IPAddress address, int port)
+ {
+ storedAddress = address;
+ storedPort = port;
+ tcpListener = null;
+ tcpListener = new TcpListener(storedAddress, storedPort);
+ return tcpListener;
+ }
+
+ public static TcpListener GetTcpListener()
+ {
+ if (tcpListener != null)
+ return tcpListener;
+ return null;
+ }
+
+ public static Thread InitClientService(ThreadStart threadStart)
+ {
+ if (clientservice == null || clientservice.ThreadState == ThreadState.Stopped || clientservice.ThreadState == ThreadState.Aborted)
+ {
+ clientservice = new Thread(threadStart)
+ {
+ Name = "ClientService",
+ IsBackground = true
+ };
+ }
+ return clientservice;
+ }
+
+ public static Thread GetClientService() => clientservice;
+
+ public static bool GetClientServiceAlive() => clientservice != null && clientservice.IsAlive;
+
+ public static void DisposeClientService()
+ {
+ if (clientservice != null)
+ {
+ if (clientservice.IsAlive)
+ {
+ clientservice.Abort();
+ }
+ clientservice = null;
+ }
+ }
+
+ public static Thread InitHeartbeatThread(ThreadStart threadStart)
+ {
+ if (heartbeatThread == null || heartbeatThread.ThreadState == ThreadState.Stopped || heartbeatThread.ThreadState == ThreadState.Aborted)
+ {
+ heartbeatThread = new Thread(threadStart)
+ {
+ Name = "HeartbeatThread",
+ IsBackground = true
+ };
+ }
+ return heartbeatThread;
+ }
+
+ public static Thread GetHeartbeatThread() => heartbeatThread;
+
+ public static bool GetHeartbeatThreadAlive() => heartbeatThread != null && heartbeatThread.IsAlive;
+
+
+ public static void DisposeHeartbeatThread()
+ {
+ if (heartbeatThread != null)
+ {
+ if (heartbeatThread.IsAlive)
+ {
+ heartbeatThread.Abort();
+ }
+ heartbeatThread = null;
+ }
+ }
+ }
+}
diff --git a/BedrockService/Service/Logging/ServiceLogger.cs b/BedrockService/Service/Logging/ServiceLogger.cs
new file mode 100644
index 00000000..5ea2019a
--- /dev/null
+++ b/BedrockService/Service/Logging/ServiceLogger.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace BedrockService.Service.Logging
+{
+ public class ServiceLogger
+ {
+ public List Log = new List();
+ public StringBuilder OutString = new StringBuilder();
+ public StreamWriter LogWriter;
+ private bool IsServer;
+ public string LogDir = $@"{Program.ServiceDirectory}\Service\ServiceLogs";
+
+ public ServiceLogger(bool isServer)
+ {
+ IsServer = isServer;
+ if (isServer)
+ {
+ if (!Directory.Exists(LogDir))
+ Directory.CreateDirectory(LogDir);
+ LogWriter = new StreamWriter($@"{LogDir}\ServiceLog_{DateTime.Now:yyyymmddhhmmss}.log", true);
+ }
+ }
+
+ public void AppendLine(string text)
+ {
+ string addText = $"Service: {text}\r\n";
+ Log.Add(addText);
+ LogWriter.WriteLine(text);
+ LogWriter.Flush();
+ if(IsServer)
+ Console.WriteLine(text);
+ }
+
+ public int Count()
+ {
+ return Log.Count;
+ }
+
+ public string FromIndex(int index)
+ {
+ return Log[index];
+ }
+
+ public override string ToString()
+ {
+ OutString = new StringBuilder();
+ foreach (string s in Log)
+ {
+ OutString.Append(s);
+ }
+ return OutString.ToString();
+ }
+ }
+}
diff --git a/BedrockService/Service/Management/ConfigManager.cs b/BedrockService/Service/Management/ConfigManager.cs
new file mode 100644
index 00000000..3693f03a
--- /dev/null
+++ b/BedrockService/Service/Management/ConfigManager.cs
@@ -0,0 +1,339 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace BedrockService.Service.Management
+{
+ public class ConfigManager
+ {
+ private string configDir = $@"{Program.ServiceDirectory}\Server\Configs"; // Get Executable directory for the root.
+ private string globalFile;
+ private string loadedVersion;
+ private static readonly object FileLock = new object();
+
+ public ConfigManager()
+ {
+ globalFile = $@"{configDir}\Globals.conf";
+ if (!Directory.Exists($@"{configDir}\Backups"))
+ Directory.CreateDirectory($@"{configDir}\Backups");
+ }
+
+ public bool LoadConfigs()
+ {
+ bool loading = true;
+ if (!Directory.Exists(configDir))
+ {
+ Directory.CreateDirectory(configDir);
+ }
+ if (File.Exists($@"{configDir}\..\bedrock_ver.ini"))
+ loadedVersion = File.ReadAllText($@"{configDir}\..\bedrock_ver.ini");
+
+ string[] files = Directory.GetFiles(configDir, "*.conf");
+ string globFileResult = null;
+ foreach (string file in files)
+ {
+ if (file.EndsWith("Globals.conf"))
+ {
+ globFileResult = file;
+ InstanceProvider.GetServiceLogger().AppendLine("Loading Globals...");
+ InstanceProvider.GetHostInfo().SetGlobalsDefault();
+ string[] lines = File.ReadAllLines(file);
+ foreach (string line in lines)
+ {
+ if (!line.StartsWith("#") && !string.IsNullOrEmpty(line))
+ {
+ string[] split = line.Split('=');
+ if (split.Length == 1)
+ {
+ split[1] = "";
+ }
+ InstanceProvider.GetHostInfo().GetGlobals().First(prop => prop.KeyName == split[0]).Value = split[1];
+ }
+ }
+ }
+ }
+ if (globFileResult == null)
+ {
+ InstanceProvider.GetHostInfo().SetGlobalsDefault();
+ InstanceProvider.GetServiceLogger().AppendLine("Globals.conf was not found. Loaded defaults and saved to file.");
+ SaveGlobalFile();
+ }
+ InstanceProvider.GetHostInfo().ClearServerInfos();
+ ServerInfo serverInfo = new ServerInfo();
+ serverInfo.InitDefaults();
+ while (loading)
+ {
+ foreach (string file in files)
+ {
+ FileInfo FInfo = new FileInfo(file);
+ if (FInfo.Name == "Globals.conf")
+ continue;
+ serverInfo = new ServerInfo();
+ serverInfo.InitDefaults();
+ serverInfo.FileName = FInfo.Name;
+ serverInfo.ServerVersion = loadedVersion;
+ string[] linesArray = File.ReadAllLines(file);
+ foreach (string line in linesArray)
+ {
+ if (!line.StartsWith("#") && !string.IsNullOrEmpty(line))
+ {
+ string[] split = line.Split('=');
+ if (split.Length == 1)
+ {
+ split = new string[] { split[0], "" };
+ }
+ Property SrvProp = serverInfo.ServerPropList.FirstOrDefault(prop => prop.KeyName == split[0]);
+ if (SrvProp != null)
+ {
+ serverInfo.SetServerProp(split[0], split[1]);
+ }
+ switch (split[0])
+ {
+ case "server-name":
+ InstanceProvider.GetServiceLogger().AppendLine($"Loading ServerInfo for server {split[1]}...");
+ serverInfo.ServerName = split[1];
+ LoadPlayerDatabase(serverInfo);
+ LoadRegisteredPlayers(serverInfo);
+ break;
+
+ case "AddStartCmd":
+ serverInfo.StartCmds.Add(new StartCmdEntry(split[1]));
+ break;
+
+ case "ServerPath":
+ serverInfo.ServerPath.Value = split[1];
+ break;
+
+ case "ServerExeName":
+ serverInfo.ServerExeName.Value = split[1];
+ break;
+
+ case "BackupPath":
+ serverInfo.BackupPath.Value = split[1];
+ break;
+
+ case "LogToFile":
+ serverInfo.LogToFileEnabled.Value = split[1];
+ break;
+
+ case "AdvancedBackup":
+ serverInfo.AdvancedBackup.Value = split[1];
+ break;
+
+ case "MaxBackupCount":
+ serverInfo.MaxBackupCount.Value = split[1];
+ break;
+ }
+ }
+ }
+ InstanceProvider.GetHostInfo().GetServerInfos().Add(serverInfo);
+ }
+ if (InstanceProvider.GetHostInfo().GetServerInfos().Count == 0)
+ {
+ SaveServerProps(serverInfo, true);
+ files = Directory.GetFiles(configDir, "*.conf");
+ }
+ else
+ {
+ loading = false;
+ }
+ }
+ return true;
+ }
+
+ public void SaveGlobalFile()
+ {
+ string[] output = new string[InstanceProvider.GetHostInfo().GetGlobals().Count + 3];
+ int index = 0;
+ output[index++] = "#Globals";
+ output[index++] = string.Empty;
+ foreach (Property prop in InstanceProvider.GetHostInfo().GetGlobals())
+ {
+ output[index++] = $"{prop.KeyName}={prop.Value}";
+ }
+ output[index++] = string.Empty;
+
+ File.WriteAllLines(globalFile, output);
+ }
+
+ public ServerInfo LoadRegisteredPlayers(ServerInfo server)
+ {
+ string filePath = $@"{configDir}\{server.ServerName}.players";
+ if (!File.Exists(filePath))
+ {
+ File.Create(filePath).Close();
+ return server;
+ }
+ foreach (string entry in File.ReadAllLines(filePath))
+ {
+ if (entry.StartsWith("#") || string.IsNullOrWhiteSpace(entry))
+ continue;
+ string[] split = entry.Split(',');
+ InstanceProvider.GetServiceLogger().AppendLine($"Server \"{server.ServerName}\" Loaded registered player: {split[1]}");
+ Player playerFound = server.KnownPlayers.FirstOrDefault(ply => ply.XUID == split[0]);
+ if (playerFound == null)
+ {
+ server.KnownPlayers.Add(new Player(split[0], split[1], DateTime.Now.Ticks.ToString(), "0", "0", split[2] == "true", split[3], split[4] == "true", true));
+ continue;
+ }
+ InstanceProvider.GetPlayerManager().UpdatePlayerFromCfg(split[0], split[1], split[2], split[3], split[4], server);
+ }
+ return server;
+ }
+
+ public ServerInfo LoadPlayerDatabase(ServerInfo server)
+ {
+ string filePath = $@"{configDir}\{server.ServerName}.playerdb";
+ if (!File.Exists(filePath))
+ {
+ File.Create(filePath).Close();
+ return server;
+ }
+ foreach (string entry in File.ReadAllLines(filePath))
+ {
+ if (entry.StartsWith("#") || string.IsNullOrWhiteSpace(entry))
+ continue;
+ string[] split = entry.Split(',');
+ InstanceProvider.GetServiceLogger().AppendLine($"Server \"{server.ServerName}\" Loaded known player: {split[1]}");
+ server.KnownPlayers.Add(new Player(split[0], split[1], split[2], split[3], split[4]));
+ }
+ return server;
+ }
+
+ public void SaveKnownPlayerDatabase(ServerInfo server)
+ {
+ lock (FileLock)
+ {
+ string filePath = $@"{configDir}\{server.ServerName}.playerdb";
+ if (File.Exists(filePath))
+ {
+ File.Copy(filePath, $@"{configDir}\{server.ServerName}_{DateTime.Now.ToString("mmddyyhhmmssff")}.dbbak", true);
+ }
+ TextWriter writer = new StreamWriter(filePath);
+ foreach (Player entry in server.KnownPlayers)
+ {
+ writer.WriteLine($"{entry.XUID},{entry.Username},{entry.FirstConnectedTime},{entry.LastConnectedTime},{entry.LastDisconnectTime}");
+ }
+ writer.Flush();
+ writer.Close();
+ }
+ }
+
+ public void SaveRegisteredPlayers(ServerInfo server)
+ {
+ lock (FileLock)
+ {
+ string filePath = $@"{configDir}\{server.ServerName}.players";
+ if (File.Exists(filePath))
+ {
+ File.Copy(filePath, $@"{configDir}\{server.ServerName}_{DateTime.Now.ToString("mmddyyhhmmssff")}.bak", true);
+ }
+ TextWriter writer = new StreamWriter(filePath);
+ foreach (Player entry in server.KnownPlayers)
+ {
+ writer.WriteLine($"{entry.XUID},{entry.Username},{entry.FirstConnectedTime},{entry.LastConnectedTime},{entry.LastDisconnectTime}");
+ }
+ writer.Flush();
+ writer.Close();
+ }
+ }
+
+
+ public void WriteJSONFiles(ServerInfo server)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("[\n");
+
+ foreach (Player player in server.KnownPlayers)
+ {
+ if (player.FromConfig)
+ {
+ sb.Append("\t{\n");
+ sb.Append($"\t\t\"ignoresPlayerLimit\": {player.IgnorePlayerLimits}\n");
+ sb.Append($"\t\t\"name\": \"{player.Username}\",\n");
+ sb.Append($"\t\t\"xuid\": \"{player.XUID}\",\n");
+ sb.Append("\t},\n");
+ }
+ }
+ if (sb.Length > 2)
+ {
+ sb.Remove(sb.Length - 2, 2);
+ }
+ sb.Append("\n]");
+
+ File.WriteAllText($@"{server.ServerPath.Value}\whitelist.json", sb.ToString());
+
+ sb = new StringBuilder();
+ sb.Append("[\n");
+
+ foreach (Player player in server.KnownPlayers)
+ {
+ if (player.FromConfig)
+ {
+ sb.Append("\t{\n");
+ sb.Append($"\t\t\"permission\": \"{player.PermissionLevel}\",\n");
+ sb.Append($"\t\t\"xuid\": \"{player.XUID}\"\n");
+ sb.Append("\t},\n");
+ }
+ }
+
+ if (sb.Length > 2)
+ {
+ sb.Remove(sb.Length - 2, 2);
+ }
+ sb.Append("\n]");
+ File.WriteAllText($@"{server.ServerPath.Value}\permissions.json", sb.ToString());
+ }
+
+ public void SaveServerProps(ServerInfo server, bool SaveServerInfo)
+ {
+ int index = 0;
+ string[] output = new string[13 + server.ServerPropList.Count + server.StartCmds.Count];
+ output[index++] = "#Server";
+ foreach (Property prop in server.ServerPropList)
+ {
+ output[index++] = $"{prop.KeyName}={prop.Value}";
+ }
+ if (!SaveServerInfo)
+ {
+ if (!Directory.Exists(server.ServerPath.Value))
+ {
+ Directory.CreateDirectory(server.ServerPath.Value);
+ }
+ File.WriteAllLines($@"{server.ServerPath.Value}\server.properties", output);
+ }
+ else
+ {
+ output[index++] = string.Empty;
+ output[index++] = "#Service";
+ output[index++] = string.Empty;
+ output[index++] = $"{server.ServerPath.KeyName}={server.ServerPath.Value}";
+ output[index++] = $"{server.ServerExeName.KeyName}={server.ServerExeName.Value}";
+ output[index++] = $"{server.BackupPath.KeyName}={server.BackupPath.Value}";
+ output[index++] = $"{server.AdvancedBackup.KeyName}={server.AdvancedBackup.Value}";
+ output[index++] = $"{server.MaxBackupCount.KeyName}={server.MaxBackupCount.Value}";
+ output[index++] = $"{server.LogToFileEnabled.KeyName}={server.LogToFileEnabled.Value}";
+ output[index++] = string.Empty;
+ output[index++] = "#StartCmds";
+
+ foreach (StartCmdEntry startCmd in server.StartCmds)
+ {
+ output[index++] = $"AddStartCmd={startCmd.Command}";
+ }
+ output[index++] = string.Empty;
+
+ File.WriteAllLines($@"{configDir}\{server.FileName}", output);
+ if (server.ServerPath.Value == null)
+ server.ServerPath.Value = server.ServerPath.DefaultValue;
+ if (!Directory.Exists(server.ServerPath.Value))
+ {
+ Directory.CreateDirectory(server.ServerPath.Value);
+ }
+ File.WriteAllLines($@"{server.ServerPath.Value}\server.properties", output);
+ }
+ }
+ }
+}
+
diff --git a/BedrockService/Service/Networking/Enums.cs b/BedrockService/Service/Networking/Enums.cs
new file mode 100644
index 00000000..d9ae17dd
--- /dev/null
+++ b/BedrockService/Service/Networking/Enums.cs
@@ -0,0 +1,35 @@
+namespace BedrockService.Service.Networking
+{
+ public enum NetworkMessageSource
+ {
+ Client,
+ Server,
+ Service
+ }
+
+
+ public enum NetworkMessageDestination
+ {
+ Client,
+ Server,
+ Service
+ }
+
+ public enum NetworkMessageTypes
+ {
+ Connect,
+ Disconnect,
+ ConsoleLogUpdate,
+ PropUpdate,
+ Command,
+ Backup,
+ Restart,
+ Heartbeat
+ }
+ public enum NetworkMessageStatus
+ {
+ Failed,
+ Passed,
+ None
+ }
+}
diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs
new file mode 100644
index 00000000..d407b66a
--- /dev/null
+++ b/BedrockService/Service/Networking/TCPListener.cs
@@ -0,0 +1,366 @@
+using BedrockService.Service.Logging;
+using BedrockService.Service.Server;
+using BedrockService.Service.Server.HostInfoClasses;
+using BedrockService.Service.Server.Logging;
+using BedrockService.Utilities;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+namespace BedrockService.Service.Networking
+{
+ public class TCPListener
+ {
+ public TcpClient client;
+ private TcpListener InListener;
+ private NetworkStream stream;
+ private bool keepalive;
+ private bool heartbeatRecieved = false;
+ private bool firstHeartbeatRecieved = false;
+ private int heartbeatFailTimeout;
+ private int heartbeatFailTimeoutLimit = 500;
+
+ public void StartListening(int port)
+ {
+ IPAddress addr = IPAddress.Parse("127.0.0.1");
+ InListener = InstanceProvider.InitTCPListener(addr, port);
+ try
+ {
+ InListener.Start();
+ }
+ catch
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Error! Port is occupied and cannot be opened... Program will be killed!");
+ Thread.Sleep(2000);
+ Environment.Exit(1);
+ }
+
+ while (true)
+ {
+ try
+ {
+ client = InListener.AcceptTcpClient();
+ stream = client.GetStream();
+ InstanceProvider.InitClientService(new ThreadStart(IncomingListener)).Start();
+ InstanceProvider.InitHeartbeatThread(new ThreadStart(SendBackHeatbeatSignal)).Start();
+ }
+ catch (ThreadStateException) { }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine(e.ToString());
+ }
+ }
+ //listener.Stop();
+ }
+ private void IncomingListener()
+ {
+ keepalive = true;
+ InstanceProvider.GetServiceLogger().AppendLine("Established connection! Listening for incoming packets!");
+ int AvailBytes = 0;
+ int byteCount = 0;
+ while (InstanceProvider.GetClientServiceAlive())
+ {
+ try
+ {
+ byte[] buffer = new byte[4];
+ AvailBytes = client.Client.Available;
+ while (AvailBytes != 0) // Recieve data from client.
+ {
+ byteCount = stream.Read(buffer, 0, 4);
+ int expectedLen = BitConverter.ToInt32(buffer, 0);
+ buffer = new byte[expectedLen];
+ byteCount = stream.Read(buffer, 0, expectedLen);
+ NetworkMessageSource msgSource = (NetworkMessageSource)buffer[0];
+ NetworkMessageDestination msgDest = (NetworkMessageDestination)buffer[1];
+ NetworkMessageTypes msgType = (NetworkMessageTypes)buffer[2];
+ NetworkMessageStatus msgStatus = (NetworkMessageStatus)buffer[3];
+ string data = GetOffsetString(buffer);
+ string[] dataSplit = null;
+ switch (msgDest)
+ {
+ case NetworkMessageDestination.Server:
+
+ JsonParser message;
+ switch (msgType)
+ {
+ case NetworkMessageTypes.PropUpdate:
+
+ message = JsonParser.Deserialize(data);
+ List propList = message.Value.ToObject>();
+ Property prop = propList.First(p => p.KeyName == "server-name");
+ InstanceProvider.GetBedrockServer(prop.Value).serverInfo.ServerPropList = propList;
+ InstanceProvider.GetConfigManager().SaveServerProps(InstanceProvider.GetBedrockServer(prop.Value).serverInfo, true);
+ InstanceProvider.GetConfigManager().LoadConfigs();
+ InstanceProvider.GetBedrockServer(prop.Value).CurrentServerStatus = BedrockServer.ServerStatus.Stopping;
+ while (InstanceProvider.GetBedrockServer(prop.Value).CurrentServerStatus == BedrockServer.ServerStatus.Stopping)
+ {
+ Thread.Sleep(100);
+ }
+ InstanceProvider.GetBedrockServer(prop.Value).StartControl(InstanceProvider.GetBedrockService()._hostControl);
+ SendData(NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.PropUpdate);
+
+ break;
+ case NetworkMessageTypes.Restart:
+
+ RestartServer(data, false);
+ break;
+
+ case NetworkMessageTypes.Backup:
+
+ RestartServer(data, true);
+ break;
+ case NetworkMessageTypes.Command:
+
+ dataSplit = data.Split(';');
+ InstanceProvider.GetBedrockServer(dataSplit[0]).StdInStream.WriteLine(dataSplit[1]);
+ InstanceProvider.GetServiceLogger().AppendLine($"Sent command {dataSplit[1]} to stdInput stream");
+
+ break;
+ }
+ break;
+ case NetworkMessageDestination.Service:
+ switch (msgType)
+ {
+ case NetworkMessageTypes.Connect:
+
+ InstanceProvider.GetHostInfo().ServiceLog = InstanceProvider.GetServiceLogger().Log;
+ string jsonString = JsonParser.Serialize(JsonParser.FromValue(InstanceProvider.GetHostInfo()));
+ byte[] stringAsBytes = GetBytes(jsonString);
+ SendData(stringAsBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Connect);
+ heartbeatRecieved = false;
+
+ break;
+ case NetworkMessageTypes.Disconnect:
+
+ DisconnectClient();
+
+ break;
+ case NetworkMessageTypes.Heartbeat:
+
+ if (InstanceProvider.GetHeartbeatThreadAlive())
+ heartbeatRecieved = true;
+ else
+ {
+ InstanceProvider.InitHeartbeatThread(new ThreadStart(SendBackHeatbeatSignal)).Start();
+ Thread.Sleep(500);
+ heartbeatRecieved = true;
+ }
+
+ break;
+ case NetworkMessageTypes.ConsoleLogUpdate:
+
+ StringBuilder srvString = new StringBuilder();
+ string[] split = data.Split('|');
+ for (int i = 0; i < split.Length; i++)
+ {
+ dataSplit = split[i].Split(';');
+ string srvName = dataSplit[0];
+ int srvTextLen;
+ int clientCurLen;
+ int loop;
+ if(srvName != "Service")
+ {
+ ServerLogger srvText = InstanceProvider.GetBedrockServer(srvName).serverInfo.ConsoleBuffer;
+ srvTextLen = srvText.Count();
+ clientCurLen = int.Parse(dataSplit[1]);
+ loop = clientCurLen;
+ while (loop < srvTextLen)
+ {
+ srvString.Append($"{srvName};{srvText.FromIndex(loop)};{loop}|");
+ loop++;
+ }
+
+ }
+ else
+ {
+ ServiceLogger srvText = InstanceProvider.GetServiceLogger();
+ srvTextLen = srvText.Count();
+ clientCurLen = int.Parse(dataSplit[1]);
+ loop = clientCurLen;
+ while (loop < srvTextLen)
+ {
+ srvString.Append($"{srvName};{srvText.FromIndex(loop)};{loop}|");
+ loop++;
+ }
+ }
+ }
+ if (srvString.Length > 1)
+ {
+ srvString.Remove(srvString.Length - 1, 1);
+ stringAsBytes = GetBytes(srvString.ToString());
+ SendData(stringAsBytes, NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.ConsoleLogUpdate);
+ }
+ break;
+
+ }
+ break;
+ }
+ AvailBytes = client.Client.Available;
+ }
+ Thread.Sleep(200);
+ }
+ catch (OutOfMemoryException)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("");
+
+ }
+ catch (ObjectDisposedException e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Client was disposed! Killing thread...");
+ break;
+ }
+ catch (ThreadAbortException) { }
+
+ catch (JsonException e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error parsing json array: {e.Message}");
+ InstanceProvider.GetServiceLogger().AppendLine($"Stacktrace: {e.InnerException}");
+ }
+ catch (Exception e)
+ {
+ //InstanceProvider.GetServiceLogger().AppendLine($"Error: {e.Message} {e.StackTrace}");
+ //InstanceProvider.GetServiceLogger().AppendLine($"Error: {e.Message}: {AvailBytes}, {byteCount}\n{e.StackTrace}");
+ }
+ AvailBytes = client.Client.Available;
+ if (InstanceProvider.GetClientService().ThreadState == ThreadState.Aborted)
+ keepalive = false;
+ }
+ InstanceProvider.GetServiceLogger().AppendLine("IncomingListener thread exited.");
+ }
+
+ private string GetOffsetString(byte[] array) => Encoding.UTF8.GetString(array, 4, array.Length - 4);
+
+ private byte[] GetBytes(string input) => Encoding.UTF8.GetBytes(input);
+
+ private void DisconnectClient()
+ {
+ try
+ {
+ stream.Close();
+ stream.Dispose();
+ client.Close();
+ client.Dispose();
+ InstanceProvider.DisposeClientService();
+ InstanceProvider.DisposeHeartbeatThread();
+ }
+ catch { }
+ }
+
+ public void SendBackHeatbeatSignal()
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("HeartBeatSender started.");
+ while (keepalive)
+ {
+ heartbeatRecieved = false;
+ while (!heartbeatRecieved)
+ {
+ Thread.Sleep(100);
+ heartbeatFailTimeout++;
+ if (heartbeatFailTimeout > heartbeatFailTimeoutLimit)
+ {
+ if (!firstHeartbeatRecieved)
+ {
+ try
+ {
+ SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat);
+ heartbeatFailTimeout = 0;
+ }
+ catch
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("HeartBeatSender exited.");
+ return;
+ }
+ }
+ DisconnectClient();
+ InstanceProvider.GetServiceLogger().AppendLine("HeartBeatSender exited.");
+ return;
+ }
+ }
+ heartbeatRecieved = false;
+ heartbeatFailTimeout = 0;
+ SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat);
+ Thread.Sleep(3000);
+ }
+ InstanceProvider.GetServiceLogger().AppendLine("HeartBeatSender exited.");
+ }
+
+ private void RestartServer(string payload, bool performBackup)
+ {
+ if (InstanceProvider.GetBedrockServer(payload).CurrentServerStatus == BedrockServer.ServerStatus.Started)
+ {
+ InstanceProvider.GetBedrockServer(payload).CurrentServerStatus = BedrockServer.ServerStatus.Stopping;
+ while (InstanceProvider.GetBedrockServer(payload).CurrentServerStatus == BedrockServer.ServerStatus.Stopping)
+ {
+ Thread.Sleep(100);
+ }
+ if (performBackup)
+ {
+ if (InstanceProvider.GetBedrockServer(payload).Backup())
+ {
+ SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Backup, NetworkMessageStatus.Passed);
+ }
+ else
+ {
+ SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Backup, NetworkMessageStatus.Failed);
+ }
+ }
+ InstanceProvider.GetBedrockServer(payload).CurrentServerStatus = BedrockServer.ServerStatus.Starting;
+ Thread.Sleep(1000);
+ }
+ }
+
+ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageStatus status)
+ {
+ byte[] compiled = new byte[8 + bytes.Length];
+ byte[] len = BitConverter.GetBytes(4 + bytes.Length);
+ Buffer.BlockCopy(len, 0, compiled, 0, 4);
+ compiled[4] = (byte)source;
+ compiled[5] = (byte)destination;
+ compiled[6] = (byte)type;
+ compiled[7] = (byte)status;
+ Buffer.BlockCopy(bytes, 0, compiled, 8, bytes.Length);
+ if (keepalive)
+ {
+ try
+ {
+ stream.Write(compiled, 0, compiled.Length);
+ stream.Flush();
+ return true;
+
+ }
+ catch
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Error writing to network stream!");
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public bool SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type)
+ {
+ if (SendData(new byte[0], source, destination, type, NetworkMessageStatus.None))
+ return true;
+ return false;
+ }
+
+ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type)
+ {
+ if (SendData(bytes, source, destination, type, NetworkMessageStatus.None))
+ return true;
+ return false;
+ }
+
+ public bool SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageStatus status)
+ {
+ if (SendData(new byte[0], source, destination, type, status))
+ return true;
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BedrockService/Service/Networking/Updater.cs b/BedrockService/Service/Networking/Updater.cs
new file mode 100644
index 00000000..7fe1b440
--- /dev/null
+++ b/BedrockService/Service/Networking/Updater.cs
@@ -0,0 +1,136 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace BedrockService.Service.Networking
+{
+ public static class Updater
+ {
+ public static bool VersionChanged = false;
+ public static string[] FileList;
+
+ public static async Task CheckUpdates()
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Checking MCS Version and fetching update if needed...");
+ var client = new HttpClient();
+ client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/apng,*/*;q=0.8");
+ client.DefaultRequestHeaders.Add("Accept-Language", "en-GB,en;q=0.9,en-US;q=0.8");
+ client.DefaultRequestHeaders.Add("Connection", "keep-alive");
+ client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
+ client.DefaultRequestHeaders.Add("Pragma", "no-cache");
+ client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko; Google Page Speed Insights) Chrome/27.0.1453 Safari/537.36");
+ client.Timeout = new TimeSpan(0, 0, 2);
+
+ string content = FetchHTTPContent(client).Result;
+ if (content == null) // This really doesn't fail often. Give it one more try or fail.
+ {
+ Thread.Sleep(500);
+ content = await FetchHTTPContent(client);
+ }
+ if (content == null)
+ return false;
+ string pattern = @"(https://minecraft.azureedge.net/bin-win/bedrock-server-)(.*)(\.zip)";
+ Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
+ Match m = regex.Match(content);
+ if (!m.Success)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Checking for updates failed. Check website functionality!");
+ return false;
+ }
+ string downloadPath = m.Groups[0].Value;
+ string version = m.Groups[2].Value;
+ client.Dispose();
+
+ if (File.Exists($@"{Program.ServiceDirectory}\Server\bedrock_ver.ini"))
+ {
+ string LocalVer = File.ReadAllText($@"{Program.ServiceDirectory}\Server\bedrock_ver.ini");
+ if (LocalVer != version)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"New version detected! Now fetching from {downloadPath}...");
+ VersionChanged = true;
+ FetchBuild(downloadPath, version).Wait();
+ File.WriteAllText($@"{Program.ServiceDirectory}\Server\bedrock_ver.ini", version);
+ return true;
+ }
+ }
+ else
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Version ini file missing, fetching build to recreate...");
+ FetchBuild(downloadPath, version).Wait();
+ File.WriteAllText($@"{Program.ServiceDirectory}\Server\bedrock_ver.ini", version);
+ return true;
+ }
+ return false;
+ }
+
+ private static async Task FetchHTTPContent(HttpClient client)
+ {
+ try
+ {
+ return await client.GetStringAsync("https://www.minecraft.net/en-us/download/server/bedrock");
+ }
+ catch (HttpRequestException e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error! Updater timed out, could not fetch current build!");
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Updater resulted in error: {e.Message}\n{e.InnerException}\n{e.StackTrace}");
+ }
+ return null;
+ }
+
+ public static async Task FetchBuild(string path, string version)
+ {
+ string ZipDir = $@"{Program.ServiceDirectory}\Server\MCSFiles\Update_{version}.zip";
+ if (!Directory.Exists($@"{Program.ServiceDirectory}\Server\MCSFiles"))
+ {
+ Directory.CreateDirectory($@"{Program.ServiceDirectory}\Server\MCSFiles");
+ }
+ if (File.Exists(ZipDir))
+ {
+ File.Delete(ZipDir);
+ }
+ if ((string)InstanceProvider.GetHostInfo().GetGlobalValue("AcceptedMojangLic") == "false")
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("------First time download detected------\n");
+ InstanceProvider.GetServiceLogger().AppendLine("You will need to agree to the Minecraft End User License Agreement");
+ InstanceProvider.GetServiceLogger().AppendLine("in order to continue. Visit https://account.mojang.com/terms");
+ InstanceProvider.GetServiceLogger().AppendLine("to view terms. Type \"Yes\" and press enter to confirm that");
+ InstanceProvider.GetServiceLogger().AppendLine("you agree to said terms.");
+ Console.Write("Do you agree to the terms? ");
+ Console.Out.Flush();
+ if (Console.ReadLine() != "Yes")
+ {
+ return;
+ }
+ InstanceProvider.GetHostInfo().SetGlobalProperty("AcceptedMojangLic", "true");
+ InstanceProvider.GetConfigManager().SaveGlobalFile();
+ InstanceProvider.GetServiceLogger().AppendLine("Now downloading latest build of Minecraft Bedrock Server. Please wait...");
+ }
+ using (var httpClient = new HttpClient())
+ {
+ using (var request = new HttpRequestMessage(HttpMethod.Get, path))
+ {
+ using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(ZipDir, FileMode.Create, FileAccess.Write, FileShare.None, 256000, true))
+ {
+ try
+ {
+ await contentStream.CopyToAsync(stream);
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Download zip resulted in error: {e.StackTrace}");
+ }
+ httpClient.Dispose();
+ request.Dispose();
+ contentStream.Dispose();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs
new file mode 100644
index 00000000..5899bf6b
--- /dev/null
+++ b/BedrockService/Service/Program.cs
@@ -0,0 +1,71 @@
+using System;
+using System.IO;
+using System.Reflection;
+using Topshelf;
+using Topshelf.Runtime;
+
+namespace BedrockService.Service
+{
+ class Program
+ {
+ public static readonly string ServiceDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ public static bool DebugModeEnabled = false;
+ public static bool IsConsoleMode = false;
+ static void Main(string[] args)
+ {
+ if (args.Length == 0 && Environment.UserInteractive)
+ {
+ IsConsoleMode = true;
+ InstanceProvider.GetServiceLogger().AppendLine("BedrockService startup detected in Console mode."); ;
+ }
+ Host host = HostFactory.New(x =>
+ {
+ x.SetStartTimeout(TimeSpan.FromSeconds(10));
+ x.SetStopTimeout(TimeSpan.FromSeconds(10));
+ //x.UseLog4Net();
+ x.UseAssemblyInfoForServiceInfo();
+ x.Service(settings => InstanceProvider.GetBedrockService(), s =>
+ {
+ s.BeforeStartingService(_ => InstanceProvider.GetServiceLogger().AppendLine("Starting service..."));
+ s.BeforeStoppingService(_ => InstanceProvider.GetServiceLogger().AppendLine("Stopping service..."));
+
+ });
+
+
+ x.RunAsLocalSystem();
+ x.SetDescription("Windows Service Wrapper for Windows Bedrock Server");
+ x.SetDisplayName("BedrockService");
+ x.SetServiceName("BedrockService");
+ x.UnhandledExceptionPolicy = UnhandledExceptionPolicyCode.LogErrorOnly;
+
+ x.EnableServiceRecovery(src =>
+ {
+ src.RestartService(delayInMinutes: 0);
+ src.RestartService(delayInMinutes: 1);
+ src.SetResetPeriod(days: 1);
+ });
+
+ x.OnException((ex) =>
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Exception occured Main : " + ex.ToString());
+ });
+ });
+
+ TopshelfExitCode rc = host.Run();
+ var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());
+ if (DebugModeEnabled)
+ {
+ Console.Write("Program is force-quitting. Press any key to exit.");
+ Console.Out.Flush();
+ Console.ReadLine();
+ }
+ Environment.ExitCode = exitCode;
+ }
+
+ private static void Program_Exited(object sender, EventArgs e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Program exit detected... shutting down!");
+ InstanceProvider.GetBedrockService().Stop(InstanceProvider.GetBedrockService()._hostControl);
+ }
+ }
+}
diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..37ca46b4
--- /dev/null
+++ b/BedrockService/Service/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BedrockService")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BedrockService")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[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)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f4b1f910-30b0-4f94-a49f-94142e790c9d")]
+
+// Version information for an assembly consists of the following four values:
+//
+// 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")]
diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs
new file mode 100644
index 00000000..5c0beeb9
--- /dev/null
+++ b/BedrockService/Service/Server/BedrockServer.cs
@@ -0,0 +1,384 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using BedrockService.Service.Server.Logging;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Threading;
+using Topshelf;
+
+namespace BedrockService.Service.Server
+{
+ public class BedrockServer : ServerInfo
+ {
+ Process process;
+ public StreamWriter StdInStream;
+ Thread WatchdogThread;
+ public ServerInfo serverInfo;
+
+
+ const string startupMessage = "[INFO] Server started.";
+ HostControl hostController;
+ public BedrockServer(ServerInfo serverToSet)
+ {
+ serverInfo = serverToSet;
+ }
+ public BackgroundWorker Worker { get; set; }
+
+ public enum ServerStatus
+ {
+ Stopped,
+ Starting,
+ Stopping,
+ Started
+ }
+
+ public ServerStatus CurrentServerStatus;
+
+ public bool StopControl()
+ {
+ CurrentServerStatus = ServerStatus.Stopping;
+ if (!(process is null))
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Sending Stop to Bedrock . Process.HasExited = " + process.HasExited.ToString());
+
+ process.CancelOutputRead();
+ process.CancelErrorRead();
+
+ StdInStream.WriteLine("stop");
+ while (!process.HasExited) { }
+
+ }
+ if (!(Worker is null))
+ {
+ Worker.CancelAsync();
+ while (Worker.IsBusy) { Thread.Sleep(10); }
+ Worker.Dispose();
+ }
+ Worker = null;
+ process = null;
+ GC.Collect();
+ CurrentServerStatus = ServerStatus.Stopped;
+ return true;
+ }
+
+ public void StartControl(HostControl hostControl)
+ {
+ if (Worker is null)
+ {
+ Worker = new BackgroundWorker() { WorkerSupportsCancellation = true };
+ }
+
+ if (!Worker.IsBusy)
+ {
+ Worker.DoWork += (s, e) =>
+ {
+ RunServer(hostControl);
+ };
+ Worker.RunWorkerAsync();
+ }
+ CurrentServerStatus = ServerStatus.Started;
+ }
+
+ public void RunServer(HostControl hostControl)
+ {
+ hostController = hostControl;
+ string appName = serverInfo.ServerExeName.Value.Substring(0, serverInfo.ServerExeName.Value.Length - 4);
+
+ try
+ {
+ if (File.Exists(serverInfo.ServerPath.Value + "\\" + serverInfo.ServerExeName.Value))
+ {
+ if (MonitoredAppExists(appName))
+ {
+ Process[] processList = Process.GetProcessesByName(appName);
+ if (processList.Length != 0)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($@"Application {appName} was found running! Killing to proceed.");
+ KillProcess(processList);
+ }
+ }
+ // Fires up a new process to run inside this one
+ CreateProcess();
+ }
+ else
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("The Bedrock Server is not accessible at " + serverInfo.ServerPath.Value + "\\" + serverInfo.ServerExeName.Value + "\r\nCheck if the file is at that location and that permissions are correct.");
+ hostControl.Stop();
+ }
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error Running Bedrock Server: {e.StackTrace}");
+ hostControl.Stop();
+
+ }
+
+ }
+
+ private void CreateProcess()
+ {
+ process = Process.Start(new ProcessStartInfo
+ {
+ UseShellExecute = false,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ FileName = serverInfo.ServerPath.Value + "\\" + serverInfo.ServerExeName.Value
+ });
+ process.PriorityClass = ProcessPriorityClass.RealTime;
+ process.OutputDataReceived += StdOutToLog;
+ process.ErrorDataReceived += ErrOutToLog;
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ StdInStream = process.StandardInput;
+ }
+
+ private void KillProcess(Process[] processList)
+ {
+ foreach (Process process in processList)
+ {
+ try
+ {
+ process.Kill();
+ Thread.Sleep(1000);
+ InstanceProvider.GetServiceLogger().AppendLine($@"App {serverInfo.ServerExeName.Value.Substring(0, serverInfo.ServerExeName.Value.Length - 4)} killed!");
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Killing proccess resulted in error: {e.StackTrace}");
+ }
+ }
+ }
+
+ private bool MonitoredAppExists(string monitoredAppName)
+ {
+ try
+ {
+ Process[] processList = Process.GetProcessesByName(monitoredAppName);
+ if (processList.Length == 0)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("ApplicationWatcher MonitoredAppExists Exception: " + ex.StackTrace);
+ return true;
+ }
+ }
+
+ public void StartWatchdog(HostControl hostControl)
+ {
+ hostController = hostControl;
+ WatchdogThread = new Thread(new ThreadStart(ApplicationWatchdogMonitor));
+ WatchdogThread.IsBackground = true;
+ WatchdogThread.Name = "WatchdogMonitor";
+ WatchdogThread.Start();
+ }
+
+ public void ApplicationWatchdogMonitor()
+ {
+ while (WatchdogThread.IsAlive)
+ {
+ string appName = serverInfo.ServerExeName.Value.Substring(0, serverInfo.ServerExeName.Value.Length - 4);
+ if (!MonitoredAppExists(appName) && CurrentServerStatus == ServerStatus.Starting)
+ {
+ hostController.RequestAdditionalTime(TimeSpan.FromSeconds(30));
+ StartControl(hostController);
+ InstanceProvider.GetServiceLogger().AppendLine($"Recieved start signal for server {serverInfo.ServerName}.");
+ }
+ while (MonitoredAppExists(appName) && CurrentServerStatus == ServerStatus.Started)
+ {
+ Thread.Sleep(5000);
+ }
+ if (MonitoredAppExists(appName) && CurrentServerStatus == ServerStatus.Stopping)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"BedrockService signaled stop to application {appName}.");
+ InstanceProvider.GetServiceLogger().AppendLine("Stopping...");
+ StopControl();
+ while (CurrentServerStatus == ServerStatus.Stopping)
+ {
+ Thread.Sleep(250);
+ }
+ }
+ if (!MonitoredAppExists(appName) && CurrentServerStatus == ServerStatus.Started)
+ {
+ StopControl();
+ InstanceProvider.GetServiceLogger().AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}.");
+ StartControl(hostController);
+ }
+ if (!MonitoredAppExists(appName) && CurrentServerStatus == ServerStatus.Stopped)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Server stopped successfully.");
+ }
+ while (!MonitoredAppExists(appName) && CurrentServerStatus == ServerStatus.Stopped)
+ {
+ Thread.Sleep(1000);
+ }
+ }
+ }
+
+ private void StdOutToLog(object sender, DataReceivedEventArgs e)
+ {
+ serverInfo.ConsoleBuffer = serverInfo.ConsoleBuffer ?? new ServerLogger(serverInfo.LogToFileEnabled.Value == "true", serverInfo.ServerName);
+ serverInfo.ConsoleBuffer.Append($"{serverInfo.ServerName}: {e.Data}\r\n");
+ if (e.Data != null)
+ {
+ string dataMsg = e.Data;
+ if (dataMsg.Contains(startupMessage))
+ {
+ CurrentServerStatus = ServerStatus.Started;
+ Thread.Sleep(1000);
+
+ if (serverInfo.StartCmds.Count > 0)
+ {
+ RunStartupCommands();
+ }
+ }
+ if (dataMsg.Contains("[INFO] Player connected"))
+ {
+ int usernameStart = dataMsg.IndexOf(':') + 2;
+ int usernameEnd = dataMsg.IndexOf(',');
+ int usernameLength = usernameEnd - usernameStart;
+ int xuidStart = dataMsg.IndexOf(':', usernameEnd) + 2;
+ string username = dataMsg.Substring(usernameStart, usernameLength);
+ string xuid = dataMsg.Substring(xuidStart, dataMsg.Length - xuidStart);
+ Console.WriteLine($"Player {username} connected with XUID: {xuid}");
+ InstanceProvider.GetPlayerManager().PlayerConnected(username, xuid, serverInfo);
+ }
+ if (dataMsg.Contains("[INFO] Player disconnected"))
+ {
+ int usernameStart = dataMsg.IndexOf(':') + 2;
+ int usernameEnd = dataMsg.IndexOf(',');
+ int usernameLength = usernameEnd - usernameStart;
+ int xuidStart = dataMsg.IndexOf(':', usernameEnd) + 2;
+ string username = dataMsg.Substring(usernameStart, usernameLength);
+ string xuid = dataMsg.Substring(xuidStart, dataMsg.Length - xuidStart);
+ Console.WriteLine($"Player {username} disconnected with XUID: {xuid}");
+ InstanceProvider.GetPlayerManager().PlayerDisconnected(xuid, serverInfo);
+ }
+ }
+ }
+
+ private void ErrOutToLog(object sender, DataReceivedEventArgs e)
+ {
+ serverInfo.ConsoleBuffer.Append($"{serverInfo.ServerName}: ERROR!! {e.Data}\r\n");
+ }
+
+ public bool Backup()
+ {
+ try
+ {
+ FileInfo exe = new FileInfo(serverInfo.ServerPath.Value + serverInfo.ServerExeName.Value);
+
+ if (serverInfo.BackupPath.Value.Length > 0)
+ {
+ DirectoryInfo serverDir = new DirectoryInfo(serverInfo.ServerPath.Value);
+ DirectoryInfo worldsDir = new DirectoryInfo($@"{serverInfo.ServerPath.Value}\worlds");
+ DirectoryInfo backupDir = new DirectoryInfo($@"{serverInfo.BackupPath.Value}\{serverInfo.ServerName}");
+ if (!Directory.Exists(backupDir.FullName))
+ {
+ Directory.CreateDirectory($@"{serverInfo.BackupPath.Value}\{serverInfo.ServerName}");
+ }
+ int dirCount = backupDir.GetDirectories().Length; // this line creates a new int with a value derived from the number of directories found in the backups folder.
+ try // use a try catch any time you know an error could occur.
+ {
+ if (dirCount >= int.Parse(serverInfo.MaxBackupCount.Value)) // Compare the directory count with the value set in the config. Values from config are stored as strings, and therfore must be converted to integer first for compare.
+ {
+ string pattern = $@"Backup_(.*)$"; // This is a regular expression pattern. If you would like to know more, Grab notepad++ and play with regex search, a lot of guides out there.
+ Regex reg = new Regex(pattern); // Creates a new Regex class with our pattern loaded.
+
+ List Dates = new List(); // creates a new list long integer array named Dates, and initializes it.
+ foreach (DirectoryInfo dir in backupDir.GetDirectories()) // Loop through the array of directories in backup folder. In this "foreach" loop, we name each entry in the array "dir" and then do something to it.
+ {
+ if (reg.IsMatch(dir.Name)) // Using regex.IsMatch will return true if the pattern matches the name of the folder we are working with.
+ {
+ Match match = reg.Match(dir.Name); // creates an instance of the match to work with.
+ Dates.Add(Convert.ToInt64(match.Groups[1].Value)); // if it was a match, we then pull the number we saved in the (.*) part of the pattern from the groups method in the match. Groups saves the entire match first, followed by anthing saved in parentheses. Because we need to compare dates, we must convert the string to an integer.
+ }
+ }
+ long OldestDate = 0; // Create a new int to store the oldest date in.
+ foreach (long date in Dates) // for each date in the Dates array....
+ {
+ if (OldestDate == 0) // if this is the first entry in Dates, OldestDate will still be 0. Set it to a date so compare can happen.
+ {
+ OldestDate = date; // OldestDate now equals date.
+ }
+ else if (date < OldestDate) // If now the next entry in Dates is a smaller number than the previously set OldestDate, reset OldestDate to date.
+ {
+ OldestDate = date; // OldestDate now equals date.
+ }
+ }
+ Directory.Delete($@"{backupDir}\Backup_{OldestDate}", true); // After running through all directories, this string $@"{backupDir}\Backup_{OldestDate}" should now represent the folder that has the lowest/oldest date. Delete it. Supply the "true" after the directory string to enable recusive mode, removing all files and folders.
+ }
+ }
+ catch (Exception e) // catch all exceptions here.
+ {
+ if (e.GetType() == typeof(FormatException)) // if the exception is equal a type of FormatException, Do the following... if this was a IOException, they would not match.
+ {
+ InstanceProvider.GetServiceLogger().AppendLine("Error in Config! MaxBackupCount must be nothing but a number!"); // this exception will be thrown if the string could not become a number (i.e. of there was a letter in the mix).
+ }
+ }
+
+ var targetDirectory = backupDir.CreateSubdirectory($"Backup_{DateTime.Now:yyyyMMddhhmmss}");
+ InstanceProvider.GetServiceLogger().AppendLine($"Backing up files for server {serverInfo.ServerName}. Please wait!");
+ if (serverInfo.AdvancedBackup.Value == "false")
+ {
+ CopyFilesRecursively(worldsDir, targetDirectory);
+ }
+ else if (serverInfo.AdvancedBackup.Value == "true")
+ {
+ CopyFilesRecursively(serverDir, targetDirectory);
+ }
+ return true;
+ }
+ }
+ catch (Exception e)
+ {
+ InstanceProvider.GetServiceLogger().AppendLine($"Error with Backup: {e.StackTrace}");
+ return false;
+ }
+ return false;
+ }
+
+ private static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
+ {
+ foreach (DirectoryInfo dir in source.GetDirectories())
+ CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
+ foreach (FileInfo file in source.GetFiles())
+ file.CopyTo(Path.Combine(target.FullName, file.Name));
+ }
+
+ private static void DeleteFilesRecursively(DirectoryInfo source)
+ {
+ foreach (DirectoryInfo dir in source.GetDirectories())
+ {
+ DeleteFilesRecursively(dir);
+ }
+
+ foreach (FileInfo file in source.GetFiles())
+ {
+ file.Delete();
+ Directory.Delete(source.FullName);
+ }
+ }
+
+ private void RunStartupCommands()
+ {
+ foreach (StartCmdEntry cmd in serverInfo.StartCmds)
+ {
+ StdInStream.WriteLine(cmd.Command.Trim());
+ Thread.Sleep(1000);
+ }
+ }
+ }
+}
diff --git a/BedrockService/Service/Server/HostInfoClasses/HostInfo.cs b/BedrockService/Service/Server/HostInfoClasses/HostInfo.cs
new file mode 100644
index 00000000..5265de89
--- /dev/null
+++ b/BedrockService/Service/Server/HostInfoClasses/HostInfo.cs
@@ -0,0 +1,78 @@
+using BedrockService.Service.Logging;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BedrockService.Service.Server.HostInfoClasses
+{
+ public partial class HostInfo
+ {
+ public string HostName { get; set; }
+ public string Address { get; set; }
+ public string HostDisplayName { get; set; }
+
+ public List ServiceLog = new List();
+ public List Servers = new List();
+ public List Globals = new List();
+
+ public void SetGlobalsDefault()
+ {
+ Globals.Add(new Property("BackupEnabled", "false"));
+ Globals.Add(new Property("AcceptedMojangLic", "false"));
+ Globals.Add(new Property("CheckUpdates", "false"));
+ Globals.Add(new Property("LogToFile", "false"));
+ Globals.Add(new Property("BackupCron", "0 1 * * *"));
+ Globals.Add(new Property("UpdateCron", "0 2 * * *"));
+ Globals.Add(new Property("ClientPort", "19134"));
+ }
+
+ public void SetGlobalProperty(string name, string entry)
+ {
+ Property GlobalToEdit = Globals.FirstOrDefault(glob => glob.KeyName == name);
+ Globals[Globals.IndexOf(GlobalToEdit)].Value = entry;
+ }
+
+ public string GetGlobalValue(string key) => Globals.FirstOrDefault(prop => prop.KeyName == key).Value;
+
+ public void SetGlobalProperty(Property propToSet)
+ {
+ Property GlobalToEdit = Globals.FirstOrDefault(glob => glob.KeyName == propToSet.KeyName);
+ Globals[Globals.IndexOf(GlobalToEdit)].Value = propToSet.Value;
+ }
+
+ public void SetGlobalPropertyDefault(string name)
+ {
+ Property GlobalToEdit = Globals.FirstOrDefault(glob => glob.KeyName == name);
+ Globals[Globals.IndexOf(GlobalToEdit)].Value = GlobalToEdit.DefaultValue;
+ }
+
+ public string ServiceLogToString()
+ {
+ StringBuilder OutString = new StringBuilder();
+ foreach (string s in ServiceLog)
+ {
+ OutString.Append(s);
+ }
+ return OutString.ToString();
+ }
+
+ public void SetGlobalPropertyDefault(Property propToSet)
+ {
+ Property GlobalToEdit = Globals.FirstOrDefault(glob => glob.KeyName == propToSet.KeyName);
+ Globals[Globals.IndexOf(GlobalToEdit)].Value = GlobalToEdit.DefaultValue;
+ }
+
+ public ServerInfo GetServerInfo(string serverName)
+ {
+ return Servers.FirstOrDefault(srv => srv.ServerName == serverName);
+ }
+
+ public List GetServerInfos() => Servers;
+
+ public void ClearServerInfos() => Servers = new List();
+
+ public List GetGlobals() => Globals;
+
+ public void SetGlobals(List props) => Globals = props;
+ }
+}
diff --git a/BedrockService/Service/Server/HostInfoClasses/Player.cs b/BedrockService/Service/Server/HostInfoClasses/Player.cs
new file mode 100644
index 00000000..6fa90c76
--- /dev/null
+++ b/BedrockService/Service/Server/HostInfoClasses/Player.cs
@@ -0,0 +1,53 @@
+using Newtonsoft.Json;
+
+namespace BedrockService.Service.Server.HostInfoClasses
+{
+ public class Player
+ {
+ public string Username { get; set; }
+ public string XUID { get; set; }
+ public string PermissionLevel;
+ public string[] PermissionLevels = new string[3]
+ {
+ "visitor",
+ "member" ,
+ "operator"
+ };
+ public string FirstConnectedTime { get; set; }
+ public string LastConnectedTime { get; set; }
+ public string LastDisconnectTime { get; set; }
+ public bool Whitelisted { get; set; }
+ public bool IgnorePlayerLimits { get; set; }
+ public bool FromConfig { get; set; }
+
+ public Player(string xuid, string username, string firstConn, string lastConn, string lastDiscon)
+ {
+ Username = username;
+ XUID = xuid;
+ FirstConnectedTime = firstConn;
+ LastConnectedTime = lastConn;
+ LastDisconnectTime = lastDiscon;
+ }
+
+ [JsonConstructor]
+ public Player(string xuid, string username, string firstConn, string lastConn, string lastDiscon, bool whtlist, string perm, bool ignoreLimit, bool fromCfg)
+ {
+ Username = username;
+ XUID = xuid;
+ FirstConnectedTime = firstConn;
+ LastConnectedTime = lastConn;
+ LastDisconnectTime = lastDiscon;
+ Whitelisted = whtlist;
+ PermissionLevel = perm;
+ IgnorePlayerLimits = ignoreLimit;
+ }
+
+ public Player(string xuid, string username)
+ {
+ Username = username;
+ XUID = xuid;
+ }
+ }
+
+}
+
diff --git a/BedrockService/Service/Server/HostInfoClasses/Property.cs b/BedrockService/Service/Server/HostInfoClasses/Property.cs
new file mode 100644
index 00000000..bc059c72
--- /dev/null
+++ b/BedrockService/Service/Server/HostInfoClasses/Property.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json;
+
+namespace BedrockService.Service.Server.HostInfoClasses
+{
+ public class Property
+ {
+ public string KeyName { get; set; }
+ public string Value { get; set; }
+ public string DefaultValue { get; set; }
+
+ [JsonConstructor]
+ public Property(string key, string defaultValue)
+ {
+ KeyName = key;
+ Value = defaultValue;
+ DefaultValue = defaultValue;
+ }
+ }
+}
diff --git a/BedrockService/Service/Server/HostInfoClasses/ServerInfo.cs b/BedrockService/Service/Server/HostInfoClasses/ServerInfo.cs
new file mode 100644
index 00000000..167be234
--- /dev/null
+++ b/BedrockService/Service/Server/HostInfoClasses/ServerInfo.cs
@@ -0,0 +1,104 @@
+using BedrockService.Service.Server.Logging;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BedrockService.Service.Server.HostInfoClasses
+{
+ public class ServerInfo
+ {
+ public bool Primary { get; set; }
+ public string ServerName { get; set; }
+ public string ServerVersion { get; set; }
+ public string FileName { get; set; }
+ public ServerLogger ConsoleBuffer { get; set; }
+ public Property ServerPath { get; set; }
+ public Property ServerExeName { get; set; }
+ public Property BackupPath { get; set; }
+ public Property MaxBackupCount { get; set; }
+ public Property AdvancedBackup { get; set; }
+ public Property LogToFileEnabled { get; set; }
+ public List KnownPlayers = new List();
+ public List ServerPropList = new List();
+ public List StartCmds = new List();
+
+ public enum PermissionLevels
+ {
+ Visitor,
+ Member,
+ Operator
+ }
+
+ public void InitDefaults()
+ {
+ ServerName = "Default Server";
+ FileName = "Default.conf";
+ ServerPath = new Property("ServerPath", @"C:\Program Files (x86)\Minecraft Bedrock Server Launcher\Servers\Server");
+ ServerExeName = new Property("ServerExeName", "bedrock_server.exe");
+ BackupPath = new Property("BackupPath", "Default");
+ MaxBackupCount = new Property("MaxBackupCount", "10");
+ AdvancedBackup = new Property("AdvancedBackup", "false");
+ LogToFileEnabled = new Property("LogToFile", "false");
+
+ ServerPropList.Add(new Property("server-name", "Default"));
+ ServerPropList.Add(new Property("gamemode", "creative"));
+ ServerPropList.Add(new Property("difficulty", "easy"));
+ ServerPropList.Add(new Property("allow-cheats", "false"));
+ ServerPropList.Add(new Property("max-players", "10"));
+ ServerPropList.Add(new Property("online-mode", "true"));
+ ServerPropList.Add(new Property("white-list", "false"));
+ ServerPropList.Add(new Property("server-port", "19132"));
+ ServerPropList.Add(new Property("server-portv6", "19133"));
+ ServerPropList.Add(new Property("view-distance", "32"));
+ ServerPropList.Add(new Property("tick-distance", "4"));
+ ServerPropList.Add(new Property("player-idle-timeout", "30"));
+ ServerPropList.Add(new Property("max-threads", "8"));
+ ServerPropList.Add(new Property("level-name", "Bedrock Level"));
+ ServerPropList.Add(new Property("level-seed", ""));
+ ServerPropList.Add(new Property("default-player-permission-level", "member"));
+ ServerPropList.Add(new Property("texturepack-required", "false"));
+ ServerPropList.Add(new Property("content-log-file-enabled", "false"));
+ ServerPropList.Add(new Property("compression-threshold", "1"));
+ ServerPropList.Add(new Property("server-authoritative-movement", "server-auth"));
+ ServerPropList.Add(new Property("player-movement-score-threshold", "20"));
+ ServerPropList.Add(new Property("player-movement-distance-threshold", "0.3"));
+ ServerPropList.Add(new Property("player-movement-duration-threshold-in-ms", "500"));
+ ServerPropList.Add(new Property("correct-player-movement", "false"));
+
+ }
+
+ public void SetServerProp(string name, string newValue)
+ {
+ Property serverProp = ServerPropList.FirstOrDefault(prop => prop.KeyName == name);
+ ServerPropList[ServerPropList.IndexOf(serverProp)].Value = newValue;
+ }
+
+ public void SetServerPropDefault(string name)
+ {
+ Property serverProp = ServerPropList.FirstOrDefault(prop => prop.KeyName == name);
+ ServerPropList[ServerPropList.IndexOf(serverProp)].Value = serverProp.DefaultValue;
+ }
+
+ public Property GetServerProp(string name)
+ {
+ return ServerPropList.FirstOrDefault(prop => prop.KeyName == name);
+ }
+
+ public void AddStartCmdEntry(string command)
+ {
+ StartCmds.Add(new StartCmdEntry(command));
+ }
+
+ public bool DeleteStartCmdEntry(string command)
+ {
+ StartCmdEntry entry = StartCmds.FirstOrDefault(prop => prop.Command == command);
+ return StartCmds.Remove(entry);
+ }
+
+ public override string ToString()
+ {
+ return ServerName;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/BedrockService/Service/Server/HostInfoClasses/StartCmdEntry.cs b/BedrockService/Service/Server/HostInfoClasses/StartCmdEntry.cs
new file mode 100644
index 00000000..dcfcfde8
--- /dev/null
+++ b/BedrockService/Service/Server/HostInfoClasses/StartCmdEntry.cs
@@ -0,0 +1,13 @@
+namespace BedrockService.Service.Server.HostInfoClasses
+{
+
+ public class StartCmdEntry
+ {
+ public string Command = "help 1";
+
+ public StartCmdEntry(string entry)
+ {
+ Command = entry;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BedrockService/Service/Server/Logging/ServerLogger.cs b/BedrockService/Service/Server/Logging/ServerLogger.cs
new file mode 100644
index 00000000..cc15ff5d
--- /dev/null
+++ b/BedrockService/Service/Server/Logging/ServerLogger.cs
@@ -0,0 +1,89 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace BedrockService.Service.Server.Logging
+{
+ public class ServerLogger
+ {
+ private List Log = new List();
+ private StringBuilder OutString = new StringBuilder();
+ [NonSerialized]
+ private StreamWriter LogWriter;
+ private bool LogToFile = false;
+ private bool LogToConsole = false;
+ private readonly string ServerName = "";
+ private string LogDir = $@"{Program.ServiceDirectory}\Server\Logs";
+
+ public ServerLogger(bool enableWriteToFile, string serverName)
+ {
+ LogToFile = enableWriteToFile;
+ ServerName = serverName;
+ LogToConsole = true;
+ if (LogToFile)
+ {
+ if (!Directory.Exists(LogDir))
+ Directory.CreateDirectory(LogDir);
+ LogWriter = new StreamWriter($@"{LogDir}\ServerLog_{serverName}_{DateTime.Now:yyyymmddhhmmss}.log", true);
+ }
+ }
+
+ [JsonConstructor]
+ public ServerLogger(string serverName)
+ {
+ ServerName = serverName;
+ LogToFile = false;
+ LogToConsole = false;
+ }
+
+ public void AppendLine(string text)
+ {
+ Log.Add(text);
+ if (LogToFile && LogWriter != null)
+ {
+ LogWriter.WriteLine(text);
+ LogWriter.Flush();
+ }
+ if(LogToConsole)
+ Console.WriteLine(text);
+ }
+
+ public void Append(string text)
+ {
+ Log.Add(text);
+ if (LogToFile && LogWriter != null)
+ {
+ LogWriter.Write(text);
+ LogWriter.Flush();
+ }
+ if (LogToConsole)
+ {
+ Console.Write(text);
+ Console.Out.Flush();
+ }
+ }
+
+ public int Count()
+ {
+ return Log.Count;
+ }
+
+ public string FromIndex(int index)
+ {
+ return Log[index];
+ }
+
+ public override string ToString()
+ {
+ OutString = new StringBuilder();
+ foreach (string s in Log)
+ {
+ OutString.Append(s);
+ }
+ return OutString.ToString();
+ }
+ }
+}
diff --git a/BedrockService/Service/Server/Management/PlayerManager.cs b/BedrockService/Service/Server/Management/PlayerManager.cs
new file mode 100644
index 00000000..276f8cf5
--- /dev/null
+++ b/BedrockService/Service/Server/Management/PlayerManager.cs
@@ -0,0 +1,104 @@
+using BedrockService.Service.Server.HostInfoClasses;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace BedrockService.Service.Server.Management
+{
+ public class PlayerManager
+ {
+ public void PlayerConnected(string username, string xuid, ServerInfo serverInstance)
+ {
+ Player playerFound = serverInstance.KnownPlayers.FirstOrDefault(ply => ply.XUID == xuid);
+ if (playerFound != null)
+ {
+ playerFound.LastConnectedTime = DateTime.Now.Ticks.ToString();
+ if (playerFound.FirstConnectedTime == null)
+ {
+ playerFound.FirstConnectedTime = playerFound.LastConnectedTime;
+ }
+ InstanceProvider.GetConfigManager().SaveKnownPlayerDatabase(serverInstance);
+ }
+ else
+ {
+ playerFound = new Player(xuid, username)
+ {
+ FirstConnectedTime = DateTime.Now.Ticks.ToString()
+ };
+ playerFound.LastConnectedTime = playerFound.FirstConnectedTime;
+ serverInstance.KnownPlayers.Add(playerFound);
+ InstanceProvider.GetConfigManager().SaveKnownPlayerDatabase(serverInstance);
+ }
+
+ }
+
+ public void PlayerDisconnected(string xuid, ServerInfo serverInstance)
+ {
+ Player playerFound = serverInstance.KnownPlayers.FirstOrDefault(ply => ply.XUID == xuid);
+ playerFound.LastDisconnectTime = DateTime.Now.Ticks.ToString();
+ InstanceProvider.GetConfigManager().SaveKnownPlayerDatabase(serverInstance);
+ }
+
+ public void UpdatePlayerFromCfg(string xuid, string username, string permission, string whitelisted, string ignoreMaxPlayerLimit, ServerInfo serverInstance)
+ {
+ Player playerFound = serverInstance.KnownPlayers.FirstOrDefault(ply => ply.XUID == xuid);
+ if (playerFound != null)
+ {
+ playerFound.PermissionLevel = permission;
+ playerFound.Whitelisted = bool.Parse(whitelisted);
+ playerFound.IgnorePlayerLimits = bool.Parse(ignoreMaxPlayerLimit);
+ playerFound.FromConfig = true;
+ }
+ else
+ {
+ playerFound = new Player(xuid, username)
+ {
+ PermissionLevel = permission,
+ Whitelisted = bool.Parse(whitelisted),
+ IgnorePlayerLimits = bool.Parse(ignoreMaxPlayerLimit),
+ FromConfig = true
+ };
+ serverInstance.KnownPlayers.Add(playerFound);
+ }
+ }
+
+ public List ListKnownConfiguredPlayers(ServerInfo serverInstance)
+ {
+ List tempList = new List();
+ if (serverInstance.KnownPlayers.Count > 0)
+ {
+ foreach (Player player in serverInstance.KnownPlayers)
+ {
+ if (player.FromConfig)
+ tempList.Add(player);
+ }
+ }
+ return tempList;
+ }
+
+ public List SearchPlayers(string cmd, string arg, string stringToMatch, ServerInfo serverInstance)
+ {
+ List tempList = new List();
+ foreach (Player player in serverInstance.KnownPlayers)
+ {
+ switch (cmd)
+ {
+ case "ByName":
+ if (player.Username.Contains(stringToMatch))
+ {
+ tempList.Add(player);
+ }
+ break;
+ case "ByPermLevel":
+ if (player.PermissionLevel == arg)
+ {
+ tempList.Add(player);
+ }
+ break;
+ }
+ }
+ return tempList;
+ }
+
+ }
+}
diff --git a/BedrockService/Service/Utilities/JsonParser.cs b/BedrockService/Service/Utilities/JsonParser.cs
new file mode 100644
index 00000000..cda5cf9f
--- /dev/null
+++ b/BedrockService/Service/Utilities/JsonParser.cs
@@ -0,0 +1,27 @@
+using Newtonsoft.Json.Linq;
+using System;
+
+namespace BedrockService.Utilities
+{
+ public class JsonParser
+ {
+ public Type Type { get; set; }
+ public JToken Value { get; set; }
+
+ public static JsonParser FromValue(T value)
+ {
+ return new JsonParser { Type = typeof(T), Value = JToken.FromObject(value) };
+ }
+
+ public static string Serialize(JsonParser message)
+ {
+ return JToken.FromObject(message).ToString();
+ }
+
+ public static JsonParser Deserialize(string data)
+ {
+ return JToken.Parse(data).ToObject();
+ }
+ }
+
+}
diff --git a/BedrockService/Service/packages.config b/BedrockService/Service/packages.config
new file mode 100644
index 00000000..5eaa239b
--- /dev/null
+++ b/BedrockService/Service/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/BedrockService/packages.config b/BedrockService/packages.config
new file mode 100644
index 00000000..e9f81015
--- /dev/null
+++ b/BedrockService/packages.config
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..91282835
--- /dev/null
+++ b/README.md
@@ -0,0 +1,234 @@
+# BedrockService
+
+# 2.0Beta notes!
+## Readme is out of date! Current beta is not complete enough to post a readme yet. Hovever, some intuitive browsing through the default files that generate after the first run will give an easy idea of how things work. Progress has picked back up on this project. Expect a true release and a proper readme soon!
+
+Moddified by Crowbarmaster brings you:
+
+Windows Service Wrapper around Bedrock Server for Minecraft
+
+Built on original code written by RaveTroll and supporters
+
+This service wrapper for Minecraft Bedrock has been updated to provide
+
+automatically applied updates, new configuration loader, and a process watchdog.
+
+Initial installation:
+
+Extract files to a directory of your chosing. If you want a quick-start jump-right-in experience,
+
+launch BedrockService.exe, accept the terms, and watch your new server launch in seconds!
+
+See below for a full, flexable, single or multi-server setup.
+
+Configuration:
+
+Server configuration(s) are found in the Configs folder. Default package ships with a "Globals.conf"
+
+and "Default.conf". Configs are in plain-text format. Configuration sections are defined by wrapping them with "[]".
+
+Globals File:
+
+```
+ [Globals]
+ BackupEnabled=false // Enable backups here
+ BackupCron=0 1 * * * // Set Cron interval for backups here
+ AcceptedMojangLic=false // Mojang Licence accepted? NOTE: Must be set "true" here before installing as a service!
+ CheckUpdates=true // Enable update routines here...
+ UpdateCron=38 19 * * * // Set Cron interval for updates here
+```
+The Globals file contains just a few variables that will be shared between multiple servers.
+
+Note the AcceptedMojangLic entry. In order to fetch downloads from minecraft.net, you must agree to the Terms
+
+found at: https://minecraft.net/terms. If you plan to use this wrapper only to run multiple servers and NOT as a service,
+
+ignore this. You will be asked in the console to accept the terms. Otherwise if you will run servers-as-a-service, this must
+
+be set to "true" before running, or the build will not download.
+
+Default Server File:
+
+```
+ [Service_Default]
+ BedrockServerExeLocation=D:\MCSRV\MC1\
+ BedrockServerExeName=bedrock_server.exe
+ BackupFolderName=Default
+ WCFPortNumber=19134
+
+ [Server_Default]
+ server-name=Test
+ gamemode=creative
+ difficulty=easy
+ allow-cheats=false
+ max-players=10
+ online-mode=true
+ white-list=true
+ server-port=19132
+ server-portv6=19133
+ view-distance=32
+ tick-distance=4
+ player-idle-timeout=30
+ max-threads=8
+ level-name=Bedrock level
+ level-seed=
+ default-player-permission-level=member
+ texturepack-required=false
+ content-log-file-enabled=false
+ compression-threshold=1
+ server-authoritative-movement=server-auth
+ player-movement-score-threshold=20
+ player-movement-distance-threshold=0.3
+ player-movement-duration-threshold-in-ms=500
+ correct-player-movement=false
+
+ [Perms_Default]
+ visitor=5555555555555555
+
+ [Whitelist_Default]
+ test=5555555555555555,false
+
+ [StartCmds_Default]
+ CmdDesc=help 1
+```
+
+This default file contains all information to run a single server, including minecraft-specific configs.
+
+If you plan only to run a single server instance, you don’t need to change anything but what you need to.
+
+To run more than a single server instance, create a copy of the default configuration file. The name of the
+
+file you create does not matter, so long as the extension remains ".conf". In order to successfully launch
+
+multiple servers:
+
+ -All Config section definitions must be changed from "xxxxx_Default" to "xxxxx_YouDefineMe". "YouDefineMe" can be
+
+ any char sequence/phase you wish and should be unique between servers.
+
+ This will now be used by the wrapper to internally define this server.
+
+ -Every entry found in the [Service_YouDefineMe] section must be both unique between servers, and point to unused resources.
+
+ -The standard interference Minecraft settings, including server-name and ports, must be unique.
+
+Everything else can be customized to your liking.
+
+Perms_YouDefineMe & Whitelist_YouDefineMe are sections of the config that will push these files to the server root directory.
+
+The Perms section will create entries in permissions.json. Each permission is formatted as DesiredPermissionLevel=PlayersXUID.
+
+Default.conf's example:
+
+```
+[Perms_Default]
+visitor=5555555555555555
+```
+
+Will output "permissions.json" to selected BedrockServerExeLocation that looks like:
+
+```
+[
+ {
+ "permission": "visitor",
+ "xuid": "5555555555555555"
+ }
+]
+```
+
+All Whitelist entries will output to "whitelist.json" in the same formatting as Perms, however formatted in the config
+
+a bit differently: MCUsername=XUID,IgnoresPlayerLimit
+
+Example:
+
+```
+[Whitelist_Default]
+test=5555555555555555,false
+```
+
+Translates to:
+
+```
+[
+ {
+ "username": "test",
+ "xuid": "5555555555555555",
+ "ignoresPlayerLimit": false
+ }
+]
+```
+
+
+Backups by default ("BackupFolderName=Default" in config) will backup servers to "backups\ServerShortName\Backup_DateString" unless changed to a custom path.
+
+Backup system backs up the entire minecraft folder, in the case that resource packs are install, they are also backed up.
+
+
+StartCmds are passable commandline startup options to this server. Each command defined in this format: CommandDescription=CommandToPass
+
+CommandDescription is meaningless, and can be the same for each, or unique to you liking.
+
+Service installation is simmilar to before, however you need not set permissions on the MC server folder, since it will be created by the service.
+
+1. You need to give permissions to SYSTEM to Modify the directory where BedrockService is located.
+
+2. Start a command prompt console with admin priviledges and navigate to the directory where you unzipped BedrockService.
+```
+ Type: bedrockservice install
+ then
+ Type: bedrockservice start
+```
+If you need to uninstall BedrockService Start a command prompt console with admin priviledges and navigate to the directory where you unzipped BedrockService.
+```
+ Type: bedrockservice stop
+ then
+ Type: bedrockservice uninstall
+```
+
+!! Notice: To ensure proper opperation, I highly reccomend you check "Run program as an administrator" in the Properties>Compatabity tab of BedrockService.exe !!
+**************************************************************************************
+
+Original Readme:
+
+Windows Service Wrapper around Bedrock Server for Minecraft
+
+Lets you run Bedrock Server as a Windows Service
+
+This approach does NOT require Docker.
+
+There is a Windows Server Software for Windows to allow users to run a multiplayer Minecraft server.
+
+You can get it at https://www.minecraft.net/en-us/download/server/bedrock/
+
+Its easy to install and runs as a console application.
+
+What if you want it to run invisibly on your computer whenever it starts and shutdown statefully whenever your computer shuts?
+
+Enter BedrockService, the little control program that performs just that task for you. Download it here: https://github.com/ravetroll/BedrockService/raw/master/Releases/BedrockService.exe.zip
+
+To configure it you have to do 4 things:
+
+1. Unzip the BedrockService.exe zip to a directory on your computer.
+
+2. You have to put the path to your copy of bedrock_server.exe in the BedrockService.exe.config file. Make sure you have run your bedrock server in console mode first to be sure it works.
+
+3. You need to give permissions to SYSTEM to Modify both the directory with BedrockService as well as the directory containing bedrock_server.exe
+
+4. Start a command prompt console with admin priviledges and navigate to the directory where you unzipped BedrockService.
+```
+ Type: bedrockservice install
+ then
+ Type: bedrockservice start
+```
+If you need to uninstall BedrockService Start a command prompt console with admin priviledges and navigate to the directory where you unzipped BedrockService.
+```
+ Type: bedrockservice stop
+ then
+ Type: bedrockservice uninstall
+```
+
+If you have some problems getting the service running Check in Windows Event Log in the Application Events for events related to BedrockService. That might help you find the problem.
+
+See the wiki at https://github.com/ravetroll/BedrockService/wiki
+