Initial commit

This commit is contained in:
2025-12-17 16:47:48 +00:00
commit 13813f3363
4964 changed files with 1079753 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
add_executable(Server
resources/qt.qrc
src/main.cpp
src/server.cpp
src/server.hpp
src/grid.cpp
src/grid.hpp
src/AppView.hpp
src/AppView.cpp
src/MainWindow.cpp
src/MainWindow.hpp
src/linenoise.hpp
${CLIENT_DIR}/common/AppSettings.cpp
${CLIENT_DIR}/common/AppSettings.hpp
${CLIENT_DIR}/common/SDLGameController.cpp
${CLIENT_DIR}/common/SDLGameController.hpp
)
target_link_libraries(Server
3D
AppServer
Core
RakNet
BulletPhysics
NetworkServer
Graphics
)
if(AYA_OS_WINDOWS)
target_sources(Server PRIVATE
resources/winrc.h
resources/script.rc
)
set_target_properties(Server PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
target_compile_definitions(Server PRIVATE QT_NO_KEYWORDS)
target_include_directories(Server PRIVATE src resources)
set_target_properties(Server PROPERTIES OUTPUT_NAME "Aya.Server")
add_custom_command(TARGET Server POST_BUILD
COMMENT "Copying runtime files to build directory"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${RUNTIME_FILES}"
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>icon.ico</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,49 @@
#include "winrc.h"
#if defined(__MINGW64__) || defined(__MINGW32__)
// MinGW-w64, MinGW
#if defined(__has_include) && __has_include(<winres.h>)
#include <winres.h>
#else
#include <afxres.h>
#include <winresrc.h>
#endif
#else
// MSVC, Windows SDK
#include <winres.h>
#endif
IDI_ICON1 ICON APP_ICON
LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_RESOURCE
PRODUCTVERSION VERSION_RESOURCE
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", APP_ORGANIZATION
VALUE "FileDescription", APP_DESCRIPTION
VALUE "FileVersion", VERSION_RESOURCE_STR
VALUE "LegalCopyright", APP_COPYRIGHT
VALUE "ProductName", APP_NAME
VALUE "ProductVersion", VERSION_RESOURCE_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", PRODUCT_LANGUAGE, PRODUCT_CHARSET
END
END

View File

@@ -0,0 +1,24 @@
#pragma once
#define VERSION_MAJOR_MINOR_STR AYA_VERSION_MAJOR_STR "." AYA_VERSION_MINOR_STR
#define VERSION_MAJOR_MINOR_PATCH_STR VERSION_MAJOR_MINOR_STR "." AYA_VERSION_PATCH_STR
#ifdef AYA_VERSION_TYPE
#define VERSION_FULL_STR VERSION_MAJOR_MINOR_PATCH_STR "-" AYA_VERSION_TYPE
#else
#define VERSION_FULL_STR VERSION_MAJOR_MINOR_PATCH_STR
#endif
#define VERSION_RESOURCE AYA_VERSION_MAJOR, AYA_VERSION_MINOR, AYA_VERSION_PATCH, 0
#define VERSION_RESOURCE_STR VERSION_FULL_STR "\0"
/*
* These properties are part of VarFileInfo.
* For more information, please see: https://learn.microsoft.com/en-us/windows/win32/menurc/varfileinfo-block
*/
#define PRODUCT_LANGUAGE 0x0409 // en-US
#define PRODUCT_CHARSET 1200 // Unicode
#define APP_ICON "icon.ico"
#define APP_NAME AYA_PROJECT_NAME "\0"
#define APP_DESCRIPTION AYA_PROJECT_NAME " Server\0"
#define APP_ORGANIZATION AYA_PROJECT_NAME "\0"
#define APP_COPYRIGHT AYA_PROJECT_NAME " License\0"

View File

@@ -0,0 +1,56 @@
#include "AppView.hpp"
#include <QWebEngineSettings>
#include <QWebEngineScript>
#include <QWebEngineScriptCollection>
#include <QNetworkRequest>
#include <QMessageBox>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QInputDialog>
#include <QProgressDialog>
#include <QFrame>
#include <QVBoxLayout>
#include <QString>
#include <QSettings>
AppView::AppView(QWidget* parent, std::string mode)
: QWebEngineView(parent)
{
setContextMenuPolicy(Qt::NoContextMenu);
QWebEngineSettings* settings = page()->settings();
settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
settings->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, true);
settings->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
settings->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
settings->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true);
settings->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, true);
// pre-load the QWebChannel API
QFile apiFile(":/qtwebchannel/qwebchannel.js");
if (!apiFile.open(QIODevice::ReadOnly))
qDebug() << "Couldn't load Qt's QWebChannel API!";
QString apiScript = QString::fromLatin1(apiFile.readAll());
apiFile.close();
QWebEngineScript script;
script.setSourceCode(apiScript);
script.setName("QWebChannel");
script.setWorldId(QWebEngineScript::MainWorld);
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setRunsOnSubFrames(false);
page()->scripts().insert(script);
// setup the transport interface
transport = new AppTransport();
channel = new QWebChannel(page());
channel->registerObject(QStringLiteral("transport"), transport);
page()->setWebChannel(channel);
load(QUrl::fromLocalFile(QString::fromStdString(GetAssetFolder() + "/app/index.html?mode=" + mode)));
}
// #include "AppView.moc"

