Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
/* Copyright (C) 2026 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2010-01-09 11:20:14 -08:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2010-01-09 11:20:14 -08:00
|
|
|
* 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.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2010-01-09 11:20:14 -08:00
|
|
|
* 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
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2010-01-09 11:20:14 -08:00
|
|
|
*/
|
|
|
|
|
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "lib/self_test.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "lib/debug.h"
|
|
|
|
|
#include "lib/file/vfs/vfs.h"
|
|
|
|
|
#include "lib/file/vfs/vfs_path.h"
|
|
|
|
|
#include "lib/file/vfs/vfs_util.h"
|
|
|
|
|
#include "lib/path.h"
|
|
|
|
|
#include "ps/CStr.h"
|
|
|
|
|
#include "ps/Errors.h"
|
2010-01-09 11:20:14 -08:00
|
|
|
#include "ps/Filesystem.h"
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "ps/XML/Xeromyces.h"
|
2021-03-01 12:52:24 -08:00
|
|
|
#include "scriptinterface/FunctionWrapper.h"
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
#include "scriptinterface/Context.h"
|
|
|
|
|
#include "scriptinterface/Interface.h"
|
|
|
|
|
#include "scriptinterface/Request.h"
|
2025-08-03 10:37:17 -07:00
|
|
|
#include "simulation2/serialization/StdDeserializer.h"
|
|
|
|
|
#include "simulation2/serialization/StdSerializer.h"
|
|
|
|
|
#include "simulation2/system/Component.h"
|
|
|
|
|
|
|
|
|
|
#include <js/RootingAPI.h>
|
|
|
|
|
#include <js/TypeDecls.h>
|
|
|
|
|
#include <js/Value.h>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
2020-11-14 07:13:56 -08:00
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
class TestComponentScripts : public CxxTest::TestSuite
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
void setUp()
|
|
|
|
|
{
|
2017-12-10 09:33:03 -08:00
|
|
|
g_VFS = CreateVfs();
|
2021-03-23 05:46:59 -07:00
|
|
|
g_VFS->Mount(L"", DataDir() / "mods" / "mod" / "", VFS_MOUNT_MUST_EXIST);
|
|
|
|
|
g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tearDown()
|
|
|
|
|
{
|
|
|
|
|
g_VFS.reset();
|
|
|
|
|
}
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
static void load_script(const Script::Interface& scriptInterface, const VfsPath& pathname)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
|
|
|
|
CVFSFile file;
|
2011-03-23 06:36:20 -07:00
|
|
|
TS_ASSERT_EQUALS(file.Load(g_VFS, pathname), PSRETURN_OK);
|
2012-05-04 14:48:46 -07:00
|
|
|
CStr content = file.DecodeUTF8(); // assume it's UTF-8
|
|
|
|
|
TSM_ASSERT(L"Running script "+pathname.string(), scriptInterface.LoadScript(pathname, content));
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
static void Script_LoadComponentScript(const Script::Interface& scriptInterface, const VfsPath& pathname)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(scriptInterface);
|
2021-05-15 06:54:58 -07:00
|
|
|
CComponentManager* componentManager = scriptInterface.ObjectFromCBData<CComponentManager>(rq);
|
2011-03-23 06:36:20 -07:00
|
|
|
TS_ASSERT(componentManager->LoadScript(VfsPath(L"simulation/components") / pathname));
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
static void Script_LoadHelperScript(const Script::Interface& scriptInterface, const VfsPath& pathname)
|
2010-11-22 12:12:04 -08:00
|
|
|
{
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(scriptInterface);
|
2021-05-15 06:54:58 -07:00
|
|
|
CComponentManager* componentManager = scriptInterface.ObjectFromCBData<CComponentManager>(rq);
|
2011-03-23 06:36:20 -07:00
|
|
|
TS_ASSERT(componentManager->LoadScript(VfsPath(L"simulation/helpers") / pathname));
|
2010-11-22 12:12:04 -08:00
|
|
|
}
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
static JS::Value Script_SerializationRoundTrip(const Script::Interface& scriptInterface, JS::HandleValue value)
|
2020-12-27 09:18:13 -08:00
|
|
|
{
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(scriptInterface);
|
2020-12-27 09:18:13 -08:00
|
|
|
|
|
|
|
|
JS::RootedValue val(rq.cx);
|
|
|
|
|
val = value;
|
|
|
|
|
std::stringstream stream;
|
|
|
|
|
CStdSerializer serializer(scriptInterface, stream);
|
|
|
|
|
serializer.ScriptVal("", &val);
|
|
|
|
|
CStdDeserializer deserializer(scriptInterface, stream);
|
|
|
|
|
deserializer.ScriptVal("", &val);
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-22 07:53:47 -07:00
|
|
|
void test_global_scripts()
|
|
|
|
|
{
|
2024-09-18 09:17:04 -07:00
|
|
|
CXeromycesEngine xeromycesEngine;
|
2020-04-23 05:06:56 -07:00
|
|
|
if (!VfsDirectoryExists(L"globalscripts/tests/"))
|
|
|
|
|
{
|
|
|
|
|
debug_printf("Skipping globalscripts tests (can't find binaries/data/mods/public/globalscripts/tests/)\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-22 07:53:47 -07:00
|
|
|
VfsPaths paths;
|
|
|
|
|
TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"globalscripts/tests/", L"test_*.js", paths));
|
|
|
|
|
for (const VfsPath& path : paths)
|
|
|
|
|
{
|
|
|
|
|
CSimContext context;
|
2023-11-19 12:13:19 -08:00
|
|
|
CComponentManager componentManager(context, *g_ScriptContext, true);
|
2019-09-22 07:53:47 -07:00
|
|
|
ScriptTestSetup(componentManager.GetScriptInterface());
|
2020-12-27 09:18:13 -08:00
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(componentManager.GetScriptInterface());
|
|
|
|
|
Script::Function::Register<Script_SerializationRoundTrip>(rq, "SerializationRoundTrip");
|
2020-12-27 09:18:13 -08:00
|
|
|
|
2019-09-22 07:53:47 -07:00
|
|
|
load_script(componentManager.GetScriptInterface(), path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
void test_scripts()
|
|
|
|
|
{
|
2024-09-18 09:17:04 -07:00
|
|
|
CXeromycesEngine xeromycesEngine;
|
2011-02-25 08:30:55 -08:00
|
|
|
if (!VfsFileExists(L"simulation/components/tests/setup.js"))
|
2010-05-07 08:59:23 -07:00
|
|
|
{
|
2020-04-23 05:06:56 -07:00
|
|
|
debug_printf("Skipping component scripts tests (can't find binaries/data/mods/public/simulation/components/tests/setup.js)\n");
|
2010-05-07 08:59:23 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-09 11:20:14 -08:00
|
|
|
VfsPaths paths;
|
2011-05-25 03:39:13 -07:00
|
|
|
TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"simulation/components/tests/", L"test_*.js", paths));
|
Add a system component to handle stat modifiers, make technologies and auras use this common interface.
The ModifiersManager system component provides an interface to add and
remove modifiers, and get modified stats.
The goal is to merge all the different stat-modifying systems 0 A.D. has
implemented over the years.
This commit makes technologies and auras use ModifiersManager. Some
cheats and AI bonuses also have a similar stat-modifying effect that
have not yet been updated.
Further, this system component makes it possible for e.g. triggers to
easily add modifiers, enabling the writing of Castle Blood Automatic,
RPG or Tower Defense maps without the need for mods or hacks.
The 'Modifier' name was preferred over 'Modification' as it is shorter
and more readable, along with the logic that 'modifiers' store
'modifications' and this stores modifiers. Renaming of other functions
and classes has been left for future work for now.
Internally, this uses a JS data structure. If performance issues arise
with it in the future, this data structure or the whole component could
be moved to C++.
The performance has been tested to be about as fast as the current
implementations (and specifically much faster for global auras with no
icons). Testing showed that sending value modification messages was by
far the slowest part.
Comments by: leper, Stan, elexis
Differential Revision: https://code.wildfiregames.com/D274
This was SVN commit r22767.
2019-08-24 00:37:07 -07:00
|
|
|
TS_ASSERT_OK(vfs::GetPathnames(g_VFS, L"simulation/helpers/tests/", L"test_*.js", paths));
|
2017-09-07 21:22:53 -07:00
|
|
|
paths.push_back(VfsPath(L"simulation/components/tests/setup_test.js"));
|
2015-07-29 16:44:12 -07:00
|
|
|
for (const VfsPath& path : paths)
|
2010-01-09 11:20:14 -08:00
|
|
|
{
|
2020-11-14 07:13:56 -08:00
|
|
|
// Clean up previous scripts.
|
|
|
|
|
g_ScriptContext->ShrinkingGC();
|
2010-01-09 11:20:14 -08:00
|
|
|
CSimContext context;
|
2023-11-19 12:13:19 -08:00
|
|
|
CComponentManager componentManager(context, *g_ScriptContext, true);
|
2010-01-09 11:20:14 -08:00
|
|
|
|
|
|
|
|
ScriptTestSetup(componentManager.GetScriptInterface());
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(componentManager.GetScriptInterface());
|
|
|
|
|
Script::Function::Register<Script_LoadComponentScript>(rq, "LoadComponentScript");
|
|
|
|
|
Script::Function::Register<Script_LoadHelperScript>(rq, "LoadHelperScript");
|
|
|
|
|
Script::Function::Register<Script_SerializationRoundTrip>(rq, "SerializationRoundTrip");
|
2010-01-09 11:20:14 -08:00
|
|
|
|
|
|
|
|
componentManager.LoadComponentTypes();
|
|
|
|
|
|
|
|
|
|
load_script(componentManager.GetScriptInterface(), L"simulation/components/tests/setup.js");
|
2015-07-29 16:44:12 -07:00
|
|
|
load_script(componentManager.GetScriptInterface(), path);
|
2010-01-09 11:20:14 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|