From 0f3597bdcc7261009529c7d0b7ef4ded799a0228 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Thu, 26 May 2022 09:57:54 +0200 Subject: [PATCH 1/4] fix: server: internal properties/methods could be set/called --- server/src/network/session.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/network/session.cpp b/server/src/network/session.cpp index d62f8b88..b19f6427 100644 --- a/server/src/network/session.cpp +++ b/server/src/network/session.cpp @@ -157,7 +157,7 @@ bool Session::processMessage(const Message& message) { if(ObjectPtr object = m_handles.getItem(message.read())) { - if(AbstractProperty* property = object->getProperty(message.read())) + if(AbstractProperty* property = object->getProperty(message.read()); property && !property->isInternal()) { try { @@ -206,7 +206,7 @@ bool Session::processMessage(const Message& message) { if(ObjectPtr object = m_handles.getItem(message.read())) { - if(AbstractUnitProperty* property = dynamic_cast(object->getProperty(message.read()))) + if(AbstractUnitProperty* property = dynamic_cast(object->getProperty(message.read())); property && !property->isInternal()) { try { @@ -225,7 +225,7 @@ bool Session::processMessage(const Message& message) { if(ObjectPtr object = m_handles.getItem(message.read())) { - if(AbstractObjectProperty* property = dynamic_cast(object->getProperty(message.read()))) + if(AbstractObjectProperty* property = dynamic_cast(object->getProperty(message.read())); property && !property->isInternal()) { try { @@ -253,7 +253,7 @@ bool Session::processMessage(const Message& message) { if(ObjectPtr object = m_handles.getItem(message.read())) { - if(AbstractMethod* method = object->getMethod(message.read())) + if(AbstractMethod* method = object->getMethod(message.read()); method && !method->isInternal()) { const ValueType resultType = message.read(); const uint8_t argumentCount = message.read(); From 1a8c17fb50798d1a64e636f0f33ea91edbc04fac Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Thu, 26 May 2022 09:59:09 +0200 Subject: [PATCH 2/4] trayicon: added allow client server restart/shutdown options to popupmenu --- server/src/os/windows/trayicon.cpp | 66 ++++++++++++++++++++++++++++++ server/src/os/windows/trayicon.hpp | 15 +++++++ 2 files changed, 81 insertions(+) diff --git a/server/src/os/windows/trayicon.cpp b/server/src/os/windows/trayicon.cpp index 3c70a413..c2ec836b 100644 --- a/server/src/os/windows/trayicon.cpp +++ b/server/src/os/windows/trayicon.cpp @@ -27,6 +27,7 @@ #include "consolewindow.hpp" #include "../../core/eventloop.hpp" #include "../../traintastic/traintastic.hpp" +#include "../../utils/setthreadname.hpp" namespace Windows { @@ -50,6 +51,8 @@ void TrayIcon::remove() void TrayIcon::run() { + setThreadName("trayicon"); + const LPCSTR windowClassName = "TraintasticServerTrayIcon"; // register window class: @@ -73,6 +76,9 @@ void TrayIcon::run() s_menu = CreatePopupMenu(); menuAddItem(MenuItem::ShowHideConsole, "Show/hide console", hasConsoleWindow()); menuAddSeperator(); + menuAddItem(MenuItem::AllowClientServerRestart, "Allow client to restart server"); + menuAddItem(MenuItem::AllowClientServerShutdown, "Allow client to shutdown server"); + menuAddSeperator(); menuAddItem(MenuItem::Restart, "Restart"); menuAddItem(MenuItem::Shutdown, "Shutdown"); @@ -97,6 +103,9 @@ void TrayIcon::run() Shell_NotifyIcon(NIM_ADD, ¬ifyIconData); + // Get settings: + EventLoop::call(getSettings); + // message loop: while(true) { @@ -166,8 +175,47 @@ LRESULT CALLBACK TrayIcon::windowProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARA case MenuItem::ShowHideConsole: setConsoleWindowVisible(!isConsoleWindowVisible()); return 0; + + case MenuItem::AllowClientServerRestart: + { + bool value; + { + std::lock_guard lock{s_settings.mutex}; + value = !s_settings.allowClientServerRestart; + } + EventLoop::call( + [value]() + { + Traintastic::instance->settings->allowClientServerRestart = value; + getSettings(); + }); + break; + } + case MenuItem::AllowClientServerShutdown: + { + bool value; + { + std::lock_guard lock{s_settings.mutex}; + value = !s_settings.allowClientServerShutdown; + } + EventLoop::call( + [value]() + { + Traintastic::instance->settings->allowClientServerShutdown = value; + getSettings(); + }); + break; + } } break; + + case WM_TRAINTASTIC_SETTINGS: + { + std::lock_guard lock{s_settings.mutex}; + menuSetItemChecked(MenuItem::AllowClientServerRestart, s_settings.allowClientServerRestart); + menuSetItemChecked(MenuItem::AllowClientServerShutdown, s_settings.allowClientServerShutdown); + break; + } } return DefWindowProc(hWnd, uMsg, wParam, lParam); } @@ -197,4 +245,22 @@ void TrayIcon::menuAddSeperator() InsertMenuItem(s_menu, GetMenuItemCount(s_menu), TRUE, &item); } +void TrayIcon::menuSetItemChecked(MenuItem id, bool checked) +{ + assert(s_menu); + CheckMenuItem(s_menu, static_cast(id), checked ? MF_CHECKED : MF_UNCHECKED); +} + +void TrayIcon::getSettings() +{ + assert(isEventLoopThread()); + const auto& settings = *Traintastic::instance->settings.value(); + { + std::lock_guard lock{s_settings.mutex}; + s_settings.allowClientServerRestart = settings.allowClientServerRestart; + s_settings.allowClientServerShutdown = settings.allowClientServerShutdown; + } + PostMessage(s_window, WM_TRAINTASTIC_SETTINGS, 0, 0); +} + } diff --git a/server/src/os/windows/trayicon.hpp b/server/src/os/windows/trayicon.hpp index f5154699..bfdcb958 100644 --- a/server/src/os/windows/trayicon.hpp +++ b/server/src/os/windows/trayicon.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #define _WINSOCKAPI_ // prevent windows.h including winsock.h #include @@ -43,19 +44,33 @@ class TrayIcon Shutdown = 1, Restart = 2, ShowHideConsole = 3, + AllowClientServerRestart = 4, + AllowClientServerShutdown = 5, + }; + + struct TraintasticSettings + { + std::mutex mutex; + bool allowClientServerRestart; + bool allowClientServerShutdown; }; static constexpr UINT WM_NOTIFYICON_CALLBACK = WM_USER + 1; + static constexpr UINT WM_TRAINTASTIC_SETTINGS = WM_USER + 2; static std::unique_ptr s_thread; static HWND s_window; static HMENU s_menu; + inline static TraintasticSettings s_settings = {{}, false, false}; static void run(); static LRESULT CALLBACK windowProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); static void menuAddItem(MenuItem id, const LPSTR text, bool enabled = true); static void menuAddSeperator(); + static void menuSetItemChecked(MenuItem id, bool checked); + + static void getSettings(); public: static void add(); From 7d659c2360da606d149d517882ffe08aad68e083 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Thu, 26 May 2022 10:00:20 +0200 Subject: [PATCH 3/4] settings: client may no longer change server restart/shutdown settings --- server/src/traintastic/settings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/traintastic/settings.cpp b/server/src/traintastic/settings.cpp index ce0769c2..8ad8894c 100644 --- a/server/src/traintastic/settings.cpp +++ b/server/src/traintastic/settings.cpp @@ -54,8 +54,8 @@ Settings::Settings(const std::filesystem::path& path) , loadLastWorldOnStartup{this, "load_last_world_on_startup", true, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }} , autoSaveWorldOnExit{this, "auto_save_world_on_exit", false, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }} , saveWorldUncompressed{this, "save_world_uncompressed", false, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }} - , allowClientServerRestart{this, "allow_client_server_restart", false, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }} - , allowClientServerShutdown{this, "allow_client_server_shutdown", false, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }} + , allowClientServerRestart{this, "allow_client_server_restart", false, PropertyFlags::ReadWrite | PropertyFlags::Internal, [this](const bool& /*value*/){ saveToFile(); }} + , allowClientServerShutdown{this, "allow_client_server_shutdown", false, PropertyFlags::ReadWrite | PropertyFlags::Internal, [this](const bool& /*value*/){ saveToFile(); }} , memoryLoggerSize{this, Name::memoryLoggerSize, Default::memoryLoggerSize, PropertyFlags::ReadWrite, [this](const uint32_t& /*value*/){ saveToFile(); }} , enableFileLogger{this, Name::enableFileLogger, Default::enableFileLogger, PropertyFlags::ReadWrite, [this](const bool& /*value*/){ saveToFile(); }} { From ccd5ca3bc3593dd3ee2b87165288ecceea7465c0 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Fri, 27 May 2022 18:45:07 +0200 Subject: [PATCH 4/4] tray: added Start automatically at logon option to menu --- server/src/os/windows/registry.cpp | 97 ++++++++++++++++++++++++++++++ server/src/os/windows/registry.hpp | 35 +++++++++++ server/src/os/windows/trayicon.cpp | 23 +++++++ server/src/os/windows/trayicon.hpp | 2 + 4 files changed, 157 insertions(+) create mode 100644 server/src/os/windows/registry.cpp create mode 100644 server/src/os/windows/registry.hpp diff --git a/server/src/os/windows/registry.cpp b/server/src/os/windows/registry.cpp new file mode 100644 index 00000000..29469f25 --- /dev/null +++ b/server/src/os/windows/registry.cpp @@ -0,0 +1,97 @@ +/** + * server/src/os/windows/registry.cpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2022 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "registry.hpp" +#include +#include + +namespace Windows::Registry { + +const char* runKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; +//const char* runTraintasticServerKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\TraintasticServer"; +const char* startupApprovedKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run"; +const char* traintasticServerKey = "TraintasticServer"; +const WCHAR* traintasticServerKeyW = L"TraintasticServer"; +const DWORD startupEnabled = 2; +const DWORD startupDisabled = 3; + +bool addRun() +{ + bool success = false; + HKEY key; + LSTATUS r = RegOpenKeyExA(HKEY_CURRENT_USER, runKey, 0, KEY_WRITE, &key); + if(r == ERROR_SUCCESS) + { + std::basic_string path; + path.resize(MAX_PATH); + path[0] = '"'; + const DWORD l = GetModuleFileNameW(nullptr, path.data() + 1, path.size() - 1); + if(l > 0) + { + path.resize(1 + l); + path.append(L"\" --tray"); + + r = RegSetValueExW(key, traintasticServerKeyW, 0, REG_SZ, reinterpret_cast(path.c_str()), (path.size() + 1) * sizeof(WCHAR)); + success = (r == ERROR_SUCCESS); + } + RegCloseKey(key); + } + return success; +} + +bool getStartUpApproved(bool& enabled) +{ + bool success = false; + HKEY key; + LSTATUS r = RegOpenKeyExA(HKEY_CURRENT_USER, startupApprovedKey, 0, KEY_READ, &key); + if(r == ERROR_SUCCESS) + { + DWORD data[3]; + DWORD size = sizeof(data); + r = RegGetValueA(key, nullptr, traintasticServerKey, RRF_RT_REG_BINARY, nullptr, &data, &size); + if(r == ERROR_SUCCESS) + { + enabled = (data[0] == startupEnabled); + success = true; + } + RegCloseKey(key); + } + return success; +} + +bool setStartUpApproved(bool enabled) +{ + bool success = false; + HKEY key; + LSTATUS r = RegOpenKeyExA(HKEY_CURRENT_USER, startupApprovedKey, 0, KEY_WRITE, &key); + if(r == ERROR_SUCCESS) + { + DWORD data[3] = {enabled ? startupEnabled : startupDisabled, 0, 0}; + DWORD size = sizeof(data); + r = RegSetValueExA(key, traintasticServerKey, 0, REG_BINARY, reinterpret_cast(&data), size); + success = (r == ERROR_SUCCESS); + RegCloseKey(key); + } + return success; +} + +} diff --git a/server/src/os/windows/registry.hpp b/server/src/os/windows/registry.hpp new file mode 100644 index 00000000..884bbfc3 --- /dev/null +++ b/server/src/os/windows/registry.hpp @@ -0,0 +1,35 @@ +/** + * server/src/os/windows/registry.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2022 Reinder Feenstra + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_OS_WINDOWS_REGISTRY_HPP +#define TRAINTASTIC_SERVER_OS_WINDOWS_REGISTRY_HPP + +namespace Windows::Registry { + +bool addRun(); + +bool getStartUpApproved(bool& enabled); +bool setStartUpApproved(bool enabled); + +} + +#endif diff --git a/server/src/os/windows/trayicon.cpp b/server/src/os/windows/trayicon.cpp index c2ec836b..97e3c228 100644 --- a/server/src/os/windows/trayicon.cpp +++ b/server/src/os/windows/trayicon.cpp @@ -25,6 +25,7 @@ #include #include #include "consolewindow.hpp" +#include "registry.hpp" #include "../../core/eventloop.hpp" #include "../../traintastic/traintastic.hpp" #include "../../utils/setthreadname.hpp" @@ -79,9 +80,15 @@ void TrayIcon::run() menuAddItem(MenuItem::AllowClientServerRestart, "Allow client to restart server"); menuAddItem(MenuItem::AllowClientServerShutdown, "Allow client to shutdown server"); menuAddSeperator(); + menuAddItem(MenuItem::StartAutomaticallyAtLogon, "Start automatically at logon"); + menuAddSeperator(); menuAddItem(MenuItem::Restart, "Restart"); menuAddItem(MenuItem::Shutdown, "Shutdown"); + bool startUpApproved = false; + Registry::getStartUpApproved(startUpApproved); + menuSetItemChecked(MenuItem::StartAutomaticallyAtLogon, startUpApproved); + // setup tray icon: static NOTIFYICONDATA notifyIconData; memset(¬ifyIconData, 0, sizeof(notifyIconData)); @@ -206,6 +213,16 @@ LRESULT CALLBACK TrayIcon::windowProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARA }); break; } + case MenuItem::StartAutomaticallyAtLogon: + { + bool startUpApproved = !menuGetItemChecked(MenuItem::StartAutomaticallyAtLogon); + if(startUpApproved) + Registry::addRun(); + Registry::setStartUpApproved(startUpApproved); + if(Registry::getStartUpApproved(startUpApproved)) + menuSetItemChecked(MenuItem::StartAutomaticallyAtLogon, startUpApproved); + break; + } } break; @@ -245,6 +262,12 @@ void TrayIcon::menuAddSeperator() InsertMenuItem(s_menu, GetMenuItemCount(s_menu), TRUE, &item); } +bool TrayIcon::menuGetItemChecked(MenuItem id) +{ + assert(s_menu); + return GetMenuState(s_menu, static_cast(id), MF_BYCOMMAND) & MF_CHECKED; +} + void TrayIcon::menuSetItemChecked(MenuItem id, bool checked) { assert(s_menu); diff --git a/server/src/os/windows/trayicon.hpp b/server/src/os/windows/trayicon.hpp index bfdcb958..fae41a64 100644 --- a/server/src/os/windows/trayicon.hpp +++ b/server/src/os/windows/trayicon.hpp @@ -46,6 +46,7 @@ class TrayIcon ShowHideConsole = 3, AllowClientServerRestart = 4, AllowClientServerShutdown = 5, + StartAutomaticallyAtLogon = 6, }; struct TraintasticSettings @@ -68,6 +69,7 @@ class TrayIcon static void menuAddItem(MenuItem id, const LPSTR text, bool enabled = true); static void menuAddSeperator(); + static bool menuGetItemChecked(MenuItem id); static void menuSetItemChecked(MenuItem id, bool checked); static void getSettings();