Merge remote-tracking branch 'origin/master' into 144-add-zone-support
Dieser Commit ist enthalten in:
Commit
dd1521a1db
44
.github/workflows/build.yml
vendored
44
.github/workflows/build.yml
vendored
@ -60,16 +60,6 @@ jobs:
|
||||
build_deb: true
|
||||
defines: "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
|
||||
|
||||
- name: "macos-12"
|
||||
os: "macos-12"
|
||||
generator: "Unix Makefiles"
|
||||
arch: ""
|
||||
target: traintastic-client
|
||||
jobs: 3
|
||||
build_type: Release
|
||||
build_deb: false
|
||||
defines: ""
|
||||
|
||||
- name: "macos-13"
|
||||
os: "macos-13"
|
||||
generator: "Unix Makefiles"
|
||||
@ -90,6 +80,16 @@ jobs:
|
||||
build_deb: false
|
||||
defines: ""
|
||||
|
||||
- name: "macos-15"
|
||||
os: "macos-15"
|
||||
generator: "Unix Makefiles"
|
||||
arch: ""
|
||||
target: traintastic-client
|
||||
jobs: 3
|
||||
build_type: Release
|
||||
build_deb: false
|
||||
defines: ""
|
||||
|
||||
steps:
|
||||
- uses: FranzDiebold/github-env-vars-action@v2
|
||||
|
||||
@ -261,18 +261,6 @@ jobs:
|
||||
defines: "-DINSTALL_SYSTEMD_SERVICE=ON -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
|
||||
ccov: false
|
||||
|
||||
- name: "macos-12"
|
||||
os: "macos-12"
|
||||
generator: "Unix Makefiles"
|
||||
arch: ""
|
||||
toolset: ""
|
||||
target: all
|
||||
jobs: 3
|
||||
build_type: Release
|
||||
build_deb: false
|
||||
defines: ""
|
||||
ccov: false
|
||||
|
||||
- name: "macos-13"
|
||||
os: "macos-13"
|
||||
generator: "Unix Makefiles"
|
||||
@ -297,6 +285,18 @@ jobs:
|
||||
defines: ""
|
||||
ccov: false
|
||||
|
||||
- name: "macos-15"
|
||||
os: "macos-15"
|
||||
generator: "Unix Makefiles"
|
||||
arch: ""
|
||||
toolset: ""
|
||||
target: all
|
||||
jobs: 3
|
||||
build_type: Release
|
||||
build_deb: false
|
||||
defines: ""
|
||||
ccov: false
|
||||
|
||||
steps:
|
||||
- uses: FranzDiebold/github-env-vars-action@v2
|
||||
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "shared/data/lncv"]
|
||||
path = shared/data/lncv
|
||||
url = ../lncv.git
|
||||
[submodule "server/thirdparty/catch2"]
|
||||
path = server/thirdparty/catch2
|
||||
url = https://github.com/catchorg/Catch2.git
|
||||
|
||||
101
client/gfx/dark/clear_persistent_variables.svg
Normale Datei
101
client/gfx/dark/clear_persistent_variables.svg
Normale Datei
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="clear_persistent_variables.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="58.303571"
|
||||
inkscape:cy="31.785714"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3713"
|
||||
spacingx="0.26458333"
|
||||
empspacing="4" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-271.59998)">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 3.175,292.76665 -10e-8,-16.93334 c 2.0852342,1e-5 3.0686586,0 4.2333333,0 4.2333328,2.11667 4.2333328,6.35 0,9.525 H 3.1749999"
|
||||
id="path3223"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 13.758333,275.83331 17.991666,292.76665 22.225,275.83331"
|
||||
id="path4097"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#cd5c5c;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 2.1166666,294.88331 23.283333,273.71665"
|
||||
id="path1540"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#cd5c5c;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 2.1166666,273.71665 23.283333,294.88331"
|
||||
id="path1542"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Nachher Breite: | Höhe: | Größe: 3.7 KiB |
@ -93,5 +93,6 @@
|
||||
<file>board_tile.misc.switch.svg</file>
|
||||
<file>board_tile.misc.label.svg</file>
|
||||
<file>zone.svg</file>
|
||||
<file>clear_persistent_variables.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
102
client/gfx/light/clear_persistent_variables.svg
Normale Datei
102
client/gfx/light/clear_persistent_variables.svg
Normale Datei
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="clear_persistent_variables.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="58.303571"
|
||||
inkscape:cy="31.785714"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3713"
|
||||
spacingx="0.26458333"
|
||||
empspacing="4" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-271.59998)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 3.175,292.76665 -10e-8,-16.93334 c 2.0852342,1e-5 3.0686586,0 4.2333333,0 4.2333328,2.11667 4.2333328,6.35 0,9.525 H 3.1749999"
|
||||
id="path3223"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.5875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 13.758333,275.83331 17.991666,292.76665 22.225,275.83331"
|
||||
id="path4097"
|
||||
sodipodi:nodetypes="ccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#b00020;stroke-width:2.11666667;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 2.1166666,294.88331 23.283333,273.71665"
|
||||
id="path1540"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#b00020;stroke-width:2.11666667;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 2.1166666,273.71665 23.283333,294.88331"
|
||||
id="path1542"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Nachher Breite: | Höhe: | Größe: 3.7 KiB |
@ -67,5 +67,6 @@
|
||||
<file>board_tile.misc.switch.svg</file>
|
||||
<file>board_tile.misc.label.svg</file>
|
||||
<file>zone.svg</file>
|
||||
<file>clear_persistent_variables.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "getboardcolorscheme.hpp"
|
||||
#include "tilepainter.hpp"
|
||||
#include "../network/board.hpp"
|
||||
#include "../network/callmethod.hpp"
|
||||
#include "../network/object.tpp"
|
||||
#include "../network/object/blockrailtile.hpp"
|
||||
#include "../network/object/nxbuttonrailtile.hpp"
|
||||
@ -39,6 +40,7 @@
|
||||
#include "../network/abstractvectorproperty.hpp"
|
||||
#include "../utils/enum.hpp"
|
||||
#include "../utils/rectf.hpp"
|
||||
#include "../misc/mimedata.hpp"
|
||||
#include "../settings/boardsettings.hpp"
|
||||
|
||||
QRect rectToViewport(const QRect& r, const int gridSize)
|
||||
@ -84,6 +86,7 @@ BoardAreaWidget::BoardAreaWidget(BoardWidget& board, QWidget* parent) :
|
||||
{
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setAcceptDrops(true);
|
||||
|
||||
if(Q_LIKELY(m_boardLeft))
|
||||
connect(m_boardLeft, &AbstractProperty::valueChanged, this, &BoardAreaWidget::updateMinimumSize);
|
||||
@ -837,6 +840,65 @@ void BoardAreaWidget::paintEvent(QPaintEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
void BoardAreaWidget::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if(event->mimeData()->hasFormat(AssignTrainMimeData::mimeType))
|
||||
{
|
||||
m_dragMoveTileLocation = TileLocation::invalid;
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void BoardAreaWidget::dragMoveEvent(QDragMoveEvent* event)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
const TileLocation l = pointToTileLocation(event->pos());
|
||||
#else
|
||||
const TileLocation l = pointToTileLocation(event->position().toPoint());
|
||||
#endif
|
||||
|
||||
if(m_dragMoveTileLocation != l)
|
||||
{
|
||||
m_dragMoveTileLocation = l;
|
||||
if(event->mimeData()->hasFormat(AssignTrainMimeData::mimeType) &&
|
||||
m_board.board().getTileId(l) == TileId::RailBlock)
|
||||
{
|
||||
return event->accept();
|
||||
}
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void BoardAreaWidget::dropEvent(QDropEvent* event)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
const TileLocation l = pointToTileLocation(event->pos());
|
||||
#else
|
||||
const TileLocation l = pointToTileLocation(event->position().toPoint());
|
||||
#endif
|
||||
|
||||
switch(m_board.board().getTileId(l))
|
||||
{
|
||||
case TileId::RailBlock:
|
||||
if(auto* assignTrain = dynamic_cast<const AssignTrainMimeData*>(event->mimeData()))
|
||||
{
|
||||
if(auto tile = std::dynamic_pointer_cast<BlockRailTile>(m_board.board().getTileObject(l)))
|
||||
{
|
||||
if(auto* method = tile->getMethod("assign_train"))
|
||||
{
|
||||
callMethod(*method, nullptr, assignTrain->trainId());
|
||||
return event->accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void BoardAreaWidget::settingsChanged()
|
||||
{
|
||||
const auto& s = BoardSettings::instance();
|
||||
|
||||
@ -89,6 +89,8 @@ class BoardAreaWidget : public QWidget
|
||||
uint8_t m_mouseMoveTileWidthMax;
|
||||
uint8_t m_mouseMoveTileHeightMax;
|
||||
|
||||
TileLocation m_dragMoveTileLocation;
|
||||
|
||||
inline int boardLeft() const { return Q_LIKELY(m_boardLeft) ? m_boardLeft->toInt() - boardMargin : 0; }
|
||||
inline int boardTop() const { return Q_LIKELY(m_boardTop) ? m_boardTop->toInt() - boardMargin: 0; }
|
||||
inline int boardRight() const { return Q_LIKELY(m_boardRight) ? m_boardRight->toInt() + boardMargin: 0; }
|
||||
@ -115,6 +117,9 @@ class BoardAreaWidget : public QWidget
|
||||
void mouseMoveEvent(QMouseEvent* event) final;
|
||||
void wheelEvent(QWheelEvent* event) final;
|
||||
void paintEvent(QPaintEvent* event) final;
|
||||
void dragEnterEvent(QDragEnterEvent* event) final;
|
||||
void dragMoveEvent(QDragMoveEvent* event) final;
|
||||
void dropEvent(QDropEvent* event) final;
|
||||
|
||||
protected slots:
|
||||
void settingsChanged();
|
||||
|
||||
@ -774,7 +774,7 @@ void TilePainter::drawTriangle(const QRectF& r)
|
||||
{r.right(), r.bottom()},
|
||||
{r.left(), r.bottom()}}};
|
||||
|
||||
m_painter.drawConvexPolygon(points.data(), points.size());
|
||||
m_painter.drawConvexPolygon(points.data(), static_cast<int>(points.size()));
|
||||
}
|
||||
|
||||
void TilePainter::drawLED(const QRectF& r, const QColor& color, const QColor& borderColor)
|
||||
|
||||
@ -171,7 +171,6 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
if(const ObjectPtr& traintastic = m_connection->traintastic())
|
||||
traintastic->callMethod("close_world");
|
||||
});
|
||||
m_actionCloseWorld->setShortcut(QKeySequence::Close);
|
||||
menu->addSeparator();
|
||||
m_actionImportWorld = menu->addAction(Theme::getIcon("world_import"), Locale::tr("qtapp.mainmenu:import_world") + "...",
|
||||
[this]()
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2022-2023 Reinder Feenstra
|
||||
* Copyright (C) 2022-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -32,6 +32,7 @@
|
||||
#include "../settings/statusbarsettings.hpp"
|
||||
#include "../widget/status/interfacestatuswidget.hpp"
|
||||
#include "../widget/status/luastatuswidget.hpp"
|
||||
#include "../widget/status/simulationstatuswidget.hpp"
|
||||
|
||||
MainWindowStatusBar::MainWindowStatusBar(MainWindow& mainWindow)
|
||||
: QStatusBar(&mainWindow)
|
||||
@ -148,6 +149,8 @@ void MainWindowStatusBar::updateStatuses()
|
||||
m_statuses->layout()->addWidget(new InterfaceStatusWidget(object, this));
|
||||
else if(object->classId() == "status.lua")
|
||||
m_statuses->layout()->addWidget(new LuaStatusWidget(object, this));
|
||||
else if(object->classId() == "status.simulation")
|
||||
m_statuses->layout()->addWidget(new SimulationStatusWidget(object, this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
44
client/src/misc/mimedata.hpp
Normale Datei
44
client/src/misc/mimedata.hpp
Normale Datei
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* client/src/misc/mimedata.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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_CLIENT_MISC_MIMEDATA_HPP
|
||||
#define TRAINTASTIC_CLIENT_MISC_MIMEDATA_HPP
|
||||
|
||||
#include <QMimeData>
|
||||
|
||||
class AssignTrainMimeData : public QMimeData
|
||||
{
|
||||
public:
|
||||
inline static const auto mimeType = QLatin1String("application/vnd.traintastic.assign_train");
|
||||
|
||||
explicit AssignTrainMimeData(const QString& trainId)
|
||||
{
|
||||
setData(mimeType, trainId.toUtf8());
|
||||
}
|
||||
|
||||
inline QString trainId() const
|
||||
{
|
||||
return QString::fromUtf8(data(mimeType));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -70,7 +70,7 @@ class ServerLogTableModel final : public QAbstractTableModel
|
||||
ServerLogTableModel(std::shared_ptr<Connection> connection);
|
||||
~ServerLogTableModel();
|
||||
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const final { Q_UNUSED(parent); return m_columnHeaders.size(); }
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const final { Q_UNUSED(parent); return static_cast<int>(m_columnHeaders.size()); }
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const final;
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const final;
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "list/marklincanlocomotivelistwidget.hpp"
|
||||
#include "objectlist/boardlistwidget.hpp"
|
||||
#include "objectlist/throttleobjectlistwidget.hpp"
|
||||
#include "objectlist/trainlistwidget.hpp"
|
||||
#include "object/luascripteditwidget.hpp"
|
||||
#include "object/objecteditwidget.hpp"
|
||||
#include "object/itemseditwidget.hpp"
|
||||
@ -47,7 +48,7 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
|
||||
|
||||
if(classId == "command_station_list")
|
||||
return new ObjectListWidget(object, parent); // todo remove
|
||||
else if(classId == "decoder_list" || classId == "list.train")
|
||||
else if(classId == "decoder_list")
|
||||
return new ThrottleObjectListWidget(object, parent); // todo remove
|
||||
else if(classId == "controller_list")
|
||||
return new ObjectListWidget(object, parent); // todo remove
|
||||
@ -61,6 +62,10 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
|
||||
{
|
||||
return new BoardListWidget(object, parent);
|
||||
}
|
||||
if(classId == "list.train")
|
||||
{
|
||||
return new TrainListWidget(object, parent);
|
||||
}
|
||||
else if(object->classId().startsWith("list."))
|
||||
return new ObjectListWidget(object, parent);
|
||||
else if(classId == "lua.script")
|
||||
|
||||
@ -160,7 +160,7 @@ void InputMonitorWidget::keyReleaseEvent(QKeyEvent* event)
|
||||
|
||||
uint32_t InputMonitorWidget::pageCount() const
|
||||
{
|
||||
return static_cast<uint32_t>(m_addressMax->toInt64() - m_addressMin->toInt64() + m_leds.size()) / m_leds.size();
|
||||
return static_cast<uint32_t>(m_addressMax->toInt64() - m_addressMin->toInt64() + m_leds.size()) / static_cast<uint32_t>(m_leds.size());
|
||||
}
|
||||
|
||||
void InputMonitorWidget::setPage(uint32_t value)
|
||||
@ -184,7 +184,7 @@ void InputMonitorWidget::setGroupBy(uint32_t value)
|
||||
|
||||
LEDWidget* InputMonitorWidget::getLED(uint32_t address)
|
||||
{
|
||||
const uint32_t first = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * m_leds.size();
|
||||
const uint32_t first = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * static_cast<uint32_t>(m_leds.size());
|
||||
|
||||
if(address >= first && (address - first) < m_leds.size())
|
||||
return m_leds[address - first];
|
||||
@ -199,7 +199,7 @@ void InputMonitorWidget::updateLEDs()
|
||||
|
||||
const uint32_t addressMin = static_cast<uint32_t>(m_addressMin->toInt64());
|
||||
const uint32_t addressMax = static_cast<uint32_t>(m_addressMax->toInt64());
|
||||
uint32_t address = addressMin + m_page * m_leds.size();
|
||||
uint32_t address = addressMin + m_page * static_cast<uint32_t>(m_leds.size());
|
||||
|
||||
for(auto* led : m_leds)
|
||||
{
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include <version.hpp>
|
||||
#include <traintastic/locale/locale.hpp>
|
||||
#include <traintastic/utils/standardpaths.hpp>
|
||||
#include "../../misc/methodaction.hpp"
|
||||
#include "../../network/object.hpp"
|
||||
#include "../../network/property.hpp"
|
||||
#include "../../network/method.hpp"
|
||||
@ -108,6 +109,17 @@ void LuaScriptEditWidget::buildForm()
|
||||
m_stop->setEnabled(value.toBool());
|
||||
});
|
||||
|
||||
|
||||
if(auto* method = m_object->getMethod("clear_persistent_variables"))
|
||||
{
|
||||
QWidget* spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->show();
|
||||
toolbar->addWidget(spacer);
|
||||
|
||||
toolbar->addAction(new MethodAction(Theme::getIcon("clear_persistent_variables"), *method, this));
|
||||
}
|
||||
|
||||
QWidget* spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->show();
|
||||
|
||||
@ -399,6 +399,16 @@ ObjectListWidget::ObjectListWidget(const ObjectPtr& object_, QWidget* parent) :
|
||||
}
|
||||
}
|
||||
|
||||
if(auto* method = object()->getMethod("clear_persistent_variables"))
|
||||
{
|
||||
QWidget* spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
spacer->show();
|
||||
m_toolbar->addWidget(spacer);
|
||||
|
||||
m_toolbar->addAction(new MethodAction(Theme::getIcon("clear_persistent_variables"), *method, this));
|
||||
}
|
||||
|
||||
if(!m_toolbar->actions().empty())
|
||||
{
|
||||
static_cast<QVBoxLayout*>(this->layout())->insertWidget(0, m_toolbar);
|
||||
|
||||
41
client/src/widget/objectlist/trainlistwidget.cpp
Normale Datei
41
client/src/widget/objectlist/trainlistwidget.cpp
Normale Datei
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* client/src/widget/objectlist/trainlistwidget.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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 "trainlistwidget.hpp"
|
||||
#include <QDrag>
|
||||
#include "../tablewidget.hpp"
|
||||
#include "../../misc/mimedata.hpp"
|
||||
|
||||
TrainListWidget::TrainListWidget(const ObjectPtr& object, QWidget* parent)
|
||||
: ThrottleObjectListWidget(object, parent)
|
||||
{
|
||||
connect(m_tableWidget, &TableWidget::rowDragged,
|
||||
[this](int row)
|
||||
{
|
||||
if(auto trainId = m_tableWidget->getRowObjectId(row); !trainId.isEmpty())
|
||||
{
|
||||
QDrag* drag = new QDrag(m_tableWidget);
|
||||
drag->setMimeData(new AssignTrainMimeData(trainId));
|
||||
drag->exec(Qt::CopyAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
/**
|
||||
* server/src/enum/commandstationstatus.hpp
|
||||
* client/src/widget/objectlist/trainlistwidget.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -20,9 +20,15 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ENUM_COMMANDSTATIONSTATUS_HPP
|
||||
#define TRAINTASTIC_SERVER_ENUM_COMMANDSTATIONSTATUS_HPP
|
||||
#ifndef TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_TRAINLISTWIDGET_HPP
|
||||
#define TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_TRAINLISTWIDGET_HPP
|
||||
|
||||
#include <traintastic/enum/commandstationstatus.hpp>
|
||||
#include "throttleobjectlistwidget.hpp"
|
||||
|
||||
class TrainListWidget : public ThrottleObjectListWidget
|
||||
{
|
||||
public:
|
||||
explicit TrainListWidget(const ObjectPtr& object, QWidget* parent = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -161,7 +161,7 @@ OutputKeyboardWidget::OutputKeyboardWidget(std::shared_ptr<OutputKeyboard> objec
|
||||
connect(led, &LEDWidget::clicked, this,
|
||||
[this, index=i]()
|
||||
{
|
||||
const uint32_t address = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * m_leds.size() / 2 + index / 2;
|
||||
const uint32_t address = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * static_cast<uint32_t>(m_leds.size()) / 2 + index / 2;
|
||||
const auto value = (index & 0x1) ? OutputPairValue::Second : OutputPairValue::First;
|
||||
callMethod(*m_setOutputValue, nullptr, address, value);
|
||||
});
|
||||
@ -251,7 +251,7 @@ uint32_t OutputKeyboardWidget::pageCount() const
|
||||
{
|
||||
leds *= 2;
|
||||
}
|
||||
return static_cast<uint32_t>(leds + m_leds.size() - 1) / m_leds.size();
|
||||
return static_cast<uint32_t>(leds + m_leds.size() - 1) / static_cast<uint32_t>(m_leds.size());
|
||||
}
|
||||
|
||||
void OutputKeyboardWidget::setPage(uint32_t value)
|
||||
@ -277,7 +277,7 @@ LEDWidget* OutputKeyboardWidget::getLED(uint32_t address)
|
||||
{
|
||||
assert(m_object->outputType() == OutputType::Single);
|
||||
|
||||
const uint32_t first = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * m_leds.size();
|
||||
const uint32_t first = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * static_cast<uint32_t>(m_leds.size());
|
||||
|
||||
if(address >= first && (address - first) < m_leds.size())
|
||||
return m_leds[address - first];
|
||||
@ -289,7 +289,7 @@ std::pair<LEDWidget*, LEDWidget*> OutputKeyboardWidget::getLEDs(uint32_t address
|
||||
{
|
||||
assert(m_object->outputType() == OutputType::Pair);
|
||||
|
||||
const uint32_t first = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * m_leds.size() / 2;
|
||||
const uint32_t first = static_cast<uint32_t>(m_addressMin->toInt64()) + m_page * static_cast<uint32_t>(m_leds.size()) / 2;
|
||||
|
||||
if(address >= first && (address - first) < m_leds.size())
|
||||
return {m_leds[(address - first) * 2], m_leds[(address - first) * 2 + 1]};
|
||||
@ -309,7 +309,7 @@ void OutputKeyboardWidget::updateLEDs()
|
||||
{
|
||||
case OutputType::Single:
|
||||
{
|
||||
uint32_t address = addressMin + m_page * m_leds.size();
|
||||
uint32_t address = addressMin + m_page * static_cast<uint32_t>(m_leds.size());
|
||||
for(auto* led : m_leds)
|
||||
{
|
||||
const auto& outputState = m_object->getOutputState(address);
|
||||
@ -329,7 +329,7 @@ void OutputKeyboardWidget::updateLEDs()
|
||||
}
|
||||
case OutputType::Pair:
|
||||
{
|
||||
uint32_t address = addressMin + m_page * m_leds.size() / 2;
|
||||
uint32_t address = addressMin + m_page * static_cast<uint32_t>(m_leds.size()) / 2;
|
||||
bool second = false;
|
||||
for(auto* led : m_leds)
|
||||
{
|
||||
|
||||
@ -162,7 +162,7 @@ OutputMapWidget::~OutputMapWidget()
|
||||
|
||||
void OutputMapWidget::updateItems(const std::vector<ObjectPtr>& items)
|
||||
{
|
||||
m_table->setRowCount(items.size());
|
||||
m_table->setRowCount(static_cast<int>(items.size()));
|
||||
m_itemObjects = items;
|
||||
m_actions.resize(items.size());
|
||||
for(size_t i = 0; i < items.size(); i++)
|
||||
@ -197,7 +197,7 @@ void OutputMapWidget::updateItems(const std::vector<ObjectPtr>& items)
|
||||
assert(false);
|
||||
text = "?";
|
||||
}
|
||||
m_table->setItem(i, columnKey, new QTableWidgetItem(text));
|
||||
m_table->setItem(static_cast<int>(i), columnKey, new QTableWidgetItem(text));
|
||||
}
|
||||
|
||||
if(m_hasUseColumn)
|
||||
@ -211,16 +211,16 @@ void OutputMapWidget::updateItems(const std::vector<ObjectPtr>& items)
|
||||
l->setAlignment(Qt::AlignCenter);
|
||||
l->addWidget(new PropertyCheckBox(*p, w));
|
||||
w->setLayout(l);
|
||||
m_table->setCellWidget(i, columnUse, w);
|
||||
m_table->setCellWidget(static_cast<int>(i), columnUse, w);
|
||||
}
|
||||
}
|
||||
|
||||
if(auto* p = items[i]->getProperty("visible"))
|
||||
{
|
||||
m_table->setRowHidden(i, !p->toBool());
|
||||
m_table->setRowHidden(static_cast<int>(i), !p->toBool());
|
||||
|
||||
connect(p, &Property::valueChangedBool, this,
|
||||
[this, row=i](bool value)
|
||||
[this, row=static_cast<int>(i)](bool value)
|
||||
{
|
||||
m_table->setRowHidden(row, !value);
|
||||
});
|
||||
@ -228,10 +228,10 @@ void OutputMapWidget::updateItems(const std::vector<ObjectPtr>& items)
|
||||
|
||||
if(auto* outputActions = dynamic_cast<ObjectVectorProperty*>(items[i]->getVectorProperty("output_actions")))
|
||||
{
|
||||
updateTableOutputActions(*outputActions, i);
|
||||
updateTableOutputActions(*outputActions, static_cast<int>(i));
|
||||
|
||||
connect(outputActions, &ObjectVectorProperty::valueChanged, this,
|
||||
[this, row=i]()
|
||||
[this, row=static_cast<int>(i)]()
|
||||
{
|
||||
updateTableOutputActions(*dynamic_cast<ObjectVectorProperty*>(sender()), row);
|
||||
});
|
||||
@ -291,7 +291,7 @@ void OutputMapWidget::updateKeyIcons()
|
||||
break; // tileId not supported (yet)
|
||||
}
|
||||
|
||||
m_table->item(i, columnKey)->setIcon(QPixmap::fromImage(image));
|
||||
m_table->item(static_cast<int>(i), columnKey)->setIcon(QPixmap::fromImage(image));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,6 +189,7 @@ PropertyLuaCodeEdit::Highlighter::Highlighter(QTextDocument* parent) :
|
||||
QStringLiteral("\\bworld(?=\\.)"),
|
||||
QStringLiteral("\\bset(?=\\.)"),
|
||||
QStringLiteral("\\benum(?=\\.)"),
|
||||
QStringLiteral("\\bpv(?=\\.)"),
|
||||
};
|
||||
for(const auto& regex : globals)
|
||||
m_rules.append(Rule(regex, QColor(0xFF, 0x8C, 0x00)));
|
||||
|
||||
66
client/src/widget/status/simulationstatuswidget.cpp
Normale Datei
66
client/src/widget/status/simulationstatuswidget.cpp
Normale Datei
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* client/src/widget/status/simulationstatuswidget.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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 "simulationstatuswidget.hpp"
|
||||
#include <QResizeEvent>
|
||||
#include <traintastic/locale/locale.hpp>
|
||||
#include "../../network/object.hpp"
|
||||
#include "../../network/property.hpp"
|
||||
#include "../../theme/theme.hpp"
|
||||
|
||||
SimulationStatusWidget::SimulationStatusWidget(const ObjectPtr& object, QWidget* parent)
|
||||
: QSvgWidget(parent)
|
||||
, m_object{object}
|
||||
{
|
||||
assert(m_object);
|
||||
assert(m_object->classId() == "status.simulation");
|
||||
|
||||
load(Theme::getIconFile("simulation"));
|
||||
|
||||
if(auto* property = m_object->getProperty("label"))
|
||||
{
|
||||
connect(property, &Property::valueChanged, this, &SimulationStatusWidget::labelChanged);
|
||||
}
|
||||
|
||||
labelChanged();
|
||||
}
|
||||
|
||||
void SimulationStatusWidget::labelChanged()
|
||||
{
|
||||
QString label;
|
||||
|
||||
if(auto* property = m_object->getProperty("label"))
|
||||
{
|
||||
label = Locale::instance->parse(property->toString());
|
||||
}
|
||||
|
||||
setToolTip(label);
|
||||
}
|
||||
|
||||
void SimulationStatusWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QSvgWidget::resizeEvent(event);
|
||||
|
||||
// force same width as height:
|
||||
setMinimumWidth(event->size().height());
|
||||
setMaximumWidth(event->size().height());
|
||||
}
|
||||
44
client/src/widget/status/simulationstatuswidget.hpp
Normale Datei
44
client/src/widget/status/simulationstatuswidget.hpp
Normale Datei
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* client/src/widget/status/simulationstatuswidget.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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_CLIENT_WIDGET_STATUS_SIMULATIONSTATUSWIDGET_HPP
|
||||
#define TRAINTASTIC_CLIENT_WIDGET_STATUS_SIMULATIONSTATUSWIDGET_HPP
|
||||
|
||||
#include <QSvgWidget>
|
||||
#include "../../network/objectptr.hpp"
|
||||
|
||||
class SimulationStatusWidget : public QSvgWidget
|
||||
{
|
||||
private:
|
||||
ObjectPtr m_object;
|
||||
|
||||
void labelChanged();
|
||||
void stateChanged();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
public:
|
||||
explicit SimulationStatusWidget(const ObjectPtr& object, QWidget* parent = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021,2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2021,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -24,6 +24,8 @@
|
||||
#include <QHeaderView>
|
||||
#include <QScrollBar>
|
||||
#include <QSettings>
|
||||
#include <QMouseEvent>
|
||||
#include <QApplication>
|
||||
#include "../network/tablemodel.hpp"
|
||||
|
||||
TableWidget::TableWidget(QWidget* parent) :
|
||||
@ -113,3 +115,23 @@ void TableWidget::updateRegion()
|
||||
|
||||
m_model->setRegion(columnMin, columnMax, rowMin, rowMax);
|
||||
}
|
||||
|
||||
void TableWidget::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
QTableView::mouseMoveEvent(event);
|
||||
|
||||
if(event->button() == Qt::LeftButton)
|
||||
{
|
||||
m_dragStartPosition = event->pos();
|
||||
}
|
||||
}
|
||||
|
||||
void TableWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
QTableView::mousePressEvent(event);
|
||||
|
||||
if((event->buttons() & Qt::LeftButton) && (event->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance())
|
||||
{
|
||||
emit rowDragged(indexAt(m_dragStartPosition).row());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020,2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -33,6 +33,10 @@ class TableWidget : public QTableView
|
||||
protected:
|
||||
TableModelPtr m_model;
|
||||
int m_selectedRow = -1;
|
||||
QPoint m_dragStartPosition;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
protected slots:
|
||||
void updateRegion();
|
||||
@ -44,6 +48,9 @@ class TableWidget : public QTableView
|
||||
QString getRowObjectId(int row) const;
|
||||
|
||||
void setTableModel(const TableModelPtr& model);
|
||||
|
||||
signals:
|
||||
void rowDragged(int row);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -20,6 +20,7 @@ class LuaDoc:
|
||||
DEFAULT_LANGUAGE = 'en-us'
|
||||
FILENAME_INDEX = 'index.html'
|
||||
FILENAME_GLOBALS = 'globals.html'
|
||||
FILENAME_PV = 'pv.html'
|
||||
FILENAME_ENUM = 'enum.html'
|
||||
FILENAME_SET = 'set.html'
|
||||
FILENAME_OBJECT = 'object.html'
|
||||
@ -30,6 +31,7 @@ class LuaDoc:
|
||||
version = None
|
||||
|
||||
def __init__(self, project_root: str) -> None:
|
||||
self._project_root = project_root
|
||||
self._globals = LuaDoc._find_globals(project_root)
|
||||
self._enums = LuaDoc._find_enums(project_root)
|
||||
self._sets = LuaDoc._find_sets(project_root)
|
||||
@ -101,6 +103,16 @@ class LuaDoc:
|
||||
for object in self._objects:
|
||||
if object['lua_name'] == id:
|
||||
return '<a href="' + object['filename'] + fragment + '">' + (self._get_term(object['name']) if title == '' else title) + '</a>'
|
||||
elif id == 'globals':
|
||||
return '<a href="' + self.FILENAME_GLOBALS + fragment + '">' + (self._get_term('globals:title') if title == '' else title) + '</a>'
|
||||
elif id == 'enum':
|
||||
return '<a href="' + self.FILENAME_ENUM + fragment + '">' + (self._get_term('enum:title') if title == '' else title) + '</a>'
|
||||
elif id == 'set':
|
||||
return '<a href="' + self.FILENAME_SET + fragment + '">' + (self._get_term('set:title') if title == '' else title) + '</a>'
|
||||
elif id == 'object':
|
||||
return '<a href="' + self.FILENAME_OBJECT + fragment + '">' + (self._get_term('object:title') if title == '' else title) + '</a>'
|
||||
elif id == 'pv':
|
||||
return '<a href="' + self.FILENAME_PV + fragment + '">' + (self._get_term('pv:title') if title == '' else title) + '</a>'
|
||||
|
||||
return '<span style="color:red">' + m.group(0) + '</span>'
|
||||
|
||||
@ -447,6 +459,7 @@ class LuaDoc:
|
||||
|
||||
self._build_index(output_dir)
|
||||
self._build_globals(output_dir, nav)
|
||||
self._build_pv(output_dir, nav)
|
||||
self._build_enums(output_dir, nav)
|
||||
self._build_sets(output_dir, nav)
|
||||
for _, lib in self._libs.items():
|
||||
@ -649,6 +662,30 @@ class LuaDoc:
|
||||
html += self._build_items_html(self._globals, 'globals.')
|
||||
LuaDoc._write_file(os.path.join(output_dir, LuaDoc.FILENAME_GLOBALS), self._add_toc(html))
|
||||
|
||||
def _build_pv(self, output_dir: str, nav: list) -> None:
|
||||
title = self._get_term('pv:title')
|
||||
html = self._get_header(title, nav + [{'title': title, 'href': LuaDoc.FILENAME_PV}])
|
||||
html += '<p>' + self._get_term('pv:paragraph_1') + '</p>' + os.linesep
|
||||
html += '<p>' + self._get_term('pv:paragraph_2') + '</p>' + os.linesep
|
||||
|
||||
html += '<h2 id="storing">' + self._get_term('pv.storing:title') + '</h2>' + os.linesep
|
||||
html += '<p>' + self._get_term('pv.storing:paragraph_1') + '</p>' + os.linesep
|
||||
html += '<pre lang="lua"><code>' + highlight_lua(LuaDoc._read_file(os.path.join(self._project_root, 'manual', 'luadoc', 'example', 'pv', 'storingpersistentdata.lua'))) + '</code></pre>'
|
||||
|
||||
html += '<h2 id="retrieving">' + self._get_term('pv.retrieving:title') + '</h2>' + os.linesep
|
||||
html += '<p>' + self._get_term('pv.retrieving:paragraph_1') + '</p>' + os.linesep
|
||||
html += '<pre lang="lua"><code>' + highlight_lua(LuaDoc._read_file(os.path.join(self._project_root, 'manual', 'luadoc', 'example', 'pv', 'retrievingpersistentdata.lua'))) + '</code></pre>'
|
||||
|
||||
html += '<h2 id="deleting">' + self._get_term('pv.deleting:title') + '</h2>' + os.linesep
|
||||
html += '<p>' + self._get_term('pv.deleting:paragraph_1') + '</p>' + os.linesep
|
||||
html += '<pre lang="lua"><code>' + highlight_lua(LuaDoc._read_file(os.path.join(self._project_root, 'manual', 'luadoc', 'example', 'pv', 'deletingpersistentdata.lua'))) + '</code></pre>'
|
||||
|
||||
html += '<h2 id="checking">' + self._get_term('pv.checking:title') + '</h2>' + os.linesep
|
||||
html += '<p>' + self._get_term('pv.checking:paragraph_1') + '</p>' + os.linesep
|
||||
html += '<pre lang="lua"><code>' + highlight_lua(LuaDoc._read_file(os.path.join(self._project_root, 'manual', 'luadoc', 'example', 'pv', 'checkingforpersistentdata.lua'))) + '</code></pre>'
|
||||
|
||||
LuaDoc._write_file(os.path.join(output_dir, LuaDoc.FILENAME_PV), self._add_toc(html))
|
||||
|
||||
def _build_enums(self, output_dir: str, nav: list) -> None:
|
||||
title = self._get_term('enum:title')
|
||||
nav_enums = nav + [{'title': title, 'href': LuaDoc.FILENAME_ENUM}]
|
||||
@ -854,6 +891,7 @@ class LuaDoc:
|
||||
|
||||
def _get_header(self, title: str, nav: list) -> str:
|
||||
menu = ' <li><a href="' + LuaDoc.FILENAME_GLOBALS + '">' + self._get_term('globals:title') + '</a></li>' + os.linesep
|
||||
menu += ' <li><a href="' + LuaDoc.FILENAME_PV + '">' + self._get_term('pv:title') + '</a></li>' + os.linesep
|
||||
for k in sorted(list(self._libs.keys()) + ['enum', 'set']):
|
||||
if k == 'enum':
|
||||
menu += ' <li><a href="' + LuaDoc.FILENAME_ENUM + '">' + self._get_term('enum:title') + '</a></li>' + os.linesep
|
||||
|
||||
6
manual/luadoc/example/pv/checkingforpersistentdata.lua
Normale Datei
6
manual/luadoc/example/pv/checkingforpersistentdata.lua
Normale Datei
@ -0,0 +1,6 @@
|
||||
if pv.freight_car_1 == nil then
|
||||
pv.freight_car_1 = {
|
||||
cargo = 'none',
|
||||
destination = 'unset'
|
||||
}
|
||||
end
|
||||
4
manual/luadoc/example/pv/deletingpersistentdata.lua
Normale Datei
4
manual/luadoc/example/pv/deletingpersistentdata.lua
Normale Datei
@ -0,0 +1,4 @@
|
||||
pv.number = nil
|
||||
pv.title = nil
|
||||
pv.very_cool = nil
|
||||
pv.freight_car_1 = nil
|
||||
9
manual/luadoc/example/pv/retrievingpersistentdata.lua
Normale Datei
9
manual/luadoc/example/pv/retrievingpersistentdata.lua
Normale Datei
@ -0,0 +1,9 @@
|
||||
log.debug(pv.number)
|
||||
log.debug(pv.title)
|
||||
log.debug(pv.very_cool)
|
||||
|
||||
log.debug(pv.freight_car_1.cargo)
|
||||
|
||||
for k, v in pairs(pv['freight_car_1']) do
|
||||
log.debug(k, v)
|
||||
end
|
||||
8
manual/luadoc/example/pv/storingpersistentdata.lua
Normale Datei
8
manual/luadoc/example/pv/storingpersistentdata.lua
Normale Datei
@ -0,0 +1,8 @@
|
||||
pv.number = 42
|
||||
pv.title = 'Traintastic is awesome!'
|
||||
pv.very_cool = true
|
||||
|
||||
pv['freight_car_1'] = {
|
||||
cargo = 'grain',
|
||||
destination = 'upper yard'
|
||||
}
|
||||
@ -67,6 +67,10 @@
|
||||
"type": "constant",
|
||||
"since": "0.1"
|
||||
},
|
||||
"pv": {
|
||||
"type": "object",
|
||||
"since": "0.3"
|
||||
},
|
||||
"world": {
|
||||
"type": "object",
|
||||
"since": "0.1"
|
||||
@ -102,4 +106,4 @@
|
||||
"type": "library",
|
||||
"since": "0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"mode": {},
|
||||
"mute": {},
|
||||
"no_smoke": {},
|
||||
"blocks": {},
|
||||
"zones": {},
|
||||
"on_block_assigned": {
|
||||
"parameters": [
|
||||
|
||||
@ -2218,5 +2218,53 @@
|
||||
{
|
||||
"term": "object.zone.trains:description",
|
||||
"definition": "List of {ref:object.trainzonestatus|train zone status} objects of all trains that are entering, entered or leaving the zone."
|
||||
},
|
||||
{
|
||||
"term": "globals.pv:description",
|
||||
"definition": "The {ref:pv|persistent variable} table."
|
||||
},
|
||||
{
|
||||
"term": "pv:title",
|
||||
"definition": "Persistent variables"
|
||||
},
|
||||
{
|
||||
"term": "pv:paragraph_1",
|
||||
"definition": "Persistent variables allow you to store and retrieve data that remains available across multiple executions of the Lua script. This can be particularly useful for maintaining state information that needs to be retained beyond the current script's lifetime."
|
||||
},
|
||||
{
|
||||
"term": "pv:paragraph_2",
|
||||
"definition": "The {ref:globals#pv|`pv`} global provides a simple and efficient interface for interacting with persistent data. Any values stored in {ref:globals#pv|`pv`} are saved across script executions and world save and loads. Below is a detailed breakdown of how to use the {ref:globals#pv|`pv`} global."
|
||||
},
|
||||
{
|
||||
"term": "pv.storing:title",
|
||||
"definition": "Storing persistent data"
|
||||
},
|
||||
{
|
||||
"term": "pv.storing:paragraph_1",
|
||||
"definition": "You can store data in {ref:globals#pv|`pv`} just like you would with a regular Lua table. Supported data types are numbers, strings, booleans, tables, {ref:enum|enums}, {ref:set|sets}, {ref:object|objects} and object methods."
|
||||
},
|
||||
{
|
||||
"term": "pv.retrieving:title",
|
||||
"definition": "Retrieving persistent data"
|
||||
},
|
||||
{
|
||||
"term": "pv.retrieving:paragraph_1",
|
||||
"definition": "To retrieve a previously stored value, including tables, access the corresponding key in the {ref:globals#pv|`pv`} global:"
|
||||
},
|
||||
{
|
||||
"term": "pv.deleting:title",
|
||||
"definition": "Deleting persistent data"
|
||||
},
|
||||
{
|
||||
"term": "pv.deleting:paragraph_1",
|
||||
"definition": "To delete a stored persistent value, including tables, simply assign `nil` to the desired key:"
|
||||
},
|
||||
{
|
||||
"term": "pv.checking:title",
|
||||
"definition": "Checking for persistent data"
|
||||
},
|
||||
{
|
||||
"term": "pv.checking:paragraph_1",
|
||||
"definition": "To determine if a persistent variable has been set, use an `if` statement with `nil` checks. Variables in {ref:globals#pv|`pv`} that haven't been initialized or have been deleted will return `nil`. This pattern is useful for initializing default values or handling cases where the persistent variables are cleared."
|
||||
}
|
||||
]
|
||||
|
||||
@ -25,7 +25,7 @@ def highlight_replace(code: str, css_class: str, clickable_links: bool = False)
|
||||
|
||||
|
||||
def highlight_lua(code: str) -> str:
|
||||
code = re.sub(r'\b(math|table|string|class|enum|set|log|world)\b', r'<span class="global">\1</span>', code) # globals
|
||||
code = re.sub(r'\b(math|table|string|class|enum|set|log|world|pv)\b', r'<span class="global">\1</span>', code) # globals
|
||||
code = re.sub(r'\b([A-Z_][A-Z0-9_]*)\b', r'<span class="const">\1</span>', code) # CONSTANTS
|
||||
code = re.sub(r'\b(and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b', r'<span class="keyword">\1</span>', code) # keywords
|
||||
code = re.sub(r'\b((|-|\+)[0-9]+(\\.[0-9]*|)((e|E)(|-|\+)[0-9]+|))\b', r'<span class="number">\1</span>', code) # numbers: infloat, decimal
|
||||
|
||||
@ -32,6 +32,8 @@ target_include_directories(traintastic-server SYSTEM PRIVATE
|
||||
thirdparty)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(thirdparty/catch2)
|
||||
set_target_properties(Catch2 PROPERTIES CXX_STANDARD 17)
|
||||
add_executable(traintastic-server-test test/main.cpp)
|
||||
add_dependencies(traintastic-server-test traintastic-lang)
|
||||
target_compile_definitions(traintastic-server-test PRIVATE -DTRAINTASTIC_TEST)
|
||||
@ -42,6 +44,7 @@ if(BUILD_TESTING)
|
||||
target_include_directories(traintastic-server-test SYSTEM PRIVATE
|
||||
../shared/thirdparty
|
||||
thirdparty)
|
||||
target_link_libraries(traintastic-server-test PRIVATE Catch2::Catch2WithMain)
|
||||
endif()
|
||||
|
||||
file(GLOB SOURCES
|
||||
@ -217,6 +220,9 @@ if(LINUX)
|
||||
if(BUILD_TESTING)
|
||||
target_link_libraries(traintastic-server-test PRIVATE PkgConfig::LIBSYSTEMD)
|
||||
endif()
|
||||
else()
|
||||
# Use inotify for monitoring serial ports:
|
||||
list(APPEND SOURCES "src/os/linux/serialportlistimplinotify.hpp" "src/os/linux/serialportlistimplinotify.cpp")
|
||||
endif()
|
||||
else()
|
||||
# socket CAN is only available on linux:
|
||||
@ -459,8 +465,7 @@ endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
include(Catch)
|
||||
target_include_directories(traintastic-server-test PRIVATE thirdparty/catch2)
|
||||
catch_discover_tests(traintastic-server-test)
|
||||
catch_discover_tests(traintastic-server-test DISCOVERY_MODE PRE_TEST)
|
||||
endif()
|
||||
|
||||
### Doxygen ###
|
||||
|
||||
@ -1,206 +0,0 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
Catch
|
||||
-----
|
||||
|
||||
This module defines a function to help use the Catch test framework.
|
||||
|
||||
The :command:`catch_discover_tests` discovers tests by asking the compiled test
|
||||
executable to enumerate its tests. This does not require CMake to be re-run
|
||||
when tests change. However, it may not work in a cross-compiling environment,
|
||||
and setting test properties is less convenient.
|
||||
|
||||
This command is intended to replace use of :command:`add_test` to register
|
||||
tests, and will create a separate CTest test for each Catch test case. Note
|
||||
that this is in some cases less efficient, as common set-up and tear-down logic
|
||||
cannot be shared by multiple test cases executing in the same instance.
|
||||
However, it provides more fine-grained pass/fail information to CTest, which is
|
||||
usually considered as more beneficial. By default, the CTest test name is the
|
||||
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
|
||||
.. command:: catch_discover_tests
|
||||
|
||||
Automatically add tests with CTest by querying the compiled test executable
|
||||
for available tests::
|
||||
|
||||
catch_discover_tests(target
|
||||
[TEST_SPEC arg1...]
|
||||
[EXTRA_ARGS arg1...]
|
||||
[WORKING_DIRECTORY dir]
|
||||
[TEST_PREFIX prefix]
|
||||
[TEST_SUFFIX suffix]
|
||||
[PROPERTIES name1 value1...]
|
||||
[TEST_LIST var]
|
||||
[REPORTER reporter]
|
||||
[OUTPUT_DIR dir]
|
||||
[OUTPUT_PREFIX prefix}
|
||||
[OUTPUT_SUFFIX suffix]
|
||||
)
|
||||
|
||||
``catch_discover_tests`` sets up a post-build command on the test executable
|
||||
that generates the list of tests by parsing the output from running the test
|
||||
with the ``--list-test-names-only`` argument. This ensures that the full
|
||||
list of tests is obtained. Since test discovery occurs at build time, it is
|
||||
not necessary to re-run CMake when the list of tests changes.
|
||||
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
|
||||
in order to function in a cross-compiling environment.
|
||||
|
||||
Additionally, setting properties on tests is somewhat less convenient, since
|
||||
the tests are not available at CMake time. Additional test properties may be
|
||||
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
|
||||
more fine-grained test control is needed, custom content may be provided
|
||||
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
|
||||
directory property. The set of discovered tests is made accessible to such a
|
||||
script via the ``<target>_TESTS`` variable.
|
||||
|
||||
The options are:
|
||||
|
||||
``target``
|
||||
Specifies the Catch executable, which must be a known CMake executable
|
||||
target. CMake will substitute the location of the built executable when
|
||||
running the test.
|
||||
|
||||
``TEST_SPEC arg1...``
|
||||
Specifies test cases, wildcarded test cases, tags and tag expressions to
|
||||
pass to the Catch executable with the ``--list-test-names-only`` argument.
|
||||
|
||||
``EXTRA_ARGS arg1...``
|
||||
Any extra arguments to pass on the command line to each test case.
|
||||
|
||||
``WORKING_DIRECTORY dir``
|
||||
Specifies the directory in which to run the discovered test cases. If this
|
||||
option is not provided, the current binary directory is used.
|
||||
|
||||
``TEST_PREFIX prefix``
|
||||
Specifies a ``prefix`` to be prepended to the name of each discovered test
|
||||
case. This can be useful when the same test executable is being used in
|
||||
multiple calls to ``catch_discover_tests()`` but with different
|
||||
``TEST_SPEC`` or ``EXTRA_ARGS``.
|
||||
|
||||
``TEST_SUFFIX suffix``
|
||||
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
|
||||
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
|
||||
be specified.
|
||||
|
||||
``PROPERTIES name1 value1...``
|
||||
Specifies additional properties to be set on all tests discovered by this
|
||||
invocation of ``catch_discover_tests``.
|
||||
|
||||
``TEST_LIST var``
|
||||
Make the list of tests available in the variable ``var``, rather than the
|
||||
default ``<target>_TESTS``. This can be useful when the same test
|
||||
executable is being used in multiple calls to ``catch_discover_tests()``.
|
||||
Note that this variable is only available in CTest.
|
||||
|
||||
``REPORTER reporter``
|
||||
Use the specified reporter when running the test case. The reporter will
|
||||
be passed to the Catch executable as ``--reporter reporter``.
|
||||
|
||||
``OUTPUT_DIR dir``
|
||||
If specified, the parameter is passed along as
|
||||
``--out dir/<test_name>`` to Catch executable. The actual file name is the
|
||||
same as the test name. This should be used instead of
|
||||
``EXTRA_ARGS --out foo`` to avoid race conditions writing the result output
|
||||
when using parallel test execution.
|
||||
|
||||
``OUTPUT_PREFIX prefix``
|
||||
May be used in conjunction with ``OUTPUT_DIR``.
|
||||
If specified, ``prefix`` is added to each output file name, like so
|
||||
``--out dir/prefix<test_name>``.
|
||||
|
||||
``OUTPUT_SUFFIX suffix``
|
||||
May be used in conjunction with ``OUTPUT_DIR``.
|
||||
If specified, ``suffix`` is added to each output file name, like so
|
||||
``--out dir/<test_name>suffix``. This can be used to add a file extension to
|
||||
the output e.g. ".xml".
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
function(catch_discover_tests TARGET)
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX"
|
||||
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
|
||||
## Generate a unique name based on the extra arguments
|
||||
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
|
||||
string(SUBSTRING ${args_hash} 0 7 args_hash)
|
||||
|
||||
# Define rule to generate test list for aforementioned test executable
|
||||
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
|
||||
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
|
||||
get_property(crosscompiling_emulator
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "TEST_REPORTER=${_REPORTER}"
|
||||
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
|
||||
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
|
||||
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
||||
"endif()\n"
|
||||
)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
||||
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
|
||||
if (NOT ${test_include_file_set})
|
||||
set_property(DIRECTORY
|
||||
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Cannot set more than one TEST_INCLUDE_FILE"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(_CATCH_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
|
||||
CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
|
||||
)
|
||||
@ -1,132 +0,0 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
set(prefix "${TEST_PREFIX}")
|
||||
set(suffix "${TEST_SUFFIX}")
|
||||
set(spec ${TEST_SPEC})
|
||||
set(extra_args ${TEST_EXTRA_ARGS})
|
||||
set(properties ${TEST_PROPERTIES})
|
||||
set(reporter ${TEST_REPORTER})
|
||||
set(output_dir ${TEST_OUTPUT_DIR})
|
||||
set(output_prefix ${TEST_OUTPUT_PREFIX})
|
||||
set(output_suffix ${TEST_OUTPUT_SUFFIX})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
function(add_command NAME)
|
||||
set(_args "")
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
|
||||
if(${result} EQUAL 0)
|
||||
message(WARNING
|
||||
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
|
||||
)
|
||||
elseif(${result} LESS 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${result}\n"
|
||||
" Output: ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Run test executable to get list of available reporters
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-reporters
|
||||
OUTPUT_VARIABLE reporters_output
|
||||
RESULT_VARIABLE reporters_result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(${reporters_result} EQUAL 0)
|
||||
message(WARNING
|
||||
"Test executable '${TEST_EXECUTABLE}' contains no reporters!\n"
|
||||
)
|
||||
elseif(${reporters_result} LESS 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${reporters_result}\n"
|
||||
" Output: ${reporters_output}\n"
|
||||
)
|
||||
endif()
|
||||
string(FIND "${reporters_output}" "${reporter}" reporter_is_valid)
|
||||
if(reporter AND ${reporter_is_valid} EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"\"${reporter}\" is not a valid reporter!\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Prepare reporter
|
||||
if(reporter)
|
||||
set(reporter_arg "--reporter ${reporter}")
|
||||
endif()
|
||||
|
||||
# Prepare output dir
|
||||
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
|
||||
set(output_dir "${TEST_WORKING_DIR}/${output_dir}")
|
||||
if(NOT EXISTS ${output_dir})
|
||||
file(MAKE_DIRECTORY ${output_dir})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
set(test ${line})
|
||||
# Escape characters in test case names that would be parsed by Catch2
|
||||
set(test_name ${test})
|
||||
foreach(char , [ ])
|
||||
string(REPLACE ${char} "\\${char}" test_name ${test_name})
|
||||
endforeach(char)
|
||||
# ...add output dir
|
||||
if(output_dir)
|
||||
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name})
|
||||
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
|
||||
endif()
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
"${reporter_arg}"
|
||||
"${output_dir_arg}"
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${CTEST_FILE}" "${script}")
|
||||
@ -28,7 +28,7 @@
|
||||
#include "../../../map/node.hpp"
|
||||
#include "../../../../core/objectproperty.hpp"
|
||||
#include "../../../../core/method.hpp"
|
||||
#include "../../../../enum/turnoutposition.hpp"
|
||||
#include <traintastic/enum/turnoutposition.hpp>
|
||||
#include "../../../../hardware/output/map/turnoutoutputmap.hpp"
|
||||
|
||||
class BlockPath;
|
||||
|
||||
@ -80,7 +80,7 @@ struct Attributes
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
static inline void addClassList(InterfaceItem& item, const std::array<std::string_view, N>& classList)
|
||||
static inline void addClassList(InterfaceItem& item, tcb::span<const std::string_view, N> classList)
|
||||
{
|
||||
item.addAttribute(AttributeName::ClassList, classList);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021,2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2021,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -23,6 +23,7 @@
|
||||
#include "object.hpp"
|
||||
#include "idobject.hpp"
|
||||
#include "subobject.hpp"
|
||||
#include "abstractevent.hpp"
|
||||
#include "abstractmethod.hpp"
|
||||
#include "abstractproperty.hpp"
|
||||
#include "abstractobjectproperty.hpp"
|
||||
@ -97,6 +98,16 @@ AbstractVectorProperty* Object::getVectorProperty(std::string_view name)
|
||||
return dynamic_cast<AbstractVectorProperty*>(getItem(name));
|
||||
}
|
||||
|
||||
const AbstractEvent* Object::getEvent(std::string_view name) const
|
||||
{
|
||||
return dynamic_cast<const AbstractEvent*>(getItem(name));
|
||||
}
|
||||
|
||||
AbstractEvent* Object::getEvent(std::string_view name)
|
||||
{
|
||||
return dynamic_cast<AbstractEvent*>(getItem(name));
|
||||
}
|
||||
|
||||
void Object::load(WorldLoader& loader, const nlohmann::json& data)
|
||||
{
|
||||
for(auto& [name, value] : data.items())
|
||||
|
||||
@ -134,6 +134,8 @@ class Object : public std::enable_shared_from_this<Object>
|
||||
AbstractObjectProperty* getObjectProperty(std::string_view name);
|
||||
const AbstractVectorProperty* getVectorProperty(std::string_view name) const;
|
||||
AbstractVectorProperty* getVectorProperty(std::string_view name);
|
||||
const AbstractEvent* getEvent(std::string_view name) const;
|
||||
AbstractEvent* getEvent(std::string_view name);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021,2023 Reinder Feenstra
|
||||
* Copyright (C) 2021,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -46,7 +46,7 @@ InterfaceList::InterfaceList(Object& _parent, std::string_view parentPropertyNam
|
||||
|
||||
Attributes::addDisplayName(create, DisplayName::List::create);
|
||||
Attributes::addEnabled(create, editable);
|
||||
Attributes::addClassList(create, Interfaces::classList);
|
||||
Attributes::addClassList(create, Interfaces::classList());
|
||||
m_interfaceItems.add(create);
|
||||
|
||||
Attributes::addDisplayName(delete_, DisplayName::List::delete_);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Reinder Feenstra
|
||||
* Copyright (C) 2021-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -23,6 +23,35 @@
|
||||
#include "interfaces.hpp"
|
||||
#include "../../utils/ifclassidcreate.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
#include "../../utils/makearray.hpp"
|
||||
|
||||
#include "dccexinterface.hpp"
|
||||
#include "ecosinterface.hpp"
|
||||
#include "hsi88.hpp"
|
||||
#include "loconetinterface.hpp"
|
||||
#include "marklincaninterface.hpp"
|
||||
#include "traintasticdiyinterface.hpp"
|
||||
#include "withrottleinterface.hpp"
|
||||
#include "wlanmausinterface.hpp"
|
||||
#include "xpressnetinterface.hpp"
|
||||
#include "z21interface.hpp"
|
||||
|
||||
tcb::span<const std::string_view> Interfaces::classList()
|
||||
{
|
||||
static constexpr auto classes = makeArray(
|
||||
DCCEXInterface::classId,
|
||||
ECoSInterface::classId,
|
||||
HSI88Interface::classId,
|
||||
LocoNetInterface::classId,
|
||||
MarklinCANInterface::classId,
|
||||
TraintasticDIYInterface::classId,
|
||||
WiThrottleInterface::classId,
|
||||
WlanMausInterface::classId,
|
||||
XpressNetInterface::classId,
|
||||
Z21Interface::classId
|
||||
);
|
||||
return classes;
|
||||
}
|
||||
|
||||
std::shared_ptr<Interface> Interfaces::create(World& world, std::string_view classId, std::string_view id)
|
||||
{
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Reinder Feenstra
|
||||
* Copyright (C) 2021-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -24,36 +24,12 @@
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACES_HPP
|
||||
|
||||
#include "interface.hpp"
|
||||
#include "../../utils/makearray.hpp"
|
||||
|
||||
#include "dccexinterface.hpp"
|
||||
#include "ecosinterface.hpp"
|
||||
#include "hsi88.hpp"
|
||||
#include "loconetinterface.hpp"
|
||||
#include "marklincaninterface.hpp"
|
||||
#include "traintasticdiyinterface.hpp"
|
||||
#include "withrottleinterface.hpp"
|
||||
#include "wlanmausinterface.hpp"
|
||||
#include "xpressnetinterface.hpp"
|
||||
#include "z21interface.hpp"
|
||||
|
||||
struct Interfaces
|
||||
{
|
||||
static constexpr std::string_view classIdPrefix = "interface.";
|
||||
|
||||
static constexpr auto classList = makeArray(
|
||||
DCCEXInterface::classId,
|
||||
ECoSInterface::classId,
|
||||
HSI88Interface::classId,
|
||||
LocoNetInterface::classId,
|
||||
MarklinCANInterface::classId,
|
||||
TraintasticDIYInterface::classId,
|
||||
WiThrottleInterface::classId,
|
||||
WlanMausInterface::classId,
|
||||
XpressNetInterface::classId,
|
||||
Z21Interface::classId
|
||||
);
|
||||
|
||||
static tcb::span<const std::string_view> classList();
|
||||
static std::shared_ptr<Interface> create(World& world, std::string_view classId, std::string_view id = std::string_view{});
|
||||
};
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_OUTPUT_MAP_TURNOUTOUTPUTMAPITEM_HPP
|
||||
|
||||
#include "outputmapitembase.hpp"
|
||||
#include "../../../enum/turnoutposition.hpp"
|
||||
#include <traintastic/enum/turnoutposition.hpp>
|
||||
|
||||
class TurnoutOutputMapItem : public OutputMapItemBase<TurnoutPosition>
|
||||
{
|
||||
|
||||
@ -37,9 +37,9 @@
|
||||
#include <traintastic/enum/outputpairvalue.hpp>
|
||||
#include <traintastic/enum/textalign.hpp>
|
||||
#include "../../src/enum/tristate.hpp"
|
||||
#include "../../src/enum/turnoutposition.hpp"
|
||||
#include <traintastic/enum/turnoutposition.hpp>
|
||||
#include "../../src/enum/signalaspect.hpp"
|
||||
#include "../../src/enum/worldevent.hpp"
|
||||
#include <traintastic/enum/worldevent.hpp>
|
||||
#include "../../src/enum/worldscale.hpp"
|
||||
#include <traintastic/enum/zonetrainstate.hpp>
|
||||
|
||||
@ -81,6 +81,14 @@ struct Enums
|
||||
if constexpr(sizeof...(Ts) != 0)
|
||||
registerValues<Ts...>(L);
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
inline static const std::array<std::string_view, sizeof...(Ts)> getMetaTableNames()
|
||||
{
|
||||
return std::array<std::string_view, sizeof...(Ts)>{EnumName<Ts>::value...};
|
||||
}
|
||||
|
||||
inline static const auto metaTableNames = getMetaTableNames<LUA_ENUMS>();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2023 Reinder Feenstra
|
||||
* Copyright (C) 2021-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -39,6 +39,12 @@ namespace Lua {
|
||||
[[noreturn]] inline void errorCantSetNonExistingProperty(lua_State* L) { luaL_error(L, "can't set non existing property"); abort(); }
|
||||
[[noreturn]] inline void errorCantSetReadOnlyProperty(lua_State* L) { luaL_error(L, "can't set read only property"); abort(); }
|
||||
|
||||
[[noreturn]] inline void errorCantStoreValueAsPersistentVariableUnsupportedType(lua_State* L)
|
||||
{
|
||||
luaL_error(L, "can't store value as persistent variable, unsupported type");
|
||||
abort();
|
||||
}
|
||||
|
||||
[[noreturn]] inline void errorDeadObject(lua_State* L) { luaL_error(L, "dead object"); abort(); }
|
||||
|
||||
[[noreturn]] inline void errorExpectedNArgumentsGotN(lua_State* L, int expected, int got) { luaL_error(L, "expected %d arguments, got %d", expected, got); abort(); }
|
||||
@ -50,6 +56,12 @@ namespace Lua {
|
||||
|
||||
[[noreturn]] inline void errorInternal(lua_State* L) { luaL_error(L, "internal error"); abort(); }
|
||||
|
||||
[[noreturn]] inline void errorTableContainsRecursion(lua_State* L)
|
||||
{
|
||||
luaL_error(L, "table contains recursion");
|
||||
abort();
|
||||
}
|
||||
|
||||
[[noreturn]] inline void errorTableIsReadOnly(lua_State* L) { luaL_error(L, "table is readonly"); abort(); }
|
||||
|
||||
}
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
|
||||
namespace Lua {
|
||||
|
||||
constexpr char const* eventsGlobal = "events";
|
||||
|
||||
struct EventData
|
||||
{
|
||||
ObjectPtrWeak object;
|
||||
@ -65,9 +67,18 @@ AbstractEvent* Event::test(lua_State* L, int index)
|
||||
|
||||
void Event::push(lua_State* L, AbstractEvent& value)
|
||||
{
|
||||
new(lua_newuserdata(L, sizeof(EventData))) EventData(value);
|
||||
luaL_getmetatable(L, metaTableName);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_getglobal(L, eventsGlobal);
|
||||
lua_rawgetp(L, -1, &value);
|
||||
if(lua_isnil(L, -1)) // event not in table
|
||||
{
|
||||
lua_pop(L, 1); // remove nil
|
||||
new(lua_newuserdata(L, sizeof(EventData))) EventData(value);
|
||||
luaL_setmetatable(L, metaTableName);
|
||||
lua_pushvalue(L, -1); // copy userdata on stack
|
||||
lua_rawsetp(L, -3, &value); // add event to table
|
||||
}
|
||||
lua_insert(L, lua_gettop(L) - 1); // swap table and userdata
|
||||
lua_pop(L, 1); // remove table
|
||||
}
|
||||
|
||||
void Event::registerType(lua_State* L)
|
||||
@ -80,6 +91,15 @@ void Event::registerType(lua_State* L)
|
||||
lua_pushcfunction(L, __gc);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// weak table for event userdata:
|
||||
lua_newtable(L);
|
||||
lua_newtable(L); // metatable
|
||||
lua_pushliteral(L, "__mode");
|
||||
lua_pushliteral(L, "v");
|
||||
lua_rawset(L, -3);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setglobal(L, eventsGlobal);
|
||||
}
|
||||
|
||||
int Event::__index(lua_State* L)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020,2022-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2020,2022-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -30,6 +30,8 @@
|
||||
|
||||
namespace Lua {
|
||||
|
||||
constexpr char const* methodsGlobal = "methods";
|
||||
|
||||
struct MethodData
|
||||
{
|
||||
ObjectPtrWeak object;
|
||||
@ -64,9 +66,18 @@ AbstractMethod* Method::test(lua_State* L, int index)
|
||||
|
||||
void Method::push(lua_State* L, AbstractMethod& value)
|
||||
{
|
||||
new(lua_newuserdata(L, sizeof(MethodData))) MethodData(value);
|
||||
luaL_getmetatable(L, metaTableName);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_getglobal(L, methodsGlobal);
|
||||
lua_rawgetp(L, -1, &value);
|
||||
if(lua_isnil(L, -1)) // method not in table
|
||||
{
|
||||
lua_pop(L, 1); // remove nil
|
||||
new(lua_newuserdata(L, sizeof(MethodData))) MethodData(value);
|
||||
luaL_setmetatable(L, metaTableName);
|
||||
lua_pushvalue(L, -1); // copy userdata on stack
|
||||
lua_rawsetp(L, -3, &value); // add method to table
|
||||
}
|
||||
lua_insert(L, lua_gettop(L) - 1); // swap table and userdata
|
||||
lua_pop(L, 1); // remove table
|
||||
}
|
||||
|
||||
void Method::registerType(lua_State* L)
|
||||
@ -77,6 +88,15 @@ void Method::registerType(lua_State* L)
|
||||
lua_pushcfunction(L, __call);
|
||||
lua_setfield(L, -2, "__call");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// weak table for method userdata:
|
||||
lua_newtable(L);
|
||||
lua_newtable(L); // metatable
|
||||
lua_pushliteral(L, "__mode");
|
||||
lua_pushliteral(L, "v");
|
||||
lua_rawset(L, -3);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setglobal(L, methodsGlobal);
|
||||
}
|
||||
|
||||
int Method::__gc(lua_State* L)
|
||||
|
||||
463
server/src/lua/persistentvariables.cpp
Normale Datei
463
server/src/lua/persistentvariables.cpp
Normale Datei
@ -0,0 +1,463 @@
|
||||
/**
|
||||
* server/src/lua/persistent.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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 "persistentvariables.hpp"
|
||||
#include <vector>
|
||||
#include "enums.hpp"
|
||||
#include "error.hpp"
|
||||
#include "event.hpp"
|
||||
#include "method.hpp"
|
||||
#include "object.hpp"
|
||||
#include "sandbox.hpp"
|
||||
#include "script.hpp"
|
||||
#include "sets.hpp"
|
||||
#include "test.hpp"
|
||||
#include "vectorproperty.hpp"
|
||||
#include "../core/abstractmethod.hpp"
|
||||
#include "../utils/contains.hpp"
|
||||
#include "../world/world.hpp"
|
||||
|
||||
namespace Lua {
|
||||
|
||||
static void checkTableRecursion(lua_State* L, std::vector<int>& indices)
|
||||
{
|
||||
const int index = indices.back();
|
||||
assert(lua_istable(L, index));
|
||||
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, index))
|
||||
{
|
||||
if(lua_istable(L, -1))
|
||||
{
|
||||
if(std::find_if(indices.begin(), indices.end(),
|
||||
[L](int idx)
|
||||
{
|
||||
return lua_rawequal(L, idx, -1);
|
||||
}) != indices.end())
|
||||
{
|
||||
errorTableContainsRecursion(L);
|
||||
}
|
||||
|
||||
indices.push_back(lua_gettop(L));
|
||||
checkTableRecursion(L, indices);
|
||||
indices.pop_back();
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkTableRecursion(lua_State* L, int index)
|
||||
{
|
||||
assert(lua_istable(L, index));
|
||||
|
||||
std::vector<int> indices;
|
||||
indices.push_back(lua_absindex(L, index));
|
||||
|
||||
checkTableRecursion(L, indices);
|
||||
}
|
||||
|
||||
static const char* metaTableName = "pv";
|
||||
|
||||
struct PersistentVariablesData
|
||||
{
|
||||
int registryIndex;
|
||||
};
|
||||
|
||||
void PersistentVariables::registerType(lua_State* L)
|
||||
{
|
||||
luaL_newmetatable(L, metaTableName);
|
||||
lua_pushcfunction(L, __index);
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_pushcfunction(L, __newindex);
|
||||
lua_setfield(L, -2, "__newindex");
|
||||
lua_pushcfunction(L, __pairs);
|
||||
lua_setfield(L, -2, "__pairs");
|
||||
lua_pushcfunction(L, __len);
|
||||
lua_setfield(L, -2, "__len");
|
||||
lua_pushcfunction(L, __gc);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
bool PersistentVariables::test(lua_State* L, int index)
|
||||
{
|
||||
return luaL_testudata(L, index, metaTableName);
|
||||
}
|
||||
|
||||
void PersistentVariables::push(lua_State* L)
|
||||
{
|
||||
auto* pv = static_cast<PersistentVariablesData*>(lua_newuserdatauv(L, sizeof(PersistentVariablesData), 1));
|
||||
luaL_getmetatable(L, metaTableName);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_newtable(L);
|
||||
pv->registryIndex = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
void PersistentVariables::push(lua_State* L, const nlohmann::json& value)
|
||||
{
|
||||
switch(value.type())
|
||||
{
|
||||
case nlohmann::json::value_t::null:
|
||||
return lua_pushnil(L);
|
||||
|
||||
case nlohmann::json::value_t::boolean:
|
||||
return lua_pushboolean(L, static_cast<bool>(value));
|
||||
|
||||
case nlohmann::json::value_t::number_integer:
|
||||
case nlohmann::json::value_t::number_unsigned:
|
||||
return lua_pushinteger(L, value);
|
||||
|
||||
case nlohmann::json::value_t::number_float:
|
||||
return lua_pushnumber(L, value);
|
||||
|
||||
case nlohmann::json::value_t::string:
|
||||
{
|
||||
const std::string s = value;
|
||||
lua_pushlstring(L, s.data(), s.size());
|
||||
return;
|
||||
}
|
||||
case nlohmann::json::value_t::object:
|
||||
{
|
||||
if(value.contains("type"))
|
||||
{
|
||||
const std::string type = value["type"];
|
||||
if(type == "object")
|
||||
{
|
||||
if(value.contains("id"))
|
||||
{
|
||||
const std::string id = value["id"];
|
||||
if(auto object = Sandbox::getStateData(L).script().world().getObjectByPath(id))
|
||||
{
|
||||
return Object::push(L, object);
|
||||
}
|
||||
return lua_pushnil(L);
|
||||
}
|
||||
}
|
||||
else if(type == "vector_property")
|
||||
{
|
||||
if(value.contains("object_id") && value.contains("name"))
|
||||
{
|
||||
const std::string id = value["object_id"];
|
||||
if(auto object = Sandbox::getStateData(L).script().world().getObjectByPath(id))
|
||||
{
|
||||
const std::string name = value["name"];
|
||||
if(auto* property = object->getVectorProperty(name); property && property->isScriptReadable())
|
||||
{
|
||||
return VectorProperty::push(L, *property);
|
||||
}
|
||||
return lua_pushnil(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(type == "method")
|
||||
{
|
||||
if(value.contains("object_id") && value.contains("name"))
|
||||
{
|
||||
const std::string id = value["object_id"];
|
||||
if(auto object = Sandbox::getStateData(L).script().world().getObjectByPath(id))
|
||||
{
|
||||
const std::string name = value["name"];
|
||||
if(auto* method = object->getMethod(name); method && method->isScriptCallable())
|
||||
{
|
||||
return Method::push(L, *method);
|
||||
}
|
||||
return lua_pushnil(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(type == "event")
|
||||
{
|
||||
if(value.contains("object_id") && value.contains("name"))
|
||||
{
|
||||
const std::string id = value["object_id"];
|
||||
if(auto object = Sandbox::getStateData(L).script().world().getObjectByPath(id))
|
||||
{
|
||||
const std::string name = value["name"];
|
||||
if(auto* event = object->getEvent(name); event && event->isScriptable())
|
||||
{
|
||||
return Event::push(L, *event);
|
||||
}
|
||||
return lua_pushnil(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(type == "pv")
|
||||
{
|
||||
push(L);
|
||||
if(value.contains("items"))
|
||||
{
|
||||
for(auto item : value["items"])
|
||||
{
|
||||
if(item.is_object()) /*[[likely]]*/
|
||||
{
|
||||
push(L, item["key"]);
|
||||
push(L, item["value"]);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(startsWith(type, "enum."))
|
||||
{
|
||||
if(value.contains("value"))
|
||||
{
|
||||
return pushEnum(L, type.substr(5).c_str(), value["value"]);
|
||||
}
|
||||
}
|
||||
else if(startsWith(type, "set."))
|
||||
{
|
||||
if(value.contains("value"))
|
||||
{
|
||||
return pushSet(L, type.substr(4).c_str(), value["value"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nlohmann::json::value_t::array:
|
||||
assert(false);
|
||||
case nlohmann::json::value_t::binary:
|
||||
case nlohmann::json::value_t::discarded:
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
errorInternal(L);
|
||||
}
|
||||
|
||||
nlohmann::json PersistentVariables::toJSON(lua_State* L, int index)
|
||||
{
|
||||
switch(lua_type(L, index))
|
||||
{
|
||||
case LUA_TNIL: /*[[unlikely]]*/
|
||||
return nullptr;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
return (lua_toboolean(L, index) != 0);
|
||||
|
||||
case LUA_TNUMBER:
|
||||
if(lua_isinteger(L, index))
|
||||
{
|
||||
return lua_tointeger(L, index);
|
||||
}
|
||||
return lua_tonumber(L, index);
|
||||
|
||||
case LUA_TSTRING:
|
||||
return lua_tostring(L, index);
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
if(auto object = Lua::test<::Object>(L, index))
|
||||
{
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", "object");
|
||||
value.emplace("id", object->getObjectId());
|
||||
return value;
|
||||
}
|
||||
else if(auto* vectorProperty = VectorProperty::test(L, index))
|
||||
{
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", "vector_property");
|
||||
value.emplace("object_id", vectorProperty->object().getObjectId());
|
||||
value.emplace("name", vectorProperty->name());
|
||||
return value;
|
||||
}
|
||||
else if(auto* method = Method::test(L, index))
|
||||
{
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", "method");
|
||||
value.emplace("object_id", method->object().getObjectId());
|
||||
value.emplace("name", method->name());
|
||||
return value;
|
||||
}
|
||||
else if(auto* event = Event::test(L, index))
|
||||
{
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", "event");
|
||||
value.emplace("object_id", event->object().getObjectId());
|
||||
value.emplace("name", event->name());
|
||||
return value;
|
||||
}
|
||||
else if(test(L, index))
|
||||
{
|
||||
auto items = nlohmann::json::array();
|
||||
|
||||
auto& pv = *static_cast<PersistentVariablesData*>(luaL_checkudata(L, index, metaTableName));
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, pv.registryIndex);
|
||||
assert(lua_istable(L, -1));
|
||||
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, -2))
|
||||
{
|
||||
auto item = nlohmann::json::object();
|
||||
item["key"] = toJSON(L, -2);
|
||||
item["value"] = toJSON(L, -1);
|
||||
items.push_back(item);
|
||||
lua_pop(L, 1); // pop value
|
||||
}
|
||||
lua_pop(L, 1); // pop table
|
||||
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", "pv");
|
||||
value.emplace("items", items);
|
||||
return value;
|
||||
}
|
||||
else if(lua_getmetatable(L, index))
|
||||
{
|
||||
lua_getfield(L, -1, "__name");
|
||||
std::string_view name = lua_tostring(L, -1);
|
||||
lua_pop(L, 2);
|
||||
|
||||
if(contains(Enums::metaTableNames, name))
|
||||
{
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", std::string("enum.").append(name));
|
||||
value.emplace("value", checkEnum(L, index, name.data()));
|
||||
return value;
|
||||
}
|
||||
else if(contains(Sets::metaTableNames, name))
|
||||
{
|
||||
auto value = nlohmann::json::object();
|
||||
value.emplace("type", std::string("set.").append(name));
|
||||
value.emplace("value", checkSet(L, index, name.data()));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
case LUA_TFUNCTION:
|
||||
case LUA_TTHREAD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
errorInternal(L);
|
||||
}
|
||||
|
||||
int PersistentVariables::__index(lua_State* L)
|
||||
{
|
||||
const auto& pv = *static_cast<const PersistentVariablesData*>(lua_touserdata(L, 1));
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, pv.registryIndex);
|
||||
lua_insert(L, 2); // moves key to 3
|
||||
lua_rawget(L, 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PersistentVariables::__newindex(lua_State* L)
|
||||
{
|
||||
checkValue(L, 2);
|
||||
|
||||
if(lua_istable(L, 3))
|
||||
{
|
||||
checkTableRecursion(L, 3);
|
||||
|
||||
push(L); // push pv userdata
|
||||
lua_insert(L, -2); // swap pv and table
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, -2))
|
||||
{
|
||||
lua_pushvalue(L, -2); // copy key on stack
|
||||
lua_insert(L, -2); // swap copied key and value
|
||||
lua_settable(L, -5); // pops copied key and value
|
||||
}
|
||||
lua_pop(L, 1); // pop table
|
||||
}
|
||||
else if(!test(L, 3))
|
||||
{
|
||||
checkValue(L, 3);
|
||||
}
|
||||
|
||||
const auto& pv = *static_cast<const PersistentVariablesData*>(lua_touserdata(L, 1));
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, pv.registryIndex);
|
||||
lua_insert(L, 2); // moves key to 3 and value to 4
|
||||
lua_rawset(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PersistentVariables::__pairs(lua_State* L)
|
||||
{
|
||||
lua_getglobal(L, "next");
|
||||
assert(lua_isfunction(L, -1));
|
||||
const auto& pv = *static_cast<const PersistentVariablesData*>(lua_touserdata(L, 1));
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, pv.registryIndex);
|
||||
assert(lua_istable(L, -1));
|
||||
return 2;
|
||||
}
|
||||
|
||||
int PersistentVariables::__len(lua_State* L)
|
||||
{
|
||||
const auto& pv = *static_cast<const PersistentVariablesData*>(lua_touserdata(L, 1));
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, pv.registryIndex);
|
||||
lua_len(L, -1);
|
||||
lua_insert(L, -2); // swap length and table
|
||||
lua_pop(L, 1); // pop table
|
||||
return 1;
|
||||
}
|
||||
|
||||
int PersistentVariables::__gc(lua_State* L)
|
||||
{
|
||||
auto* pv = static_cast<PersistentVariablesData*>(lua_touserdata(L, 1));
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, pv->registryIndex);
|
||||
pv->~PersistentVariablesData();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PersistentVariables::checkValue(lua_State* L, int index)
|
||||
{
|
||||
switch(lua_type(L, index))
|
||||
{
|
||||
case LUA_TNIL:
|
||||
case LUA_TBOOLEAN:
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TSTRING:
|
||||
return; // supported
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
if(Lua::test<::Object>(L, index) || VectorProperty::test(L, index) || Method::test(L, index) || Event::test(L, index))
|
||||
{
|
||||
return; // supported
|
||||
}
|
||||
else if(lua_getmetatable(L, index))
|
||||
{
|
||||
lua_getfield(L, -1, "__name");
|
||||
std::string_view name = lua_tostring(L, -1);
|
||||
lua_pop(L, 2);
|
||||
|
||||
if(contains(Enums::metaTableNames, name) || contains(Sets::metaTableNames, name))
|
||||
{
|
||||
return; // supported
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LUA_TTABLE:
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
case LUA_TFUNCTION:
|
||||
case LUA_TTHREAD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
errorCantStoreValueAsPersistentVariableUnsupportedType(L);
|
||||
}
|
||||
|
||||
}
|
||||
52
server/src/lua/persistentvariables.hpp
Normale Datei
52
server/src/lua/persistentvariables.hpp
Normale Datei
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* server/src/lua/persistentvariables.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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_LUA_PERSISTENTVARIABLES_HPP
|
||||
#define TRAINTASTIC_SERVER_LUA_PERSISTENTVARIABLES_HPP
|
||||
|
||||
#include <lua.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace Lua {
|
||||
|
||||
class PersistentVariables
|
||||
{
|
||||
private:
|
||||
static int __index(lua_State* L);
|
||||
static int __newindex(lua_State* L);
|
||||
static int __pairs(lua_State* L);
|
||||
static int __len(lua_State* L);
|
||||
static int __gc(lua_State* L);
|
||||
|
||||
static void checkValue(lua_State* L, int index);
|
||||
|
||||
public:
|
||||
static void registerType(lua_State* L);
|
||||
static bool test(lua_State* L, int index);
|
||||
static void push(lua_State* L);
|
||||
static void push(lua_State* L, const nlohmann::json& value);
|
||||
static nlohmann::json toJSON(lua_State* L, int index);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -26,6 +26,7 @@
|
||||
#include "event.hpp"
|
||||
#include "eventhandler.hpp"
|
||||
#include "log.hpp"
|
||||
#include "persistentvariables.hpp"
|
||||
#include "class.hpp"
|
||||
#include "to.hpp"
|
||||
#include "type.hpp"
|
||||
@ -43,7 +44,7 @@
|
||||
#define LUA_SANDBOX "_sandbox"
|
||||
#define LUA_SANDBOX_GLOBALS "_sandbox_globals"
|
||||
|
||||
constexpr std::array<std::string_view, 23> readOnlyGlobals = {{
|
||||
constexpr std::array<std::string_view, 24> readOnlyGlobals = {{
|
||||
// Lua baselib:
|
||||
"assert",
|
||||
"type",
|
||||
@ -67,6 +68,7 @@ constexpr std::array<std::string_view, 23> readOnlyGlobals = {{
|
||||
// Objects:
|
||||
"world",
|
||||
"log",
|
||||
"pv",
|
||||
// Functions:
|
||||
"is_instance",
|
||||
// Type info:
|
||||
@ -127,6 +129,7 @@ namespace Lua {
|
||||
|
||||
void Sandbox::close(lua_State* L)
|
||||
{
|
||||
syncPersistentVariables(L);
|
||||
delete *static_cast<StateData**>(lua_getextraspace(L)); // free state data
|
||||
lua_close(L);
|
||||
}
|
||||
@ -162,6 +165,7 @@ SandboxPtr Sandbox::create(Script& script)
|
||||
*static_cast<StateData**>(lua_getextraspace(L)) = new StateData(script);
|
||||
|
||||
// register types:
|
||||
PersistentVariables::registerType(L);
|
||||
Enums::registerTypes<LUA_ENUMS>(L);
|
||||
Sets::registerTypes<LUA_SETS>(L);
|
||||
Object::registerTypes(L);
|
||||
@ -226,6 +230,17 @@ SandboxPtr Sandbox::create(Script& script)
|
||||
Log::push(L);
|
||||
lua_setfield(L, -2, "log");
|
||||
|
||||
// add persistent variables:
|
||||
if(script.m_persistentVariables.empty())
|
||||
{
|
||||
PersistentVariables::push(L);
|
||||
}
|
||||
else
|
||||
{
|
||||
PersistentVariables::push(L, script.m_persistentVariables);
|
||||
}
|
||||
lua_setfield(L, -2, "pv");
|
||||
|
||||
// add class types:
|
||||
lua_newtable(L);
|
||||
Class::registerValues(L);
|
||||
@ -268,6 +283,13 @@ int Sandbox::getGlobal(lua_State* L, const char* name)
|
||||
return type;
|
||||
}
|
||||
|
||||
void Sandbox::syncPersistentVariables(lua_State* L)
|
||||
{
|
||||
getGlobal(L, "pv");
|
||||
getStateData(L).script().m_persistentVariables = PersistentVariables::toJSON(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
int Sandbox::pcall(lua_State* L, int nargs, int nresults, int errfunc)
|
||||
{
|
||||
// check if the function has _ENV as first upvalue
|
||||
|
||||
@ -130,6 +130,7 @@ class Sandbox
|
||||
static StateData& getStateData(lua_State* L);
|
||||
static int getGlobal(lua_State* L, const char* name);
|
||||
static int pcall(lua_State* L, int nargs = 0, int nresults = 0, int errfunc = 0);
|
||||
static void syncPersistentVariables(lua_State* L);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@
|
||||
#include "scriptlisttablemodel.hpp"
|
||||
#include "push.hpp"
|
||||
#include "../world/world.hpp"
|
||||
#include "../enum/worldevent.hpp"
|
||||
#include "../set/worldstate.hpp"
|
||||
#include <traintastic/enum/worldevent.hpp>
|
||||
#include <traintastic/set/worldstate.hpp>
|
||||
#include "../core/attributes.hpp"
|
||||
#include "../core/method.tpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
@ -65,6 +65,13 @@ Script::Script(World& world, std::string_view _id) :
|
||||
if(state == LuaScriptState::Running)
|
||||
stopSandbox();
|
||||
}}
|
||||
, clearPersistentVariables{*this, "clear_persistent_variables",
|
||||
[this]()
|
||||
{
|
||||
m_persistentVariables = nullptr;
|
||||
Log::log(*this, LogMessage::I9003_CLEARED_PERSISTENT_VARIABLES);
|
||||
updateEnabled();
|
||||
}}
|
||||
{
|
||||
Attributes::addDisplayName(name, DisplayName::Object::name);
|
||||
Attributes::addEnabled(name, false);
|
||||
@ -80,6 +87,8 @@ Script::Script(World& world, std::string_view _id) :
|
||||
m_interfaceItems.add(start);
|
||||
Attributes::addEnabled(stop, false);
|
||||
m_interfaceItems.add(stop);
|
||||
Attributes::addEnabled(clearPersistentVariables, false);
|
||||
m_interfaceItems.add(clearPersistentVariables);
|
||||
|
||||
updateEnabled();
|
||||
}
|
||||
@ -92,6 +101,11 @@ void Script::load(WorldLoader& loader, const nlohmann::json& data)
|
||||
std::string s;
|
||||
if(loader.readFile(std::filesystem::path(scripts) / m_basename += dotLua, s))
|
||||
code.loadJSON(s);
|
||||
|
||||
if(const auto stateData = loader.getState(id); stateData.contains("persistent_variables"))
|
||||
{
|
||||
m_persistentVariables = stateData["persistent_variables"];
|
||||
}
|
||||
}
|
||||
|
||||
void Script::save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& stateData) const
|
||||
@ -103,6 +117,15 @@ void Script::save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& state
|
||||
|
||||
m_basename = id;
|
||||
saver.writeFile(std::filesystem::path(scripts) / m_basename += dotLua, code);
|
||||
|
||||
if(m_sandbox)
|
||||
{
|
||||
Sandbox::syncPersistentVariables(m_sandbox.get());
|
||||
}
|
||||
if(!m_persistentVariables.empty())
|
||||
{
|
||||
stateData["persistent_variables"] = m_persistentVariables;
|
||||
}
|
||||
}
|
||||
|
||||
void Script::addToWorld()
|
||||
@ -156,14 +179,18 @@ void Script::worldEvent(WorldState worldState, WorldEvent worldEvent)
|
||||
void Script::updateEnabled()
|
||||
{
|
||||
const bool editable = contains(m_world.state.value(), WorldState::Edit) && state != LuaScriptState::Running;
|
||||
const bool stoppedOrError = (state == LuaScriptState::Stopped) || (state == LuaScriptState::Error);
|
||||
|
||||
Attributes::setEnabled(id, editable);
|
||||
Attributes::setEnabled(name, editable);
|
||||
Attributes::setEnabled(disabled, editable);
|
||||
Attributes::setEnabled(code, editable);
|
||||
|
||||
Attributes::setEnabled(start, state == LuaScriptState::Stopped || state == LuaScriptState::Error);
|
||||
Attributes::setEnabled(start, stoppedOrError);
|
||||
Attributes::setEnabled(stop, state == LuaScriptState::Running);
|
||||
Attributes::setEnabled(clearPersistentVariables, stoppedOrError && !m_persistentVariables.empty());
|
||||
|
||||
m_world.luaScripts->updateEnabled();
|
||||
}
|
||||
|
||||
void Script::setState(LuaScriptState value)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -32,11 +32,14 @@ namespace Lua {
|
||||
|
||||
class Script : public IdObject
|
||||
{
|
||||
friend class Sandbox;
|
||||
|
||||
private:
|
||||
mutable std::string m_basename; //!< filename on disk for script
|
||||
|
||||
protected:
|
||||
SandboxPtr m_sandbox;
|
||||
nlohmann::json m_persistentVariables;
|
||||
|
||||
void load(WorldLoader& loader, const nlohmann::json& data) final;
|
||||
void save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& stateData) const final;
|
||||
@ -65,6 +68,7 @@ class Script : public IdObject
|
||||
Property<std::string> error;
|
||||
::Method<void()> start;
|
||||
::Method<void()> stop;
|
||||
::Method<void()> clearPersistentVariables;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -54,6 +54,17 @@ ScriptList::ScriptList(Object& _parent, std::string_view parentPropertyName)
|
||||
if(!script->disabled)
|
||||
script->stop();
|
||||
}}
|
||||
, clearPersistentVariables{*this, "clear_persistent_variables",
|
||||
[this]()
|
||||
{
|
||||
for(const auto& script : m_items)
|
||||
{
|
||||
if(Attributes::getEnabled(script->clearPersistentVariables))
|
||||
{
|
||||
script->clearPersistentVariables();
|
||||
}
|
||||
}
|
||||
}}
|
||||
{
|
||||
status.setValueInternal(std::make_shared<LuaStatus>(*this, status.name()));
|
||||
|
||||
@ -74,6 +85,9 @@ ScriptList::ScriptList(Object& _parent, std::string_view parentPropertyName)
|
||||
|
||||
Attributes::addEnabled(stopAll, false);
|
||||
m_interfaceItems.add(stopAll);
|
||||
|
||||
Attributes::addEnabled(clearPersistentVariables, false);
|
||||
m_interfaceItems.add(clearPersistentVariables);
|
||||
}
|
||||
|
||||
ScriptList::~ScriptList()
|
||||
@ -100,20 +114,18 @@ void ScriptList::objectAdded(const std::shared_ptr<Script>& /*object*/)
|
||||
{
|
||||
if(m_items.size() == 1)
|
||||
{
|
||||
Attributes::setEnabled(startAll, true);
|
||||
Attributes::setEnabled(stopAll, true);
|
||||
getWorld(parent()).statuses.appendInternal(status.value());
|
||||
}
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void ScriptList::objectRemoved(const std::shared_ptr<Script>& /*object*/)
|
||||
{
|
||||
if(empty())
|
||||
{
|
||||
Attributes::setEnabled(startAll, false);
|
||||
Attributes::setEnabled(stopAll, false);
|
||||
getWorld(parent()).statuses.removeInternal(status.value());
|
||||
}
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
bool ScriptList::isListedProperty(std::string_view name)
|
||||
@ -121,4 +133,22 @@ bool ScriptList::isListedProperty(std::string_view name)
|
||||
return ScriptListTableModel::isListedProperty(name);
|
||||
}
|
||||
|
||||
void ScriptList::updateEnabled()
|
||||
{
|
||||
bool canStart = false;
|
||||
bool canStop = false;
|
||||
bool canClearPersistentVariables = false;
|
||||
|
||||
for(const auto& script : m_items)
|
||||
{
|
||||
canStart |= Attributes::getEnabled(script->start);
|
||||
canStop |= Attributes::getEnabled(script->stop);
|
||||
canClearPersistentVariables |= Attributes::getEnabled(script->clearPersistentVariables);
|
||||
}
|
||||
|
||||
Attributes::setEnabled(startAll, canStart);
|
||||
Attributes::setEnabled(stopAll, canStop);
|
||||
Attributes::setEnabled(clearPersistentVariables, canClearPersistentVariables);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -47,11 +47,14 @@ class ScriptList final : public ObjectList<Script>
|
||||
::Method<void(const std::shared_ptr<Script>&)> delete_;
|
||||
::Method<void()> startAll;
|
||||
::Method<void()> stopAll;
|
||||
::Method<void()> clearPersistentVariables;
|
||||
|
||||
ScriptList(Object& _parent, std::string_view parentPropertyName);
|
||||
~ScriptList() final;
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
|
||||
void updateEnabled();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2022 Reinder Feenstra
|
||||
* Copyright (C) 2022,2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -24,7 +24,9 @@
|
||||
#define TRAINTASTIC_SERVER_LUA_SETS_HPP
|
||||
|
||||
#include "set.hpp"
|
||||
#include "../../src/set/worldstate.hpp"
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <traintastic/set/worldstate.hpp>
|
||||
|
||||
#define LUA_SETS \
|
||||
WorldState
|
||||
@ -48,6 +50,14 @@ struct Sets
|
||||
if constexpr(sizeof...(Ts) != 0)
|
||||
registerValues<Ts...>(L);
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
inline static const std::array<std::string_view, sizeof...(Ts)> getMetaTableNames()
|
||||
{
|
||||
return std::array<std::string_view, sizeof...(Ts)>{set_name_v<Ts>...};
|
||||
}
|
||||
|
||||
inline static const auto metaTableNames = getMetaTableNames<LUA_SETS>();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2023 Reinder Feenstra
|
||||
* Copyright (C) 2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -29,6 +29,8 @@
|
||||
|
||||
namespace Lua {
|
||||
|
||||
constexpr char const* vectorPropertiesGlobal = "vector_properties";
|
||||
|
||||
struct VectorPropertyData
|
||||
{
|
||||
ObjectPtrWeak object;
|
||||
@ -63,9 +65,18 @@ AbstractVectorProperty* VectorProperty::test(lua_State* L, int index)
|
||||
|
||||
void VectorProperty::push(lua_State* L, AbstractVectorProperty& value)
|
||||
{
|
||||
new(lua_newuserdata(L, sizeof(VectorPropertyData))) VectorPropertyData(value);
|
||||
luaL_getmetatable(L, metaTableName);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_getglobal(L, vectorPropertiesGlobal);
|
||||
lua_rawgetp(L, -1, &value);
|
||||
if(lua_isnil(L, -1)) // vector property not in table
|
||||
{
|
||||
lua_pop(L, 1); // remove nil
|
||||
new(lua_newuserdata(L, sizeof(VectorPropertyData))) VectorPropertyData(value);
|
||||
luaL_setmetatable(L, metaTableName);
|
||||
lua_pushvalue(L, -1); // copy userdata on stack
|
||||
lua_rawsetp(L, -3, &value); // add vector property to table
|
||||
}
|
||||
lua_insert(L, lua_gettop(L) - 1); // swap table and userdata
|
||||
lua_pop(L, 1); // remove table
|
||||
}
|
||||
|
||||
void VectorProperty::registerType(lua_State* L)
|
||||
@ -78,6 +89,15 @@ void VectorProperty::registerType(lua_State* L)
|
||||
lua_pushcfunction(L, __gc);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// weak table for vector property userdata:
|
||||
lua_newtable(L);
|
||||
lua_newtable(L); // metatable
|
||||
lua_pushliteral(L, "__mode");
|
||||
lua_pushliteral(L, "v");
|
||||
lua_rawset(L, -3);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setglobal(L, vectorPropertiesGlobal);
|
||||
}
|
||||
|
||||
int VectorProperty::__index(lua_State* L)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
/**
|
||||
* server/src/enum/turnoutposition.hpp
|
||||
* server/src/os/linux/isserialdevice.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2020-2021 Reinder Feenstra
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -20,9 +20,20 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ENUM_TURNOUTPOSITION_HPP
|
||||
#define TRAINTASTIC_SERVER_ENUM_TURNOUTPOSITION_HPP
|
||||
#ifndef TRAINTASTIC_SERVER_OS_LINUX_ISSERIALDEVICE_HPP
|
||||
#define TRAINTASTIC_SERVER_OS_LINUX_ISSERIALDEVICE_HPP
|
||||
|
||||
#include <traintastic/enum/turnoutposition.hpp>
|
||||
#include "../../utils/startswith.hpp"
|
||||
|
||||
namespace Linux {
|
||||
|
||||
inline bool isSerialDevice(std::string_view devPath)
|
||||
{
|
||||
return startsWith(devPath, "/dev/ttyS") ||
|
||||
startsWith(devPath, "/dev/ttyUSB") ||
|
||||
startsWith(devPath, "/dev/ttyACM");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
159
server/src/os/linux/serialportlistimplinotify.cpp
Normale Datei
159
server/src/os/linux/serialportlistimplinotify.cpp
Normale Datei
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* server/src/os/linux/serialportlistimplinotify.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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 "serialportlistimplinotify.hpp"
|
||||
#include "isserialdevice.hpp"
|
||||
#include <sys/inotify.h>
|
||||
#include <poll.h>
|
||||
#include <filesystem>
|
||||
#include "../../core/eventloop.hpp"
|
||||
#include "../../utils/startswith.hpp"
|
||||
#include "../../utils/setthreadname.hpp"
|
||||
|
||||
namespace Linux {
|
||||
|
||||
SerialPortListImplInotify::SerialPortListImplInotify(SerialPortList& list)
|
||||
: SerialPortListImpl(list)
|
||||
, m_stopEvent{eventfd(0, O_NONBLOCK)}
|
||||
, m_thread{
|
||||
[this]()
|
||||
{
|
||||
setThreadName("serialport-inotify");
|
||||
|
||||
int inotifyFd = inotify_init1(IN_NONBLOCK);
|
||||
if(inotifyFd == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int watchFd = inotify_add_watch(inotifyFd, "/dev", IN_CREATE | IN_DELETE);
|
||||
if(watchFd == -1)
|
||||
{
|
||||
close(inotifyFd);
|
||||
return;
|
||||
}
|
||||
|
||||
// Polling for events:
|
||||
pollfd fds[2];
|
||||
fds[0].fd = inotifyFd;
|
||||
fds[0].events = POLLIN;
|
||||
fds[1].fd = m_stopEvent;
|
||||
fds[1].events = POLLIN;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int r = poll(fds, 2, -1); // wait for events
|
||||
if(r == -1)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
{
|
||||
continue; // interrupted by signal
|
||||
}
|
||||
break; // poll failed
|
||||
}
|
||||
|
||||
if(fds[1].revents & POLLIN) // stop event
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if(fds[0].revents & POLLIN) // inotify event
|
||||
{
|
||||
handleInotifyEvents(inotifyFd);
|
||||
}
|
||||
}
|
||||
|
||||
// clean up:
|
||||
inotify_rm_watch(inotifyFd, watchFd);
|
||||
close(inotifyFd);
|
||||
}}
|
||||
{
|
||||
}
|
||||
|
||||
SerialPortListImplInotify::~SerialPortListImplInotify()
|
||||
{
|
||||
eventfd_write(m_stopEvent, 1);
|
||||
m_thread.join();
|
||||
close(m_stopEvent);
|
||||
}
|
||||
|
||||
void SerialPortListImplInotify::handleInotifyEvents(int inotifyFd)
|
||||
{
|
||||
char buffer[1024];
|
||||
ssize_t length = read(inotifyFd, buffer, sizeof(buffer));
|
||||
if(length < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(ssize_t i = 0; i < length;)
|
||||
{
|
||||
const auto* event = reinterpret_cast<const inotify_event*>(&buffer[i]);
|
||||
const auto devPath = std::string("/dev/") + event->name;
|
||||
|
||||
if(event->mask & IN_CREATE)
|
||||
{
|
||||
if(isSerialDevice(devPath))
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, devPath]()
|
||||
{
|
||||
addToList(devPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(event->mask & IN_DELETE)
|
||||
{
|
||||
if(isSerialDevice(devPath))
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, devPath]()
|
||||
{
|
||||
removeFromList(devPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
i += sizeof(inotify_event) + event->len;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> SerialPortListImplInotify::get() const
|
||||
{
|
||||
std::vector<std::string> devices;
|
||||
const std::filesystem::path devDir("/dev");
|
||||
|
||||
if(std::filesystem::exists(devDir) && std::filesystem::is_directory(devDir))
|
||||
{
|
||||
for(const auto& entry : std::filesystem::directory_iterator(devDir))
|
||||
{
|
||||
const auto& devPath = entry.path().string();
|
||||
if(isSerialDevice(devPath))
|
||||
{
|
||||
devices.push_back(devPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
}
|
||||
48
server/src/os/linux/serialportlistimplinotify.hpp
Normale Datei
48
server/src/os/linux/serialportlistimplinotify.hpp
Normale Datei
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* server/src/os/linux/serialportlistimplinotify.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 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_LINUX_SERIALPORTLISTIMPLINOTIFY_HPP
|
||||
#define TRAINTASTIC_SERVER_OS_LINUX_SERIALPORTLISTIMPLINOTIFY_HPP
|
||||
|
||||
#include "../serialportlistimpl.hpp"
|
||||
#include <thread>
|
||||
|
||||
namespace Linux {
|
||||
|
||||
class SerialPortListImplInotify final : public SerialPortListImpl
|
||||
{
|
||||
private:
|
||||
int m_stopEvent;
|
||||
std::thread m_thread;
|
||||
|
||||
void handleInotifyEvents(int inotifyFd);
|
||||
|
||||
public:
|
||||
SerialPortListImplInotify(SerialPortList& list);
|
||||
~SerialPortListImplInotify() final;
|
||||
|
||||
std::vector<std::string> get() const final;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "serialportlistimplsystemd.hpp"
|
||||
#include "isserialdevice.hpp"
|
||||
#include "../../core/eventloop.hpp"
|
||||
#include "../../utils/startswith.hpp"
|
||||
#include "../../utils/setthreadname.hpp"
|
||||
@ -42,11 +43,7 @@ static std::string_view getDevPath(sd_device* device)
|
||||
|
||||
static bool isSerialDevice(sd_device* device)
|
||||
{
|
||||
auto devPath = getDevPath(device);
|
||||
return
|
||||
startsWith(devPath, "/dev/ttyS") ||
|
||||
startsWith(devPath, "/dev/ttyUSB") ||
|
||||
startsWith(devPath, "/dev/ttyACM");
|
||||
return isSerialDevice(getDevPath(device));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2022 Reinder Feenstra
|
||||
* Copyright (C) 2022,2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -26,6 +26,8 @@
|
||||
#endif
|
||||
#ifdef HAS_LIBSYSTEMD
|
||||
#include "linux/serialportlistimplsystemd.hpp"
|
||||
#elif defined(__linux__)
|
||||
#include "linux/serialportlistimplinotify.hpp"
|
||||
#elif defined(WIN32)
|
||||
#include "windows/serialportlistimplwin32.hpp"
|
||||
#else
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2022 Reinder Feenstra
|
||||
* Copyright (C) 2022,2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -33,6 +33,10 @@ class SerialPortListImpl;
|
||||
namespace Linux {
|
||||
class SerialPortListImplSystemD;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
namespace Linux {
|
||||
class SerialPortListImplInotify;
|
||||
}
|
||||
#elif defined(WIN32)
|
||||
namespace Windows {
|
||||
class SerialPortListImplWin32;
|
||||
@ -46,6 +50,8 @@ class SerialPortList
|
||||
private:
|
||||
#ifdef HAS_LIBSYSTEMD
|
||||
using Impl = Linux::SerialPortListImplSystemD;
|
||||
#elif defined(__linux__)
|
||||
using Impl = Linux::SerialPortListImplInotify;
|
||||
#elif defined(WIN32)
|
||||
using Impl = Windows::SerialPortListImplWin32;
|
||||
#else
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
/**
|
||||
* server/src/enum/worldevent.hpp
|
||||
* server/src/status/simulationstatus.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 Reinder Feenstra
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -20,9 +20,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ENUM_WORLDEVENT_HPP
|
||||
#define TRAINTASTIC_SERVER_ENUM_WORLDEVENT_HPP
|
||||
#include "simulationstatus.hpp"
|
||||
|
||||
#include <traintastic/enum/worldevent.hpp>
|
||||
|
||||
#endif
|
||||
SimulationStatus::SimulationStatus(Object& parent_, std::string_view parentPropertyName_)
|
||||
: Status(parent_, parentPropertyName_)
|
||||
, enabled{this, "enabled", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
|
||||
{
|
||||
label.setValueInternal("$world:simulation$");
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
/**
|
||||
* server/src/set/worldstate.hpp
|
||||
* server/src/status/simulationstatus.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020,2022 Reinder Feenstra
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -20,9 +20,19 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_SET_WORLDSTATE_HPP
|
||||
#define TRAINTASTIC_SERVER_SET_WORLDSTATE_HPP
|
||||
#ifndef TRAINTASTIC_SERVER_STATUS_SIMULATIONSTATUS_HPP
|
||||
#define TRAINTASTIC_SERVER_STATUS_SIMULATIONSTATUS_HPP
|
||||
|
||||
#include <traintastic/set/worldstate.hpp>
|
||||
#include "status.hpp"
|
||||
|
||||
class SimulationStatus : public Status
|
||||
{
|
||||
CLASS_ID("status.simulation")
|
||||
|
||||
public:
|
||||
Property<bool> enabled;
|
||||
|
||||
SimulationStatus(Object& parent_, std::string_view parentPropertyName_);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -147,7 +147,7 @@ Train::Train(World& world, std::string_view _id) :
|
||||
mode{this, "mode", TrainMode::ManualUnprotected, PropertyFlags::ReadWrite | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, mute{this, "mute", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, noSmoke{this, "no_smoke", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, blocks{*this, "blocks", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState},
|
||||
, blocks{*this, "blocks", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly},
|
||||
zones{*this, "zones", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly},
|
||||
notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
, onBlockAssigned{*this, "on_block_assigned", EventFlags::Scriptable}
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* server/src/utils/attributes.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020 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_UTILS_ATTRIBUTES_HPP
|
||||
#define TRAINTASTIC_SERVER_UTILS_ATTRIBUTES_HPP
|
||||
|
||||
|
||||
|
||||
#endif // ATTRIBUTES_HPP
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2022 Reinder Feenstra
|
||||
* Copyright (C) 2022,2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -23,8 +23,15 @@
|
||||
#ifndef TRAINTASTIC_SERVER_UTILS_CONTAINS_HPP
|
||||
#define TRAINTASTIC_SERVER_UTILS_CONTAINS_HPP
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
template<class T, std::size_t N>
|
||||
inline bool contains(const std::array<T, N>& array, T value)
|
||||
{
|
||||
return std::find(array.begin(), array.end(), value) != array.end();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool contains(const std::vector<T>& vector, T value)
|
||||
{
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2021,2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2021,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -54,7 +54,7 @@ RailVehicleList::RailVehicleList(Object& _parent, std::string_view parentPropert
|
||||
|
||||
Attributes::addDisplayName(create, DisplayName::List::create);
|
||||
Attributes::addEnabled(create, editable);
|
||||
Attributes::addClassList(create, RailVehicles::classList);
|
||||
Attributes::addClassList(create, RailVehicles::classList());
|
||||
m_interfaceItems.add(create);
|
||||
|
||||
Attributes::addDisplayName(delete_, DisplayName::List::delete_);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020,2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -22,6 +22,23 @@
|
||||
|
||||
#include "railvehicles.hpp"
|
||||
#include "../../utils/ifclassidcreate.hpp"
|
||||
#include "../../utils/makearray.hpp"
|
||||
|
||||
#include "locomotive.hpp"
|
||||
#include "multipleunit.hpp"
|
||||
#include "freightwagon.hpp"
|
||||
#include "tankwagon.hpp"
|
||||
|
||||
tcb::span<const std::string_view> RailVehicles::classList()
|
||||
{
|
||||
static constexpr auto classes = makeArray(
|
||||
Locomotive::classId,
|
||||
MultipleUnit::classId,
|
||||
FreightWagon::classId,
|
||||
TankWagon::classId
|
||||
);
|
||||
return classes;
|
||||
}
|
||||
|
||||
std::shared_ptr<RailVehicle> RailVehicles::create(World& world, std::string_view classId, std::string_view id)
|
||||
{
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2020,2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2020,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -24,25 +24,13 @@
|
||||
#define TRAINTASTIC_SERVER_VEHICLE_RAIL_RAILVEHICLES_HPP
|
||||
|
||||
#include "railvehicle.hpp"
|
||||
#include "../../utils/makearray.hpp"
|
||||
|
||||
#include "locomotive.hpp"
|
||||
#include "multipleunit.hpp"
|
||||
#include "freightwagon.hpp"
|
||||
#include "tankwagon.hpp"
|
||||
|
||||
struct RailVehicles
|
||||
{
|
||||
static constexpr std::string_view classIdPrefix = "vehicle.rail.";
|
||||
|
||||
static constexpr auto classList = makeArray(
|
||||
Locomotive::classId,
|
||||
MultipleUnit::classId,
|
||||
FreightWagon::classId,
|
||||
TankWagon::classId
|
||||
);
|
||||
|
||||
static tcb::span<const std::string_view> classList();
|
||||
static std::shared_ptr<RailVehicle> create(World& world, std::string_view classId, std::string_view id);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -71,6 +71,7 @@
|
||||
#include "../train/trainlist.hpp"
|
||||
#include "../vehicle/rail/railvehiclelist.hpp"
|
||||
#include "../lua/scriptlist.hpp"
|
||||
#include "../status/simulationstatus.hpp"
|
||||
#include "../utils/category.hpp"
|
||||
|
||||
using nlohmann::json;
|
||||
@ -127,6 +128,8 @@ void World::init(World& world)
|
||||
world.blockRailTiles.setValueInternal(std::make_shared<BlockRailTileList>(world, world.blockRailTiles.name()));
|
||||
world.linkRailTiles.setValueInternal(std::make_shared<LinkRailTileList>(world, world.linkRailTiles.name()));
|
||||
world.nxManager.setValueInternal(std::make_shared<NXManager>(world, world.nxManager.name()));
|
||||
|
||||
world.simulationStatus.setValueInternal(std::make_shared<SimulationStatus>(world, world.simulationStatus.name()));
|
||||
}
|
||||
|
||||
World::World(Private /*unused*/) :
|
||||
@ -235,8 +238,18 @@ World::World(Private /*unused*/) :
|
||||
simulation{this, "simulation", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
|
||||
[this](bool value)
|
||||
{
|
||||
simulationStatus->enabled.setValueInternal(value);
|
||||
if(value)
|
||||
{
|
||||
statuses.appendInternal(simulationStatus.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
statuses.removeInternal(simulationStatus.value());
|
||||
}
|
||||
event(value ? WorldEvent::SimulationEnabled : WorldEvent::SimulationDisabled);
|
||||
}},
|
||||
simulationStatus{this, "simulation_status", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore},
|
||||
save{*this, "save", MethodFlags::NoScript,
|
||||
[this]()
|
||||
{
|
||||
|
||||
@ -57,6 +57,7 @@ class NXManager;
|
||||
class Clock;
|
||||
class TrainList;
|
||||
class RailVehicleList;
|
||||
class SimulationStatus;
|
||||
|
||||
template <typename T>
|
||||
class ControllerList;
|
||||
@ -146,6 +147,7 @@ class World : public Object
|
||||
Property<bool> mute;
|
||||
Property<bool> noSmoke;
|
||||
Property<bool> simulation;
|
||||
ObjectProperty<SimulationStatus> simulationStatus;
|
||||
|
||||
Method<void()> save;
|
||||
|
||||
|
||||
@ -36,10 +36,12 @@
|
||||
#include "../board/board.hpp"
|
||||
#include "../board/tile/tiles.hpp"
|
||||
#include "../hardware/interface/interfaces.hpp"
|
||||
#include "../hardware/interface/dccexinterface.hpp" //! \todo Remove in v0.4
|
||||
#include "../hardware/decoder/decoder.hpp"
|
||||
#include "../hardware/decoder/decoderfunction.hpp"
|
||||
#include "../hardware/identification/identification.hpp"
|
||||
#include "../vehicle/rail/railvehicles.hpp"
|
||||
#include "../vehicle/rail/freightwagon.hpp" //! \todo Remove in v0.4
|
||||
#include "../train/train.hpp"
|
||||
#include "../train/trainblockstatus.hpp"
|
||||
#include "../lua/script.hpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/core/method.tpp"
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/core/method.tpp"
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/core/method.tpp"
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../../src/world/world.hpp"
|
||||
#include "../../src/core/method.tpp"
|
||||
#include "../../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/core/method.tpp"
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/world/worldloader.hpp"
|
||||
#include "../src/world/worldsaver.hpp"
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
#ifndef __aarch64__
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/core/method.tpp"
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../../src/core/method.tpp"
|
||||
#include "../../src/core/objectproperty.tpp"
|
||||
#include "../../src/world/world.hpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../../src/lua/check.hpp"
|
||||
#include "protect.hpp"
|
||||
#include <string_view>
|
||||
@ -28,7 +28,7 @@
|
||||
TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
{
|
||||
{
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -40,7 +40,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -53,7 +53,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -66,7 +66,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -78,7 +78,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -90,7 +90,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -102,7 +102,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -114,7 +114,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -126,7 +126,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", bool)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../../src/lua/check.hpp"
|
||||
#include "protect.hpp"
|
||||
#include "../../src/lua/enums.hpp"
|
||||
@ -56,7 +56,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
const TestType lastValue = EnumValues<TestType>::value.rbegin()->first;
|
||||
|
||||
{
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -68,7 +68,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -80,7 +80,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -92,7 +92,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("enum")
|
||||
INFO("enum");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -105,7 +105,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("other enum")
|
||||
INFO("other enum");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -117,7 +117,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -129,7 +129,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -141,7 +141,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -153,7 +153,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -165,7 +165,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -177,7 +177,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include "protect.hpp"
|
||||
#include "../../src/lua/check.hpp"
|
||||
#include <string_view>
|
||||
@ -28,7 +29,7 @@
|
||||
TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
{
|
||||
{
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -40,7 +41,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -52,7 +53,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -64,59 +65,59 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
lua_pushinteger(L, 123);
|
||||
TestType r;
|
||||
REQUIRE(protect<Lua::check<TestType>>(r, L, -1));
|
||||
REQUIRE(r == Approx(123));
|
||||
REQUIRE(r == Catch::Approx(123));
|
||||
|
||||
closeStateWithProtect(L);
|
||||
}
|
||||
|
||||
{
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
lua_pushnumber(L, 0.5);
|
||||
TestType r;
|
||||
REQUIRE(protect<Lua::check<TestType>>(r, L, -1));
|
||||
REQUIRE(r == Approx(0.5));
|
||||
REQUIRE(r == Catch::Approx(0.5));
|
||||
|
||||
closeStateWithProtect(L);
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"123\"")
|
||||
INFO("\"123\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
lua_pushliteral(L, "123");
|
||||
TestType r;
|
||||
REQUIRE(protect<Lua::check<TestType>>(r, L, -1));
|
||||
REQUIRE(r == Approx(123));
|
||||
REQUIRE(r == Catch::Approx(123));
|
||||
|
||||
closeStateWithProtect(L);
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"0.5\"")
|
||||
INFO("\"0.5\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
lua_pushliteral(L, "0.5");
|
||||
TestType r;
|
||||
REQUIRE(protect<Lua::check<TestType>>(r, L, -1));
|
||||
REQUIRE(r == Approx(0.5));
|
||||
REQUIRE(r == Catch::Approx(0.5));
|
||||
|
||||
closeStateWithProtect(L);
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -128,7 +129,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -140,7 +141,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -152,7 +153,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", float, double)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "protect.hpp"
|
||||
#include "../../src/lua/check.hpp"
|
||||
#include <string_view>
|
||||
@ -28,7 +28,7 @@
|
||||
TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t)
|
||||
{
|
||||
{
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -40,7 +40,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -52,7 +52,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -64,7 +64,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -77,7 +77,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("1000")
|
||||
INFO("1000");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -97,7 +97,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("-1000")
|
||||
INFO("-1000");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -121,7 +121,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("4.2")
|
||||
INFO("4.2");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -133,7 +133,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"123\"")
|
||||
INFO("\"123\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -146,7 +146,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"1000\"")
|
||||
INFO("\"1000\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -166,7 +166,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"-1000\"")
|
||||
INFO("\"-1000\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -190,7 +190,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"4.2\"")
|
||||
INFO("\"4.2\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -202,7 +202,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -214,7 +214,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -226,7 +226,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -238,7 +238,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", int8_t, int16_t, int32_t,
|
||||
}
|
||||
|
||||
{
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
|
||||
@ -20,14 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "protect.hpp"
|
||||
#include "../../src/lua/check.hpp"
|
||||
|
||||
TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_view)
|
||||
{
|
||||
{
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -39,7 +39,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -51,7 +51,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -63,7 +63,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -77,7 +77,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -91,7 +91,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -104,7 +104,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -116,7 +116,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
@ -128,7 +128,7 @@ TEMPLATE_TEST_CASE("Lua::check<>", "[lua][lua-check]", std::string, std::string_
|
||||
}
|
||||
|
||||
{
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
|
||||
lua_State* L = newStateWithProtect();
|
||||
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <algorithm>
|
||||
#include "protect.hpp"
|
||||
#include "run.hpp"
|
||||
#include "../../src/lua/enums.hpp"
|
||||
@ -48,7 +49,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
const TestType lastKey = EnumValues<TestType>::value.rbegin()->first;
|
||||
|
||||
{
|
||||
INFO("write to enum.*")
|
||||
INFO("write to enum.*");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -79,7 +80,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("iterate over enum.*")
|
||||
INFO("iterate over enum.*");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -141,7 +142,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("single value")
|
||||
INFO("single value");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -160,7 +161,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("tostring")
|
||||
INFO("tostring");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -182,7 +183,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("add")
|
||||
INFO("add");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -194,7 +195,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("subtract")
|
||||
INFO("subtract");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -206,7 +207,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("multiply")
|
||||
INFO("multiply");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -218,7 +219,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("modulo")
|
||||
INFO("modulo");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -230,7 +231,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("power")
|
||||
INFO("power");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -242,7 +243,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("divide")
|
||||
INFO("divide");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -254,7 +255,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("divide (integer)")
|
||||
INFO("divide (integer)");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -266,7 +267,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary and")
|
||||
INFO("binary and");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -278,7 +279,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary or")
|
||||
INFO("binary or");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -290,7 +291,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary xor")
|
||||
INFO("binary xor");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -302,7 +303,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("shift left")
|
||||
INFO("shift left");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -314,7 +315,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("shift right")
|
||||
INFO("shift right");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -326,7 +327,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("unary minus")
|
||||
INFO("unary minus");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -337,7 +338,7 @@ TEMPLATE_TEST_CASE("Lua::Enum<>", "[lua][lua-enum]", LUA_ENUMS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary not")
|
||||
INFO("binary not");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../../src/lua/push.hpp"
|
||||
|
||||
// Enums:
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "../../src/world/world.hpp"
|
||||
#include "../../src/core/method.tpp"
|
||||
#include "../../src/core/objectproperty.tpp"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../../hardware/interfaces.hpp"
|
||||
#include "../../../src/core/method.tpp"
|
||||
#include "../../../src/core/objectproperty.tpp"
|
||||
|
||||
95
server/test/lua/script/deadobject.cpp
Normale Datei
95
server/test/lua/script/deadobject.cpp
Normale Datei
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* server/test/lua/script/deadobject.cpp
|
||||
*
|
||||
* This file is part of the traintastic test suite.
|
||||
*
|
||||
* Copyright (C) 2024 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 <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
#include "../../../src/core/eventloop.hpp"
|
||||
#include "../../../src/core/method.tpp"
|
||||
#include "../../../src/core/objectproperty.tpp"
|
||||
#include "../../../src/log/log.hpp"
|
||||
#include "../../../src/log/memorylogger.hpp"
|
||||
#include "../../../src/lua/scriptlist.hpp"
|
||||
#include "../../../src/train/train.hpp"
|
||||
#include "../../../src/train/trainlist.hpp"
|
||||
#include "../../../src/utils/endswith.hpp"
|
||||
#include "../../../src/world/world.hpp"
|
||||
|
||||
using Catch::Matchers::EndsWith;
|
||||
|
||||
TEST_CASE("Lua script: dead object", "[lua][lua-script][lua-script-dead-object]")
|
||||
{
|
||||
Log::enableMemoryLogger(100);
|
||||
EventLoop::threadId = std::this_thread::get_id(); // else MemoryLogger will post it to the event loop
|
||||
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// create train:
|
||||
auto train = world->trains->create();
|
||||
REQUIRE(train);
|
||||
std::weak_ptr<Train> trainWeak = train;
|
||||
train->id = "train";
|
||||
train->name = "Train";
|
||||
train.reset();
|
||||
|
||||
// read name:
|
||||
script->code =
|
||||
"local train = world.get_object(\"train\")\n"
|
||||
"assert(train.name == \"Train\")\n"
|
||||
"world.on_event(\n"
|
||||
" function ()\n"
|
||||
" assert(train.name == \"Train\")\n"
|
||||
" end)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
|
||||
// delete train:
|
||||
world->trains->delete_(trainWeak.lock());
|
||||
REQUIRE(trainWeak.expired());
|
||||
|
||||
// trigger event to call dead object:
|
||||
world->simulation = true;
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running); // error in event does not stop script
|
||||
|
||||
// check log:
|
||||
REQUIRE(Log::getMemoryLogger());
|
||||
auto& logger = *Log::getMemoryLogger();
|
||||
REQUIRE(logger.size() != 0);
|
||||
auto& lastLog = logger[logger.size() - 1];
|
||||
|
||||
REQUIRE(lastLog.message == LogMessage::E9001_X_DURING_EXECUTION_OF_X_EVENT_HANDLER);
|
||||
REQUIRE(lastLog.args);
|
||||
auto args = *lastLog.args;
|
||||
REQUIRE(args.size() == 2);
|
||||
REQUIRE_THAT(args[0], EndsWith("dead object"));
|
||||
REQUIRE(args[1] == "world.on_event");
|
||||
|
||||
// stop script:
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
900
server/test/lua/script/persistentvariables.cpp
Normale Datei
900
server/test/lua/script/persistentvariables.cpp
Normale Datei
@ -0,0 +1,900 @@
|
||||
/**
|
||||
* server/test/lua/script/persistentvariables.cpp
|
||||
*
|
||||
* This file is part of the traintastic test suite.
|
||||
*
|
||||
* Copyright (C) 2024 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 <catch2/catch_test_macros.hpp>
|
||||
#include "../../../src/core/method.tpp"
|
||||
#include "../../../src/core/objectproperty.tpp"
|
||||
#include "../../../src/lua/scriptlist.hpp"
|
||||
#include "../../../src/train/train.hpp"
|
||||
#include "../../../src/train/trainlist.hpp"
|
||||
#include "../../../src/utils/endswith.hpp"
|
||||
#include "../../../src/world/world.hpp"
|
||||
#include "../../../src/world/worldloader.hpp"
|
||||
#include "../../../src/world/worldsaver.hpp"
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - bool", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = true";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == true)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - int", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = 42";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == 42)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - float", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = math.pi";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == math.pi)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - string", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = 'traintastic'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == 'traintastic')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - enum", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = enum.world_event.RUN";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == enum.world_event.RUN)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - set", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = set.world_state.RUN";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == set.world_state.RUN)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - object", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = world";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == world)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - vector property", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
auto train = world->trains->create();
|
||||
REQUIRE(train);
|
||||
train->id = "train";
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = world.get_object('train').blocks";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == world.get_object('train').blocks)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
train.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - method", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = world.stop";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == world.stop)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - event", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = world.on_event";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test == world.on_event)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - unsupported - function", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = function(a, b)\nreturn a+b\nend";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Error);
|
||||
REQUIRE(endsWith(script->error.value(), "can't store value as persistent variable, unsupported type"));
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - table, empty", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = {}";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test ~= nil)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - table, array like", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = {1, 2, 3}";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test[1] == 1)\nassert(pv.test[2] == 2)\nassert(pv.test[3] == 3)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - table, array length", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = {1, 2, 3}";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(#pv.test == 3)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - table, map like", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv.test = {one=1, two=2}";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv.test.one == 1)\nassert(pv.test.two == 2)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - bool as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[true] = false";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[true] == false)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - float as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[math.pi] = 3";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[math.pi] == 3)";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - enum key as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[enum.world_event.RUN] = 'y'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[enum.world_event.RUN] == 'y')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - set as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[set.world_state.RUN] = 'test'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[set.world_state.RUN] == 'test')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - object as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[world] = 'world'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[world] == 'world')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - vector property as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
auto train = world->trains->create();
|
||||
REQUIRE(train);
|
||||
train->id = "train";
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[world.get_object('train').blocks] = 'test'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[world.get_object('train').blocks] == 'test')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
train.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - method as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[world.stop] = 'test'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[world.stop] == 'test')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/restore - event as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[world.on_event] = 'test'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code = "assert(pv[world.on_event] == 'test')";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - unsupported - function as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[function(a, b)\nreturn a+b\nend] = 'test'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Error);
|
||||
REQUIRE(endsWith(script->error.value(), "can't store value as persistent variable, unsupported type"));
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - unsupported - table as key", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code = "pv[{}] = 'test'";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Error);
|
||||
REQUIRE(endsWith(script->error.value(), "can't store value as persistent variable, unsupported type"));
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - unsupported - table recursion", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code =
|
||||
"t = {}\n"
|
||||
"t['t'] = t\n"
|
||||
"pv['t'] = t";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Error);
|
||||
REQUIRE(endsWith(script->error.value(), "table contains recursion"));
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - unsupported - table recursion 2", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code =
|
||||
"t = {}\n"
|
||||
"a = {}\n"
|
||||
"b = {}\n"
|
||||
"t['a'] = a\n"
|
||||
"t['b'] = b\n"
|
||||
"t['a']['b'] = b\n"
|
||||
"t['b']['a'] = a\n"
|
||||
"pv['t'] = t";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Error);
|
||||
REQUIRE(endsWith(script->error.value(), "table contains recursion"));
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - pairs()", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code =
|
||||
"pv.test = {a=1, b=2, c=3}\n"
|
||||
"for k, v in pairs(pv.test) do\n"
|
||||
" assert((k == 'a' and v == 1) or (k == 'b' and v == 2) or (k == 'c' and v == 3))\n"
|
||||
"end";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code =
|
||||
"for k, v in pairs(pv.test) do\n"
|
||||
" assert((k == 'a' and v == 1) or (k == 'b' and v == 2) or (k == 'c' and v == 3))\n"
|
||||
"end";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - ipairs()", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code =
|
||||
"pv.test = {5, 4, 3, 2, 1}\n"
|
||||
"assert(#pv.test == 5)\n"
|
||||
"local n = 1\n"
|
||||
"for k, v in ipairs(pv.test) do\n"
|
||||
" assert(k == n)\n"
|
||||
" assert(v == (#pv.test - n + 1))\n"
|
||||
" n = n + 1\n"
|
||||
"end";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
// check pv:
|
||||
script->code =
|
||||
"assert(#pv.test == 5)\n"
|
||||
"local n = 1\n"
|
||||
"for k, v in ipairs(pv.test) do\n"
|
||||
" assert(k == n)\n"
|
||||
" assert(v == (#pv.test - n + 1))\n"
|
||||
" n = n + 1\n"
|
||||
"end";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - clear", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
|
||||
// set pv:
|
||||
script->code =
|
||||
"pv.object = world\n"
|
||||
"pv.table = {a=1, b=2, c=3}\n"
|
||||
"pv.array = {5, 4, 3, 2, 1}\n";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script->clearPersistentVariables();
|
||||
|
||||
// check pv:
|
||||
script->code =
|
||||
"assert(pv.object == nil)\n"
|
||||
"assert(pv.table == nil)\n"
|
||||
"assert(pv.array == nil)\n";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
script.reset();
|
||||
}
|
||||
|
||||
TEST_CASE("Lua script: pv - save/load", "[lua][lua-script][lua-script-pv]")
|
||||
{
|
||||
static const std::string code =
|
||||
"pv.bool = true\n"
|
||||
"pv.int = 42\n"
|
||||
"pv.float = 2.5\n"
|
||||
"pv.string = \"Traintastic\"\n"
|
||||
"pv.object = world\n"
|
||||
"pv.method = world.stop\n"
|
||||
"pv.event = world.on_event\n"
|
||||
"pv.table = {a=1, b=2, c=3}\n"
|
||||
"pv.array = {5, 4, 3, 2, 1}\n";
|
||||
|
||||
std::filesystem::path ctw;
|
||||
std::string worldUUID;
|
||||
|
||||
{
|
||||
auto world = World::create();
|
||||
REQUIRE(world);
|
||||
worldUUID = world->uuid;
|
||||
|
||||
auto script = world->luaScripts->create();
|
||||
REQUIRE(script);
|
||||
script->id = "script";
|
||||
|
||||
// set pv:
|
||||
script->code = code;
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
|
||||
{
|
||||
ctw = std::filesystem::temp_directory_path() / std::string(world->uuid.value()).append(World::dotCTW);
|
||||
WorldSaver saver(*world, ctw);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_ptr<World> world;
|
||||
{
|
||||
WorldLoader loader(ctw);
|
||||
world = loader.world();
|
||||
REQUIRE(world);
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(world->uuid.value() == worldUUID);
|
||||
REQUIRE(world->luaScripts->length == 1);
|
||||
|
||||
auto script = world->luaScripts->operator[](0);
|
||||
REQUIRE(script);
|
||||
REQUIRE(script->id.value() == "script");
|
||||
REQUIRE(script->code.value() == code);
|
||||
|
||||
script->code =
|
||||
"assert(pv.bool == true)\n"
|
||||
"assert(pv.int == 42)\n"
|
||||
"assert(pv.float == 2.5)\n"
|
||||
"assert(pv.string == \"Traintastic\")\n"
|
||||
"assert(pv.object == world)\n"
|
||||
"assert(pv.method == world.stop)\n"
|
||||
"assert(pv.event == world.on_event)\n"
|
||||
"assert(pv.table.a == 1)\n"
|
||||
"assert(pv.table.b == 2)\n"
|
||||
"assert(pv.table.c == 3)\n"
|
||||
"assert(#pv.array == 5)\n"
|
||||
"assert(pv.array[1] == 5)\n"
|
||||
"assert(pv.array[2] == 4)\n"
|
||||
"assert(pv.array[3] == 3)\n"
|
||||
"assert(pv.array[4] == 2)\n"
|
||||
"assert(pv.array[5] == 1)\n";
|
||||
script->start();
|
||||
INFO(script->error.value());
|
||||
REQUIRE(script->state.value() == LuaScriptState::Running);
|
||||
script->stop();
|
||||
REQUIRE(script->state.value() == LuaScriptState::Stopped);
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(std::filesystem::remove(ctw));
|
||||
}
|
||||
@ -20,7 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <algorithm>
|
||||
#include "protect.hpp"
|
||||
#include "run.hpp"
|
||||
#include "../../src/lua/sets.hpp"
|
||||
@ -49,7 +50,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
const TestType lastKey = set_values_v<TestType>.rbegin()->first;
|
||||
|
||||
{
|
||||
INFO("write to set.*")
|
||||
INFO("write to set.*");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -80,7 +81,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("iterate over enum.*")
|
||||
INFO("iterate over enum.*");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -142,7 +143,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("single value")
|
||||
INFO("single value");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -161,7 +162,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("tostring")
|
||||
INFO("tostring");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -205,7 +206,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("add")
|
||||
INFO("add");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -221,7 +222,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("subtract")
|
||||
INFO("subtract");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -237,7 +238,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("multiply")
|
||||
INFO("multiply");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -249,7 +250,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("modulo")
|
||||
INFO("modulo");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -261,7 +262,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("power")
|
||||
INFO("power");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -273,7 +274,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("divide")
|
||||
INFO("divide");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -285,7 +286,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("divide (integer)")
|
||||
INFO("divide (integer)");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -297,7 +298,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary and")
|
||||
INFO("binary and");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -313,7 +314,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary or")
|
||||
INFO("binary or");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -329,7 +330,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary xor")
|
||||
INFO("binary xor");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -341,7 +342,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("shift left")
|
||||
INFO("shift left");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -353,7 +354,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("shift right")
|
||||
INFO("shift right");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -365,7 +366,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("unary minus")
|
||||
INFO("unary minus");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
@ -376,7 +377,7 @@ TEMPLATE_TEST_CASE("Lua::Set<>", "[lua][lua-set]", LUA_SETS)
|
||||
}
|
||||
|
||||
{
|
||||
INFO("binary not")
|
||||
INFO("binary not");
|
||||
|
||||
lua_State* L = createState<TestType>();
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../../src/lua/to.hpp"
|
||||
#include <string_view>
|
||||
|
||||
@ -38,13 +38,13 @@ TEMPLATE_TEST_CASE("Lua::to<>", "[lua][lua-to]", bool)
|
||||
{
|
||||
lua_State* L = luaL_newstate();
|
||||
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
lua_pushnil(L);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
lua_pushboolean(L, false);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
{
|
||||
@ -54,7 +54,7 @@ TEMPLATE_TEST_CASE("Lua::to<>", "[lua][lua-to]", bool)
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
lua_pushboolean(L, true);
|
||||
REQUIRE(Lua::to<TestType>(L, -1));
|
||||
{
|
||||
@ -64,37 +64,37 @@ TEMPLATE_TEST_CASE("Lua::to<>", "[lua][lua-to]", bool)
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
lua_pushinteger(L, 123);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
lua_pushnumber(L, 0.5);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
lua_pushliteral(L, "test");
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
lua_newtable(L);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
lua_newuserdata(L, 0);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
lua_pushlightuserdata(L, nullptr);
|
||||
REQUIRE_FALSE(Lua::to<TestType>(L, -1));
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include "../../src/lua/enums.hpp"
|
||||
#include "../../src/lua/to.hpp"
|
||||
#include <string_view>
|
||||
@ -57,25 +57,25 @@ TEMPLATE_TEST_CASE("Lua::to<>", "[lua][lua-to]", LUA_ENUMS)
|
||||
|
||||
lua_State* L = luaL_newstate();
|
||||
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
lua_pushnil(L);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
lua_pushboolean(L, false);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
lua_pushboolean(L, true);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("enum")
|
||||
INFO("enum");
|
||||
Lua::Enum<TestType>::registerType(L);
|
||||
Lua::Enum<TestType>::push(L, lastValue);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == lastValue);
|
||||
@ -86,44 +86,44 @@ TEMPLATE_TEST_CASE("Lua::to<>", "[lua][lua-to]", LUA_ENUMS)
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("other enum")
|
||||
INFO("other enum");
|
||||
Lua::Enum<OtherEnumType>::registerType(L);
|
||||
Lua::Enum<OtherEnumType>::push(L, EnumValues<OtherEnumType>::value.rbegin()->first);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
lua_pushinteger(L, 123);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
lua_pushnumber(L, 0.5);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
lua_pushliteral(L, "test");
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
lua_newtable(L);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
lua_newuserdata(L, 0);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
lua_pushlightuserdata(L, nullptr);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == firstValue);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
|
||||
@ -20,13 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include "../../src/lua/to.hpp"
|
||||
#include <string_view>
|
||||
|
||||
#define REQUIRE_TRY_TO(x) \
|
||||
{ \
|
||||
TestType v; \
|
||||
TestType v = 0.0; \
|
||||
REQUIRE(Lua::to<TestType>(L, -1, v)); \
|
||||
REQUIRE(v == x); \
|
||||
}
|
||||
@ -43,67 +44,67 @@ TEMPLATE_TEST_CASE("Lua::to<>", "[lua][lua-to]", float, double)
|
||||
{
|
||||
lua_State* L = luaL_newstate();
|
||||
|
||||
INFO("nil")
|
||||
INFO("nil");
|
||||
lua_pushnil(L);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("false")
|
||||
INFO("false");
|
||||
lua_pushboolean(L, false);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("true")
|
||||
INFO("true");
|
||||
lua_pushboolean(L, true);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("123")
|
||||
INFO("123");
|
||||
lua_pushinteger(L, 123);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Approx(123));
|
||||
REQUIRE_TRY_TO(Approx(123));
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Catch::Approx(123));
|
||||
REQUIRE_TRY_TO(Catch::Approx(123));
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("0.5")
|
||||
INFO("0.5");
|
||||
lua_pushnumber(L, 0.5);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Approx(0.5));
|
||||
REQUIRE_TRY_TO(Approx(0.5));
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Catch::Approx(0.5));
|
||||
REQUIRE_TRY_TO(Catch::Approx(0.5));
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("\"123\"")
|
||||
INFO("\"123\"");
|
||||
lua_pushliteral(L, "123");
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Approx(123));
|
||||
REQUIRE_TRY_TO(Approx(123));
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Catch::Approx(123));
|
||||
REQUIRE_TRY_TO(Catch::Approx(123));
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("\"0.5\"")
|
||||
INFO("\"0.5\"");
|
||||
lua_pushliteral(L, "0.5");
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Approx(0.5));
|
||||
REQUIRE_TRY_TO(Approx(0.5));
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == Catch::Approx(0.5));
|
||||
REQUIRE_TRY_TO(Catch::Approx(0.5));
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("\"test\"")
|
||||
INFO("\"test\"");
|
||||
lua_pushliteral(L, "test");
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("table")
|
||||
INFO("table");
|
||||
lua_newtable(L);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("userdata")
|
||||
INFO("userdata");
|
||||
lua_newuserdata(L, 0);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
lua_pop(L, 1);
|
||||
|
||||
INFO("lightuserdata")
|
||||
INFO("lightuserdata");
|
||||
lua_pushlightuserdata(L, nullptr);
|
||||
REQUIRE(Lua::to<TestType>(L, -1) == 0);
|
||||
REQUIRE_TRY_TO_FAIL();
|
||||
|
||||
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren