Skip to content

Commit 1a495f4

Browse files
committed
feat(cli): add game export command
1 parent d608f89 commit 1a495f4

7 files changed

Lines changed: 407 additions & 20 deletions

File tree

CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,17 @@ else()
293293
endif()
294294
endif()
295295

296+
# ----------------------------------------------------
297+
# Optional game command support
298+
# ----------------------------------------------------
299+
if (TARGET vix::game)
300+
message(STATUS "[cli] Game command enabled")
301+
target_link_libraries(vix_cli PRIVATE vix::game)
302+
target_compile_definitions(vix_cli PRIVATE VIX_CLI_HAS_GAME=1)
303+
else()
304+
message(STATUS "[cli] Game command disabled: vix::game target not found")
305+
endif()
306+
296307
if (TARGET vix_warnings)
297308
target_link_libraries(vix_cli PRIVATE vix_warnings)
298309
endif()
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
*
3+
* @file GameExportCommand.hpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2026, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
* vix game export command.
14+
*
15+
*/
16+
#ifndef VIX_CLI_COMMANDS_GAME_EXPORT_COMMAND_HPP
17+
#define VIX_CLI_COMMANDS_GAME_EXPORT_COMMAND_HPP
18+
19+
#include <filesystem>
20+
#include <string>
21+
#include <vector>
22+
23+
namespace vix::commands::GameCommand
24+
{
25+
/**
26+
* @brief Options accepted by `vix game export`.
27+
*/
28+
struct ExportOptions
29+
{
30+
/**
31+
* @brief Project root to export.
32+
*/
33+
std::filesystem::path project_root{"."};
34+
35+
/**
36+
* @brief Optional output directory override.
37+
*/
38+
std::filesystem::path output_directory{};
39+
40+
/**
41+
* @brief Optional export name override.
42+
*/
43+
std::string name{};
44+
45+
/**
46+
* @brief Whether an existing export directory can be overwritten.
47+
*/
48+
bool overwrite{true};
49+
50+
/**
51+
* @brief Whether assets should be copied.
52+
*/
53+
bool copy_assets{true};
54+
};
55+
56+
/**
57+
* @brief Parse options for `vix game export`.
58+
*/
59+
[[nodiscard]] ExportOptions parse_export_options(
60+
const std::vector<std::string> &args);
61+
62+
/**
63+
* @brief Run `vix game export`.
64+
*/
65+
[[nodiscard]] int export_game(
66+
const std::vector<std::string> &args);
67+
68+
/**
69+
* @brief Run the `vix game` command group.
70+
*/
71+
[[nodiscard]] int run(
72+
const std::vector<std::string> &args);
73+
74+
/**
75+
* @brief Print help for `vix game`.
76+
*/
77+
[[nodiscard]] int help();
78+
79+
} // namespace vix::commands::GameCommand
80+
81+
#endif // VIX_CLI_COMMANDS_GAME_EXPORT_COMMAND_HPP

src/CLI.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include <vix/cli/commands/ResetCommand.hpp>
5151
#include <vix/cli/commands/TaskCommand.hpp>
5252
#include <vix/cli/commands/AgentCommand.hpp>
53+
#include <vix/cli/commands/GameExportCommand.hpp>
5354
#include <vix/utils/Env.hpp>
5455
#include <vix/cli/Style.hpp>
5556
#include <vix/utils/Logger.hpp>
@@ -285,6 +286,8 @@ namespace vix
285286
{ return commands::TaskCommand::run(args); };
286287
commands_["agent"] = [](auto args)
287288
{ return commands::AgentCommand::run(args); };
289+
commands_["game"] = [](auto args)
290+
{ return commands::GameCommand::run(args); };
288291

289292
commands_["-h"] = [this](auto args)
290293
{ return help(args); };
@@ -509,6 +512,8 @@ namespace vix
509512
return commands::TaskCommand::help();
510513
if (cmd == "modules")
511514
return commands::ModulesCommand::help();
515+
if (cmd == "game")
516+
return commands::GameCommand::help();
512517