View File

@@ -0,0 +1,30 @@
#pragma once
#include <QObject>
#include <QNetworkAccessManager>
#include <QtWebChannel>
#include <QWebEngineView>
class AppTransport : public QObject
{
Q_OBJECT
QWidget* parent;
QNetworkAccessManager* manager;
};
class AppView : public QWebEngineView
{
Q_OBJECT
QWebChannel* channel;
AppTransport* transport;
public:
AppView(QWidget* parent, std::string mode);
AppTransport* getTransport()
{
return transport;
};
};

View File

@@ -0,0 +1,19 @@
#include "MainWindow.hpp"
#include <QIcon>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget* parent)
: QWidget(parent)
{
QWidget* widget = new QWidget(this);
app = new AppView(widget, "server");
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(widget);
setWindowIcon(QIcon(":/icon.ico"));
setMinimumSize(800, 600);
setMaximumSize(800, 600);
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <QWidget>
#include <QEvent>
#include "AppView.hpp"
class MainWindow : public QWidget
{
Q_OBJECT
AppView* app;
public:
MainWindow(QWidget* parent = NULL);
// ~MainWindow();
//
// bool event(QEvent* event);
};

205
client/server/src/grid.cpp Normal file
View File

@@ -0,0 +1,205 @@
#include "grid.hpp"
#include <boost/beast.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
boost::scoped_ptr<Grid> Grid::singleton;
Grid::Grid(int port)
: acceptor(ioc)
, running(false)
, port(port)
{
}
Grid::~Grid()
{
stop();
}
bool Grid::start()
{
if (running.load())
{
return false;
}
try
{
auto const address = net::ip::make_address("0.0.0.0");
tcp::endpoint endpoint{address, static_cast<unsigned short>(port)};
acceptor.open(endpoint.protocol());
acceptor.set_option(net::socket_base::reuse_address(true));
acceptor.bind(endpoint);
acceptor.listen(net::socket_base::max_listen_connections);
running.store(true);
server_thread = std::make_unique<std::thread>(&Grid::run_server, this);
std::cout << "Grid started on port " << port << std::endl;
return true;
}
catch (std::exception const& e)
{
std::cerr << "Error starting Grid: " << e.what() << std::endl;
return false;
}
}
void Grid::stop()
{
if (!running.load())
{
return;
}
running.store(false);
try
{
acceptor.close();
ioc.stop();
}
catch (std::exception const& e)
{
std::cerr << "Error stopping Grid: " << e.what() << std::endl;
}
if (server_thread && server_thread->joinable())
{
server_thread->join();
}
std::cout << "Grid stopped" << std::endl;
}
bool Grid::is_running() const
{
return running.load();
}
void Grid::run_server()
{
do_accept();
ioc.run();
}
void Grid::do_accept()
{
}
void Grid::handle_session(tcp::socket socket)
{
try
{
beast::flat_buffer buffer;
http::request<http::string_body> req;
// Read the request
http::read(socket, buffer, req);
http::response<http::string_body> res;
res.version(req.version());
res.set(http::field::server, "Grid/1.0");
res.set(http::field::content_type, "application/json");
res.set(http::field::access_control_allow_origin, "*");
res.set(http::field::access_control_allow_methods, "GET, POST");
res.set(http::field::access_control_allow_headers, "Content-Type");
std::string method = std::string(req.method_string());
std::string path = std::string(req.target());
std::string body = req.body();
/*
if (method == "GET" || method == "POST")
{
if (method == "POST" && !body.empty())
{
rapidjson::Document doc;
if (doc.Parse(body.c_str()).HasParseError())
{
res.result(http::status::bad_request);
res.body() = R"({"error":"Invalid JSON"})";
}
else
{
std::string response_body = handle_request(method, path, doc);
res.result(http::status::ok);
res.body() = response_body;
}
}
else
{
std::string response_body = handle_request(method, path, rapidjson::Document());
res.result(http::status::ok);
res.body() = response_body;
}
}
else
{
res.result(http::status::method_not_allowed);
res.body() = R"({"error":"Method not allowed"})";
}
*/
res.prepare_payload();
http::write(socket, res);
socket.shutdown(tcp::socket::shutdown_send);
}
catch (std::exception const& e)
{
std::cerr << "Session error: " << e.what() << std::endl;
}
}
std::string Grid::handle_request(const std::string& method, const std::string& path, const rapidjson::Document& json_body)
{
if (json_handler)
{
return json_handler(method, path, json_body);
}
return R"({"status":"ok","message":"Grid server is running"})";
}
bool Grid::is_valid_json(const std::string& str)
{
if (str.empty())
return false;
rapidjson::Document doc;
if (doc.Parse(str.c_str()).HasParseError())
{
return false;
}
return doc.IsObject() || doc.IsArray();
}
void start_grid_server(int port)
{
if (!Grid::singleton)
{
Grid::singleton.reset(new Grid(port));
}
Grid::singleton->start();
}
void stop_grid_server()
{
if (Grid::singleton)
{
Grid::singleton->stop();
}
}
bool is_grid_server_running()
{
return Grid::singleton && Grid::singleton->is_running();
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <functional>
#include <thread>
#include <atomic>
#include <memory>
#include <boost/beast.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/smart_ptr/scoped_ptr.hpp>
#include <rapidjson/document.h>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
class Grid : public std::enable_shared_from_this<Grid>
{
private:
net::io_context ioc;
tcp::acceptor acceptor;
std::unique_ptr<std::thread> server_thread;
std::atomic<bool> running;
int port;
void run_server();
void do_accept();
void handle_session(tcp::socket socket);
std::string handle_request(const std::string& method, const std::string& path, const rapidjson::Document& body);
bool is_valid_json(const std::string& str);
public:
static boost::scoped_ptr<Grid> singleton;
Grid(int port);
~Grid();
bool start();
void stop();
bool is_running() const;
std::function<std::string(const std::string& method, const std::string& path, const rapidjson::Document& json_body)> json_handler;
};
void start_grid_server(int port);
void stop_grid_server();
bool is_grid_server_running();

File diff suppressed because it is too large Load Diff

230
client/server/src/main.cpp Normal file
View File

@@ -0,0 +1,230 @@
// clang-format off
#include <QApplication>
#include <boost/program_options.hpp>
#include <string>
#include "DataModel/ContentProvider.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/DebugSettings.hpp"
#include "DataModel/GameBasicSettings.hpp"
#include "Script/ScriptContext.hpp"
#include "Utility/StandardOut.hpp"
#include "Utility/Statistics.hpp"
#include "AppSettings.hpp"
#include "MainWindow.hpp"
#include "server.hpp"
#include "winrc.h"
namespace po = boost::program_options;
bool tryRead(const std::string& input, std::string& output)
{
std::ifstream file(input);
if (file)
{
std::stringstream buffer;
buffer << file.rdbuf();
output = buffer.str();
return true;
}
if (Aya::ContentProvider::isHttpUrl(input))
{
try
{
Aya::Http http(input);
http.get(output);
return true;
}
catch (const std::exception& e)
{
Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "Error trying to fetch '%s': %s", input.c_str(), e.what());
return false;
}
}
output = input;
return true;
}
QCoreApplication* createApplication(int &argc, const char *argv[])
{
if (!argc)
return new QApplication(argc, (char**)argv);
return new QCoreApplication(argc, (char**)argv);
}
int main(int argc, const char* argv[])
{
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
bool ui = qobject_cast<QApplication*>(app.data()) != nullptr;
QCoreApplication::setOrganizationName(AYA_PROJECT_NAME);
QCoreApplication::setApplicationName(AYA_PROJECT_NAME " Server");
QCoreApplication::setApplicationVersion(VERSION_FULL_STR);
po::options_description desc("Aya Server options");
std::string testScript, evaluateScript;
std::string instanceUrl, instanceAccessKey;
std::string trustCheckUrl;
std::string masterServerUrl, masterServerKey, masterServerHost, masterServerName, masterServerDescription;
std::string localServerPlace, localServerPassword;
std::string contentPath;
int gridPort, localServerPort;
desc.add_options()
("help,?", "Usage help")
("version,V", "Print version and exit")
("verbose", "Enable verbose logging (prints all output messages including FastLogs)")
("lan,L", "For local network or single-player servers (disables all online interaction)")
("insecure,I", "Disables anti-cheat & authentication checks")
("test", po::value<std::string>(&testScript)->implicit_value("ayaasset://scripts/ServerCoreScripts/Test.lua"), "Runs a test job with the given Lua script")
("eval,E", po::value<std::string>(&evaluateScript), "Lua script to run")
("port,G", po::value<int>(&gridPort)->implicit_value(64989), "Runs the Grid HTTP service on the given port")
("instance-url,U", po::value<std::string>(&instanceUrl)/*->default_value("http://www.kiseki.lol")*/, "Instance URL (leave blank for no connected instance)")
("instance-access-key,k", po::value<std::string>(&instanceAccessKey), "Instance access key (for sensitive instance actions)")
("trust-check-url,T", po::value<std::string>(&trustCheckUrl), "Only allow asset requests from the given domain (leave blank for no trust check)")
("ms-url,a", po::value<std::string>(&masterServerUrl), "Master server URL for broadcasting server details (leave blank for no master server connection)")
("ms-key,A", po::value<std::string>(&masterServerKey), "Master server authorization key (if the master server requires it)")
("ms-host,H", po::value<std::string>(&masterServerHost)->default_value("Bloxhead"), "Name of the host to be broadcasted")
("ms-name,N", po::value<std::string>(&masterServerName)->default_value("Aya Server"), "Name of the server to be broadcasted")
("ms-description,d", po::value<std::string>(&masterServerDescription)->default_value("Welcome to my server!"), "Short description of the server to be broadcasted")
("server-port,P", po::value<int>(&localServerPort)->implicit_value(53640), "Runs a server on the given port")
("server-place,f", po::value<std::string>(&localServerPlace)->default_value("ayaasset://place.rbxl"), "Path to place file for local server")
("server-password,K", po::value<std::string>(&localServerPassword), "Local server password for direct connections")
("content-path", po::value<std::string>(&contentPath)->default_value(GetAssetFolder()), "Path to the content directory")
("no-repl", "Disable terminal REPL");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help"))
{
std::cout << desc << std::endl;
return 0;
}
if (vm.count("version"))
{
std::cout << VERSION_FULL_STR << std::endl;
return 0;
}
AppSettings settings(QCoreApplication::applicationDirPath().toStdString());
if (!settings.load())
{
Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "Failed to load AppSettings.ini - please make sure it exists with a valid ContentPath under the Aya group, and make sure that it is free of any errors.");
return 0;
}
// Override AppSettings loaded settings with anything from the command line:
if (!IsVerboseLogging())
SetVerboseLogging(vm.count("verbose") > 0);
if (!IsInsecureMode())
SetInsecureMode(vm.count("insecure") > 0);
if (!instanceUrl.empty())
{
SetBaseURL(instanceUrl.c_str());
if (!instanceAccessKey.empty())
{
SetInstanceAccessKey(instanceAccessKey.c_str());
}
}
if (!trustCheckUrl.empty())
{
SetTrustCheckURL(trustCheckUrl.c_str());
}
if (!masterServerUrl.empty())
{
SetMasterServerURL(masterServerUrl.c_str());
if (!masterServerKey.empty())
{
SetMasterServerKey(masterServerKey.c_str());
}
}
if (vm.count("test"))
{
std::string script;
if (!tryRead(testScript, script))
return 1;
// queue_job("Test Job", script, 60 * 60 * 24);
}
if (!evaluateScript.empty())
{
// Evaluate script and exit
// May be a Lua script or a path to a file or a path to an asset
bool success = true;
try
{
auto dm = Aya::DataModel::createDataModel(false, new Aya::NullVerb(NULL, ""), false);
Aya::DataModel::LegacyLock lock(dm, Aya::DataModelJob::Write);
std::string script;
if (Aya::ContentProvider::isUrl(evaluateScript))
{
std::auto_ptr<std::istream> stream = Aya::ServiceProvider::create<Aya::ContentProvider>(dm.get())->getContent(Aya::ContentId(evaluateScript));
script = std::string(static_cast<std::stringstream const&>(std::stringstream() << stream->rdbuf()).str());
}
else
{
success = tryRead(evaluateScript, script);
}
if (success)
{
if (script.empty())
{
throw std::runtime_error("Could not load script '" + evaluateScript + "'");
}
Aya::ScriptContext* sc = Aya::ServiceProvider::create<Aya::ScriptContext>(dm.get());
sc->executeInNewThread(Aya::Security::WebService, Aya::ProtectedString::fromTrustedSource(script), "Script");
}
}
catch (std::exception& e)
{
Aya::StandardOut::singleton()->print(Aya::MESSAGE_ERROR, e);
success = false;
}
return success ? 0 : 1;
}
bool isLAN = vm.count("lan") > 0;
if (true)
{
MainWindow window(NULL);
window.setWindowTitle("Aya Server");
window.show();
window.setFocus();
}
start_aya_server(
ui,
isLAN,
gridPort,
localServerPort,
localServerPlace,
localServerPassword,
masterServerHost,
masterServerName,
masterServerDescription
);
return app->exec();
}