513518
if (cmd.size() > 5 && cmd.rfind("make:", 0) == 0)
514519
return commands::MakeCommand::help();
@@ -655,6 +660,7 @@ namespace vix
655660
out << indent(2) << "Runtime and advanced:\n";
656661
docs("/cli/commands");
657662
out << indent(3) << "agent Run local AI agent commands\n";
663+
out << indent(3) << "game Export and manage Vix game projects\n";
658664
out << indent(3) << "p2p Run P2P node/tools\n";
659665
out << indent(3) << "orm Database migrations/status/rollback\n\n";
660666

@@ -684,6 +690,7 @@ namespace vix
684690
out << indent(2) << "vix make:class User\n";
685691
out << indent(2) << "vix add @cnerium/app\n";
686692
out << indent(2) << "vix install\n";
693+
out << indent(2) << "vix game export\n";
687694
out << indent(2) << "vix agent ask \"Explain Vix.cpp\" --model qwen2.5-coder:1.5b --timeout 120000\n";
688695
out << indent(2) << "vix help run\n\n";
689696

src/commands/Dispatch.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <vix/cli/commands/ResetCommand.hpp>
5050
#include <vix/cli/commands/TaskCommand.hpp>
5151
#include <vix/cli/commands/AgentCommand.hpp>
52+
#include <vix/cli/commands/GameExportCommand.hpp>
5253

5354
#include <stdexcept>
5455

@@ -118,6 +119,14 @@ namespace vix::cli::dispatch
118119
[]()
119120
{ return vix::commands::AgentCommand::help(); }});
120121

122+
add({"game",
123+
"Game",
124+
"Export and manage Vix game projects",
125+
[](const Args &a)
126+
{ return vix::commands::GameCommand::run(a); },
127+
[]()
128+
{ return vix::commands::GameCommand::help(); }});
129+
121130
add({"check",
122131
"Project",
123132
"Validate build / script check",

src/commands/GameExportCommand.cpp

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/**
2+
*
3+
* @file GameExportCommand.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2026, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
* vix game export command.
14+
*
15+
*/
16+
17+
#include <vix/cli/commands/GameExportCommand.hpp>
18+
#include <vix/cli/util/Ui.hpp>
19+
20+
#ifdef VIX_CLI_HAS_GAME
21+
#include <vix/game/GameExportConfig.hpp>
22+
#include <vix/game/GameExporter.hpp>
23+
#endif
24+
25+
#include <cstddef>
26+
#include <iostream>
27+
#include <string>
28+
#include <vector>
29+
30+
namespace vix::commands::GameCommand
31+
{
32+
namespace
33+
{
34+
bool has_flag(
35+
const std::vector<std::string> &args,
36+
const std::string &flag)
37+
{
38+
for (const auto &arg : args)
39+
{
40+
if (arg == flag)
41+
return true;
42+
}
43+
44+
return false;
45+
}
46+
47+
std::string option_value(
48+
const std::vector<std::string> &args,
49+
const std::string &name)
50+
{
51+
for (std::size_t i = 0; i < args.size(); ++i)
52+
{
53+
if (args[i] == name && i + 1 < args.size())
54+
return args[i + 1];
55+
56+
const std::string prefix = name + "=";
57+
58+
if (args[i].rfind(prefix, 0) == 0)
59+
return args[i].substr(prefix.size());
60+
}
61+
62+
return {};
63+
}
64+
65+
bool wants_help(const std::vector<std::string> &args)
66+
{
67+
return has_flag(args, "-h") || has_flag(args, "--help");
68+
}
69+
} // namespace
70+
71+
ExportOptions parse_export_options(
72+
const std::vector<std::string> &args)
73+
{
74+
ExportOptions options;
75+
76+
const auto project_root = option_value(args, "--project-root");
77+
if (!project_root.empty())
78+
options.project_root = project_root;
79+
80+
const auto output = option_value(args, "--output");
81+
if (!output.empty())
82+
options.output_directory = output;
83+
84+
const auto name = option_value(args, "--name");
85+
if (!name.empty())
86+
options.name = name;
87+
88+
options.overwrite = !has_flag(args, "--no-overwrite");
89+
options.copy_assets = !has_flag(args, "--no-assets");
90+
91+
return options;
92+
}
93+
94+
int export_game(
95+
const std::vector<std::string> &args)
96+
{
97+
using namespace vix::cli::util;
98+
99+
if (wants_help(args))
100+
return help();
101+
102+
#ifndef VIX_CLI_HAS_GAME
103+
(void)args;
104+
105+
err_line(std::cerr, "Game support is not enabled in this Vix build.");
106+
warn_line(std::cerr, "Rebuild Vix with -DVIX_ENABLE_GAME=ON.");
107+
return 1;
108+
#else
109+
const auto options = parse_export_options(args);
110+
111+
vix::game::GameExportConfig config;
112+
config.project_root = options.project_root;
113+
config.overwrite = options.overwrite;
114+
config.copy_assets = options.copy_assets;
115+
116+
if (!options.output_directory.empty())
117+
config.output_directory = options.output_directory;
118+
119+
if (!options.name.empty())
120+
config.name = options.name;
121+
122+
vix::game::GameExporter exporter;
123+
124+
auto result = exporter.export_project(config);
125+
if (!result)
126+
{
127+
err_line(std::cerr, "Game export failed.");
128+
warn_line(std::cerr, result.error().message());
129+
return 1;
130+
}
131+
132+
ok_line(std::cout, "Game exported.");
133+
kv(std::cout, "Output", result.value().output_path.string());
134+
kv(std::cout, "Name", result.value().name);
135+
kv(std::cout, "Version", result.value().version);
136+
kv(std::cout, "Asset root", result.value().asset_root);
137+
kv(std::cout, "Copied files", std::to_string(result.value().copied_files));
138+
kv(std::cout, "Copied directories", std::to_string(result.value().copied_directories));
139+
140+
return 0;
141+
#endif
142+
}
143+
144+
int run(
145+
const std::vector<std::string> &args)
146+
{
147+
if (args.empty())
148+
return help();
149+
150+
if (args[0] == "-h" || args[0] == "--help")
151+
return help();
152+
153+
if (args[0] == "export")
154+
{
155+
std::vector<std::string> rest;
156+
rest.reserve(args.size());
157+
158+
for (std::size_t i = 1; i < args.size(); ++i)
159+
rest.push_back(args[i]);
160+
161+
return export_game(rest);
162+
}
163+
164+
vix::cli::util::err_line(std::cerr, "Unknown game command.");
165+
vix::cli::util::warn_line(std::cerr, "Usage: vix game export [options]");
166+
return 1;
167+
}
168+
169+
int help()
170+
{
171+
std::ostream &out = std::cout;
172+
173+
out
174+
<< "vix game\n"
175+
<< "Game project tools.\n\n"
176+
177+
<< "Usage\n"
178+
<< " vix game export [options]\n\n"
179+
180+
<< "Options\n"
181+
<< " --project-root <path> Project root, default: .\n"
182+
<< " --project-root=<path> Same as --project-root <path>\n"
183+
<< " --output <path> Override output directory\n"
184+
<< " --output=<path> Same as --output <path>\n"
185+
<< " --name <name> Override export name\n"
186+
<< " --name=<name> Same as --name <name>\n"
187+
<< " --no-overwrite Fail if output already exists\n"
188+
<< " --no-assets Do not copy assets\n"
189+
<< " -h, --help Show help\n\n"
190+
191+
<< "Examples\n"
192+
<< " vix game export\n"
193+
<< " vix game export --project-root .\n"
194+
<< " vix game export --output dist\n"
195+
<< " vix game export --name mario-release\n"
196+
<< " vix game export --no-overwrite\n\n";
197+
198+
return 0;
199+
}
200+
201+
} // namespace vix::commands::GameCommand

0 commit comments

Comments
 (0)