View File

@@ -0,0 +1,569 @@
#include <boost/program_options.hpp>
#include <generated/RCCServiceSoap.nsmap>
#include <generated/soapRCCServiceSoapService.h>
#include "DataModel/GameBasicSettings.hpp"
#include "Windows/Information.h"
#include "Script/ScriptContext.hpp"
#include "DataModel/ContentProvider.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/DebugSettings.hpp"
#include "Utility/ContentId.hpp"
#include "Utility/Http.hpp"
#include "Utility/ProtectedString.hpp"
#include "Utility/StandardOut.hpp"
#include "Utility/Statistics.hpp"
#include "API.hpp"
#include "boost.hpp"
#include "TaskScheduler.hpp"
#include "linenoise.hpp"
#include "Lua/lua.h"
#include "Utility/AyaService.hpp"
#ifdef __linux
#include <iostream>
#include <string>
#include <mutex>
#include <boost/signals2.hpp>
#include <boost/bind/bind.hpp>
#include <SDL3/SDL.h>
#endif
#include <boost/algorithm/string.hpp>
namespace po = boost::program_options;
#if defined(_WIN32) || defined(_WIN64)
class PrintfLogger
{
Aya::signals::scoped_connection messageConnection;
HANDLE handle;
Aya::spin_mutex mutex;
public:
PrintfLogger()
: handle(GetStdHandle(STD_OUTPUT_HANDLE))
{
messageConnection = Aya::StandardOut::singleton()->messageOut.connect(boost::bind(&PrintfLogger::onMessage, this, _1));
}
protected:
void onMessage(const Aya::StandardOutMessage& message)
{
Aya::spin_mutex::scoped_lock lock(mutex);
switch (message.type)
{
case Aya::MESSAGE_OUTPUT:
SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
break;
case Aya::MESSAGE_INFO:
SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
break;
case Aya::MESSAGE_WARNING:
SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN);
break;
case Aya::MESSAGE_ERROR:
SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_INTENSITY);
break;
}
printf("%s\n", message.message.c_str());
SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
};
#else
class PrintfLogger
{
Aya::signals::scoped_connection messageConnection;
std::mutex mutex;
public:
PrintfLogger()
{
messageConnection = Aya::StandardOut::singleton()->messageOut.connect(boost::bind(&PrintfLogger::onMessage, this, boost::placeholders::_1));
}
protected:
void onMessage(const Aya::StandardOutMessage& message)
{
std::lock_guard<std::mutex> lock(mutex);
// ANSI escape codes for color output
const char* colorCode = "\033[0m";
switch (message.type)
{
case Aya::MESSAGE_OUTPUT:
colorCode = "\033[34m";
break;
case Aya::MESSAGE_INFO:
colorCode = "\033[37m";
break;
case Aya::MESSAGE_WARNING:
colorCode = "\033[33m";
break;
case Aya::MESSAGE_ERROR:
colorCode = "\033[31m";
break;
default:
break;
}
std::cout << colorCode << message.message << "\033[0m" << std::endl;
}
};
#endif
template<class Soap>
class ExceptionAwareSoap : public Soap
{
public:
virtual int dispatch()
{
try
{
return Soap::dispatch();
}
catch (std::exception& e)
{
return soap_receiver_fault(this, e.what(), NULL); // return fault to sender
}
catch (std::string& s)
{
AYACRASH();
return soap_receiver_fault(this, s.c_str(), NULL); // return fault to sender
}
catch (...)
{
AYACRASH();
return soap_receiver_fault(this, "Unexpected C++ exception type", NULL); // return fault to sender
}
}
virtual RCCServiceSoapService* copy()
{
ExceptionAwareSoap<Soap>* dup = new ExceptionAwareSoap<Soap>();
soap_copy_context(dup, this);
return dup;
}
};
ExceptionAwareSoap<RCCServiceSoapService> service;
// TODO: Move this into some sort of RCCServiceSoap.hpp?
void start_CWebService();
void stop_CWebService();
static void startupRCC(int port)
{
#ifdef USE_BLOXBLOX_BRANDING
printf("Starting Bloxblox.Aya.Server v%s...\n", VERSION_MAJOR_MINOR_PATCH_STR);
#else
printf("Starting Aya.Server v%s...\n", VERSION_MAJOR_MINOR_PATCH_STR);
#endif
start_CWebService();
if (port != -1)
{
service.accept_timeout = 1; // server stops after 1 second of client inactivity
// service.send_timeout = 60; // 60 seconds
// service.recv_timeout = 60; // 60 seconds
// soap.max_keep_alive = 100; // max keep-alive sequence
SOAP_SOCKET m = service.bind(NULL, port, 100);
if (!soap_valid_socket(m))
throw std::runtime_error(*soap_faultstring(&service));
}
printf("Started Aya.Server!\n");
if (port != -1)
{
Aya::StandardOut::singleton()->printf(Aya::MESSAGE_SENSITIVE, "Now listening for incoming SOAP requests on port TCP/%d", port);
}
}
static void stepRCC()
{
SOAP_SOCKET s = service.accept();
if (!soap_valid_socket(s))
{
if (service.errnum)
throw std::runtime_error(*soap_faultstring(&service));
return;
}
RCCServiceSoapService* copy = service.copy(); // make a safe copy
if (!copy)
throw std::runtime_error(*soap_faultstring(&service));
try
{
copy->serve();
delete copy;
}
catch (...)
{
AYACRASH();
}
}
static void shutdownRCC()
{
printf("Shutting down...\n");
stop_CWebService();
// if on linux, SDL_Quit() for DummyWindow
#ifdef __linux
SDL_Quit();
#endif
}
static void handleScriptCommand(std::string& execCommand)
{
if (execCommand.empty())
return;
ns1__ScriptExecution se;
// d9mz - error: taking the address of a temporary object of type 'std::string'
std::string serverName = "Command Line";
se.name = &serverName;
se.script = &execCommand;
_ns1__Execute execute;
_ns1__ExecuteResponse response;
execute.script = &se;
execute.jobID = "Test";
RCCServiceSoapService* copy = service.copy();
copy->Execute(&execute, response);
}
static void escapeLuaString(std::string& string)
{
boost::replace_all(string, "[", "\\[");
boost::replace_all(string, "]", "\\]");
boost::replace_all(string, "\"", "\\\"");
}
int main(int argc, const char* argv[])
{
// Pipe Aya::StandardOut to printf
static boost::scoped_ptr<PrintfLogger> standardOutLog(new PrintfLogger());
// Parse arguments
std::string evaluateScript;
int port = 64989;
std::string contentPath = "content";
std::string httpAccessKey;
std::string baseUrl;
std::string testScript;
std::string password;
std::string masterServerAuthorization;
std::string masterServerUrl;
std::string masterServerHost;
std::string masterServerName;
std::string masterServerMotdPreview;
std::string masterServerMotdFile;
int localServerPort;
int virtualVersion;
std::string localServerPlace;
po::options_description desc("Aya options");
desc.add_options()("help,?", "Usage help")("version,V", "Print version and exit")("eval,E", po::value<std::string>(&evaluateScript),
"Lua script to run")("port", po::value<int>(&port)->default_value(64989), "SOAP port to listen on. Set to -1 to disable")("contentPath",
po::value<std::string>(&contentPath)->default_value(DEFAULT_CONTENT_PATH),
"Path to the content directory")("httpAccessKey", po::value<std::string>(&httpAccessKey)->default_value(""), "HTTP access key")("baseUrl",
po::value<std::string>(&baseUrl)->default_value("http://www.kiseki.lol"),
"Base URL")("test", po::value<std::string>(&testScript)->implicit_value("test.lua"), "Runs a test job with the given Lua script")(
"insecure", "Disables anti-cheat & authentication checks")("verbose", "Enable verbose logging")("noconsole,x", "Disable command line REPL")(
"password,K", po::value<std::string>(&password), "Set password")("masterServerAuthorization,A",
po::value<std::string>(&masterServerAuthorization))("masterServerUrl,a", po::value<std::string>(&masterServerUrl))(
"localServer", "Aya local server flag")("msHost,H", po::value<std::string>(&masterServerHost)->default_value("Bloxhead"))(
"msName,N", po::value<std::string>(&masterServerName)->default_value("Aya Server"))(
"msMotdPreview,m", po::value<std::string>(&masterServerMotdPreview)->default_value("Set -m to set a preview"))(
"msMotdFile,M", po::value<std::string>(&masterServerMotdFile)->default_value("motd.htm"))("localServerPort,P",
po::value<int>(&localServerPort)->default_value(53640))("virtualVersion,vv", po::value<int>(&virtualVersion)->default_value(0))(
"localServerPlace,p", po::value<std::string>(&localServerPlace)->default_value("ayaasset://test.rbxl"))(
"httpClientSettings,S", "use website for clientsettings");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
Aya::GameBasicSettings::VirtualVersion vv = static_cast<Aya::GameBasicSettings::VirtualVersion>(virtualVersion);
Aya::GameBasicSettings::singleton().setVirtualVersionInternal(vv);
bool isTest = vm.count("test") > 0;
bool isInsecure = vm.count("insecure") > 0;
bool isVerbose = vm.count("verbose") > 0;
bool noConsole = vm.count("noconsole") > 0;
bool isLocalServer = vm.count("localServer") > 0;
SetFetchLocalClientSettings(!vm.count("httpClientSettings"));
if (vm.count("help"))
{
std::cout << desc << "\n";
return 0;
}
if (vm.count("version"))
{
std::cout << VERSION_FULL_STR << "\n";
return 0;
}
if (vm.count("eval"))
{
bool success = true;
try
{
std::string code = vm["eval"].as<std::string>();
std::ifstream file(code);
if (file)
{
std::stringstream buffer;
buffer << file.rdbuf();
code = buffer.str();
}
// TODO: Account for ayaasset://, ayaassetid://, etc.
if (Aya::ContentProvider::isHttpUrl(code))
{
Aya::Http http(code);
http.get(code);
}
Aya::GameBasicSettings::VirtualVersion vv = static_cast<Aya::GameBasicSettings::VirtualVersion>(virtualVersion);
shared_ptr<Aya::DataModel> dm = Aya::DataModel::createDataModel(false, new Aya::NullVerb(NULL, ""), false, vv);
Aya::ScriptContext* sc = Aya::ServiceProvider::create<Aya::ScriptContext>(dm.get());
Aya::AyaService* ayaService = Aya::ServiceProvider::create<Aya::AyaService>(dm.get());
ayaService->setInitialVersion(vv);
sc->executeInNewThread(Aya::Security::WebService, Aya::ProtectedString::fromTrustedSource(code), "Script");
}
catch (std::exception& e)
{
Aya::StandardOut::singleton()->print(Aya::MESSAGE_ERROR, e);
success = false;
}
return success ? 0 : 1;
}
// Main startup
try
{
Aya::ContentProvider::setAssetFolder(contentPath.c_str());
SetBaseURL(baseUrl.c_str());
startupRCC(port);
if (vm.count("password"))
{
Aya::Network::setPassword(vm["password"].as<std::string>());
Aya::AyaService::passwordProtected = true;
}
if (isVerbose)
{
// TODO: Pipe FastLog to console
}
if (isInsecure)
{
// TODO: Disable all security checks
}
bool testJob = false;
if (isLocalServer)
{
Aya::StandardOut::singleton()->print(Aya::MESSAGE_WARNING, "Running an Aya Local Server.");
Aya::AyaService::masterServerUrl = masterServerUrl;
Aya::AyaService::masterServerAuthorization = masterServerAuthorization;
Aya::AyaService::localServer = true;
}
if (isTest || isLocalServer)
{
if (!isTest)
{
testScript = "content/scripts/Host.lua";
}
std::ifstream file(testScript);
std::string script;
if (!file)
{
std::cout << "Could not open file " << testScript << std::endl;
return 1;
}
std::stringstream buffer;
buffer << file.rdbuf();
if (isLocalServer)
{
// TODO: escape all the text so it doesn't break Lua
std::string motd;
std::ifstream file(masterServerMotdFile);
if (!file)
{
std::cout << "Could not open file " << masterServerMotdFile << std::endl;
}
else
{
std::stringstream motdBuffer;
motdBuffer << file.rdbuf();
motd = motdBuffer.str();
escapeLuaString(motd);
buffer << "\ngame:GetService(\"AyaService\").ServerMotdContent = [[" << motd << "]]";
}
escapeLuaString(masterServerMotdPreview);
buffer << "\ngame:GetService(\"AyaService\").ServerMotdPreview = \"" << masterServerMotdPreview << "\"";
escapeLuaString(masterServerHost);
buffer << "\ngame:GetService(\"AyaService\").Host = \"" << masterServerHost << "\"";
escapeLuaString(masterServerName);
buffer << "\ngame:GetService(\"AyaService\").ServerName = \"" << masterServerName << "\"";
escapeLuaString(localServerPlace);
buffer << "\ngame:GetService(\"AyaService\"):AnnounceMasterServer()";
buffer
<< "\ngame:GetService(\"Players\").PlayerAdded:connect(function() game:GetSerivce(\"AyaService\"):AnnounceMasterServer() "
"end)"
<< "\ngame:GetService(\"Players\").PlayerRemoving:connect(function() game:GetSerivce(\"AyaService\"):AnnounceMasterServer() end)";
char startLine[2048];
snprintf(startLine, 2048, "Start(%i, \"%s\")", localServerPort, localServerPlace.c_str());
if (masterServerUrl == "")
{
Aya::StandardOut::singleton()->printf(
Aya::MESSAGE_ERROR, "Master server ping will be disabled. Specify a master server with -a <url> -A <master server key>");
}
buffer << "\n" << startLine;
}
script = buffer.str();
testJob = true;
RCCServiceSoapService* copy = service.copy(); // make a safe copy
if (!copy)
throw std::runtime_error(*soap_faultstring(&service));
ns1__Job job;
job.id = "Test";
job.expirationInSeconds = 60 * 60 * 24;
ns1__ScriptExecution se;
// d9mz - error: taking the address of a temporary object of type 'std::string'
std::string serverName = "Start Server";
se.name = &serverName;
se.script = &script;
_ns1__OpenJob openJob;
_ns1__OpenJobResponse response;
openJob.script = &se;
openJob.job = &job;
copy->OpenJob(&openJob, response);
}
std::thread heartbeat(
[]
{
while (true)
{
stepRCC();
}
});
if (!testJob && !noConsole)
{
Aya::StandardOut::singleton()->print(Aya::MESSAGE_ERROR,
"Im sorry but I will not let you use the console unless if you are using --test. Specify --noconsole to suppress this message");
noConsole = true;
}
if (!noConsole)
{
std::cout << "Lua interactive command prompt" << std::endl;
linenoise::SetHistoryMaxLen(100);
}
while (true)
{
if (noConsole)
{
std::string toExecute;
std::getline(std::cin, toExecute);
}
else
{
std::string toExecute;
auto quit = linenoise::Readline(LUA_RELEASE "> ", toExecute);
if (quit)
break;
linenoise::AddHistory(toExecute.c_str());
try
{
int errorLine = 0;
std::string errorMessage;
if (Aya::ScriptContext::checkSyntax(toExecute.c_str(), errorLine, errorMessage))
{
Aya::StandardOut::singleton()->printf(Aya::MESSAGE_OUTPUT, "%s", toExecute.c_str());
handleScriptCommand(toExecute);
}
else
{
Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "Error in script: %s", errorMessage.c_str());
continue;
}
}
catch (std::exception& e)
{
}
}
}
heartbeat.detach();
shutdownRCC();
}
catch (std::exception& e)
{
Aya::StandardOut::singleton()->print(Aya::MESSAGE_ERROR, e);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
#include "server.hpp"
#include <boost/bind/bind.hpp>
#include <mutex>
#include "grid.hpp"
#include "Utility/StandardOut.hpp"
#ifdef _WIN32
// static boost::scoped_ptr<MainLogManager> mainLogManager(new MainLogManager("Aya Server", ".dmp", ".crashevent"));
#endif
static boost::scoped_ptr<StandardOutLogger> standardOutLog;
namespace Aya
{
class Explosion;
class Cofm;
class NormalBreakConnector;
class ContactConnector;
class RevoluteLink;
class SimBody;
class BallBallContact;
class BallBlockContact;
class BlockBlockContact;
} // namespace Aya
class StandardOutLogger
{
Aya::signals::scoped_connection messageConnection;
std::mutex mutex;
public:
StandardOutLogger()
{
messageConnection =
Aya::StandardOut::singleton()->messageOut.connect(boost::bind(&StandardOutLogger::onMessage, this, boost::placeholders::_1));
}
protected:
void onMessage(const Aya::StandardOutMessage& message)
{
std::lock_guard<std::mutex> lock(mutex);
// ANSI escape codes for color output
const char* colorCode = "\033[0m";
switch (message.type)
{
case Aya::MESSAGE_OUTPUT:
colorCode = "\033[34m";
break;
case Aya::MESSAGE_INFO:
colorCode = "\033[37m";
break;
case Aya::MESSAGE_WARNING:
colorCode = "\033[33m";
break;
case Aya::MESSAGE_ERROR:
colorCode = "\033[31m";
break;
default:
break;
}
std::cout << colorCode << message.message << "\033[0m" << std::endl;
}
};
void start_aya_server(bool ui, bool isLAN, int gridPort, int localServerPort, const std::string& localServerPlace,
const std::string& localServerPassword, const std::string& masterServerHost, const std::string& masterServerName,
const std::string& masterServerDescription)
{
if (gridPort != -1)
{
start_grid_server(gridPort);
}
}

View File

@@ -0,0 +1,84 @@
#pragma once
class StandardOutLogger;
struct Job : boost::noncopyable
{
enum JobStatus
{
RUNNING_JOB,
JOB_DONE,
JOB_ERROR
};
JobStatus status;
const std::string id;
shared_ptr<Aya::DataModel> dataModel;
Aya::signals::connection notifyAliveConnection;
Aya::Time expirationTime;
Job(const char* id)
: id(id)
, status(RUNNING_JOB)
{
//
}
void touch(double seconds);
double secondsToTimeout() const;
};
typedef std::map<std::string, boost::shared_ptr<Job>> JobMap;
class Service
{
private:
JobMap jobs;
boost::mutex sync;
long dataModelCount;
boost::mutex currentlyClosingMutex;
shared_ptr<Job> createJob(std::string id, std::string name);
public:
static boost::scoped_ptr<Service> singleton;
int jobCount() const;
Service();
~Service();
void closeDataModel(shared_ptr<Aya::DataModel> dataModel);
void arbiterActivityDump(const std::string& dump); // produces JSON dump of task scheduler avg activity for all jobs
void diagnosticDump(const std::string& dump); // produces JSON diagnostic dump
JobMap::iterator getJob(std::string id);
void openJob(const std::string& id, const std::string& name, int expirationInSeconds, const std::string& scriptName, const std::string& scriptContent);
void closeJob(const std::string& id, const char* errorMessage = NULL);
void closeExpiredJobs(int* result);
void getAllJobs(const std::string& result); // JSON result of running jobs
void renderThumbnail(const std::string& type, const std::vector<std::string> arguments, const std::string& result);
void auditLease(boost::shared_ptr<Job> job);
void renewLease(const std::string& jobID, double expirationInSeconds);
void executeScript(const std::string& jobID, const std::string& script, const std::string& result);
double getExpiration(const std::string& jobID);
};
// clang-format off
void start_aya_server(
bool ui,
bool isLAN,
int gridPort,
int localServerPort,
const std::string& localServerPlace,
const std::string& localServerPassword,
const std::string& masterServerHost,
const std::string& masterServerName,
const std::string& masterServerDescription
);
void stop_aya_server();
bool is_aya_server_running();