mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-18 13:15:20 +02:00
Binds for first person, the HUD, voice chat, time of day, No Bail, challenges, nametags and dots
Eight more actions can be put on a keyboard key, a chord or a controller
combo, in the binds list beside the others. None is bound until the player
binds it.
- First person, Hide HUD, Voice chat on / off, Next time of day, No Bail,
Show / hide challenges, Player nametags, Player dots.
- Each bind runs the console switch for it, so it follows the same rules as
typing it: No Bail still answers to a server that has it off, and the time
of day goes to the server for an admin where the server sets it.
- They are one table (action_binds): a bind is its name in the profile, the
name "bind <name>" takes, its label and its command.
- "bind firstperson|hidehud|voicechat|tod|nobail|challenges|nametags|
nametagdots <n>" sets one from the console; 0 clears it.
- They are not read while the menu, the console or the chat box is open.
Console, which the binds use and anyone can type:
- Every on/off switch takes "toggle" ("nobail toggle", "hideui toggle").
- "firstperson 0|1", which was only in the menu.
- "tod next": the time of day after the current one.
- "mp voice-chat on|off|toggle": the player's own voice chat, leaving the
rest of their voice settings; it says which way it went in their chat.
415 lines
23 KiB
C++
415 lines
23 KiB
C++
#include "Engine/Game/World/world_names.h"
|
|
#include "Extension/Console/commands.h"
|
|
#include "Extension/Profile/local_profile_runtime.h"
|
|
#include "Extension/Multiplayer/Session/session.h"
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <optional>
|
|
namespace dingosdk::console {
|
|
namespace {
|
|
constexpr std::string_view base_level_asset = "Levels/Game/DingoLevel_Root/DingoLevel_Root";
|
|
|
|
const overlay::Level *base_level(const Model &model) {
|
|
for (const auto &level : model.levels)
|
|
if (equal(level.asset, base_level_asset))
|
|
return &level;
|
|
return nullptr;
|
|
}
|
|
// On a dedicated server, its admins choose from the maps that server has
|
|
// (its retail maps and the custom maps in its Mods folder), not this PC's.
|
|
using ServerMaps = std::optional<std::vector<std::string>>;
|
|
ServerMaps admin_server_maps() {
|
|
auto session = multiplayer::model();
|
|
if (!session.server_admin || session.server_maps.empty())
|
|
return std::nullopt;
|
|
return std::move(session.server_maps);
|
|
}
|
|
// A destination `levels`, `load` and Tab offer: never the base root, and on a
|
|
// dedicated server only its maps.
|
|
bool offered(const overlay::Level &level, const ServerMaps &maps) {
|
|
if (equal(level.asset, base_level_asset))
|
|
return false;
|
|
return !maps || std::any_of(maps->begin(), maps->end(), [&](const auto &asset) { return equal(asset, level.asset); });
|
|
}
|
|
std::string destination_name(const overlay::Level &level) {
|
|
if (!level.display_name.empty())
|
|
return level.display_name;
|
|
return world_level_name(level.asset);
|
|
}
|
|
const overlay::Level *find_destination(const Model &model, std::string_view name, const ServerMaps &maps) {
|
|
if (name.empty())
|
|
return nullptr;
|
|
std::size_t number{};
|
|
const auto parsed = std::from_chars(name.data(), name.data() + name.size(), number);
|
|
if (parsed.ec == std::errc{} && parsed.ptr == name.data() + name.size()) {
|
|
std::size_t index = 0;
|
|
for (const auto &level : model.levels)
|
|
if (offered(level, maps) && ++index == number)
|
|
return &level;
|
|
return nullptr;
|
|
}
|
|
for (const auto &level : model.levels)
|
|
if (offered(level, maps) && equal(level.asset, name))
|
|
return &level;
|
|
for (const bool exact : {true, false}) {
|
|
const overlay::Level *result = nullptr;
|
|
for (const auto &level : model.levels) {
|
|
if (!offered(level, maps))
|
|
continue;
|
|
const auto label = destination_name(level);
|
|
const auto alias = world_level_short_name(level.asset);
|
|
if (exact ? (equal(label, name) || equal(alias, name))
|
|
: (ascii_case_insensitive_starts_with(label, name) ||
|
|
ascii_case_insensitive_starts_with(alias, name) ||
|
|
ascii_case_insensitive_starts_with(level.asset, name))) {
|
|
if (result)
|
|
return nullptr;
|
|
result = &level;
|
|
}
|
|
}
|
|
if (result)
|
|
return result;
|
|
}
|
|
return nullptr;
|
|
}
|
|
} // namespace
|
|
void register_world_commands(Commands ®istry) {
|
|
auto levels = action("levels", "List destinations and their one-based indices", Group::world);
|
|
levels.execution = Execution::local;
|
|
levels.run = [](const Model &m, const Values &, const Output &out) {
|
|
const auto maps = admin_server_maps();
|
|
if (maps)
|
|
out("The server's maps:");
|
|
std::size_t index = 0;
|
|
for (const auto &level : m.levels)
|
|
if (offered(level, maps))
|
|
out(std::to_string(++index) + ": " + destination_name(level) + " - " + level.asset);
|
|
if (!index)
|
|
out(maps ? "You have none of the server's maps installed." : "No destinations have been discovered yet.");
|
|
};
|
|
registry.add(std::move(levels));
|
|
auto destination = argument("destination|index");
|
|
destination.complete = [](const Model &m, auto) {
|
|
const auto maps = admin_server_maps();
|
|
std::vector<std::string> result;
|
|
for (const auto &level : m.levels) {
|
|
if (!offered(level, maps))
|
|
continue;
|
|
const auto name = destination_name(level);
|
|
result.push_back(find_destination(m, name, maps) == &level ? name : level.asset);
|
|
}
|
|
return result;
|
|
};
|
|
auto start = argument("start|-", Type::text, true);
|
|
start.complete = [](const Model &m, auto args) {
|
|
std::vector<std::string> result{"-"};
|
|
if (!args.empty())
|
|
if (const auto *level = find_destination(m, args[0], admin_server_maps()))
|
|
result.insert(result.end(), level->start_points.begin(), level->start_points.end());
|
|
return result;
|
|
};
|
|
auto load = action("load", "Load a destination with the base root supplied automatically", Group::world,
|
|
{destination, start});
|
|
load.inspect = [](const Model &m) {
|
|
const auto *root = base_level(m);
|
|
const bool ready = root && root->native_registered && root->can_load;
|
|
return State{
|
|
ready && m.can_queue_load, {}, ready ? m.load_block_reason : "The base level is not ready.", {}, false};
|
|
};
|
|
load.run = [](const Model &m, const Values &args, const Output &out) {
|
|
const auto *root = base_level(m);
|
|
const auto maps = admin_server_maps();
|
|
const auto *level = find_destination(m, std::get<std::string>(args[0]), maps);
|
|
if (!root || !root->native_registered || !root->can_load) {
|
|
out("error: The base level is not ready.");
|
|
return;
|
|
}
|
|
if (!level) {
|
|
out(maps ? "error: The server doesn't have that map (or you don't); use levels or Tab."
|
|
: "error: Destination is missing or ambiguous; use levels or Tab.");
|
|
return;
|
|
}
|
|
// A dedicated server's admin moves the whole server; everyone loads it together.
|
|
if (multiplayer::model().server_admin) {
|
|
const bool sent = multiplayer::queue_command("server", "map " + root->asset + "|" + level->asset, "");
|
|
out(sent ? "Asked the server to change map." : "error: The request queue is busy; try again.");
|
|
return;
|
|
}
|
|
if (!level->can_load) {
|
|
out("error: " +
|
|
(level->load_block_reason.empty() ? "This destination is unavailable." : level->load_block_reason));
|
|
return;
|
|
}
|
|
std::string spawn = args.size() > 1 ? std::get<std::string>(args[1]) : "";
|
|
if (spawn == "-")
|
|
spawn.clear();
|
|
if (spawn.empty())
|
|
if (const auto *automatic = level->automatic_start_point())
|
|
spawn = *automatic;
|
|
if ((!spawn.empty() &&
|
|
std::find(level->start_points.begin(), level->start_points.end(), spawn) == level->start_points.end()) ||
|
|
(spawn.empty() && !level->native_registered && !level->start_points.empty())) {
|
|
out("error: Choose a start point from this destination; use Tab.");
|
|
return;
|
|
}
|
|
request_level(root->asset, "", level->asset, spawn);
|
|
};
|
|
registry.add(std::move(load));
|
|
for (std::size_t i = 0; i < world_layers().size(); ++i) {
|
|
const auto &layer = world_layers()[i];
|
|
auto mode = argument("default|on|off");
|
|
mode.choices = {"default", "on", "off"};
|
|
auto entry = variable("world " + std::string(layer.key), std::string(layer.detail), Group::world, mode);
|
|
entry.inspect = [i](const Model &m) {
|
|
return State{
|
|
m.world.available,
|
|
m.world.enabled[i] ? std::optional<std::string>(*m.world.enabled[i] ? "on" : "off") : std::nullopt,
|
|
m.world.status[i].empty() ? "This layer is unavailable in the current level." : m.world.status[i],
|
|
"Saved selection: " + m.world.choices[i] +
|
|
(m.world.ready && m.world.supported[i] ? "" : "; live state unavailable in this level"),
|
|
m.world.choices[i] != "default"};
|
|
};
|
|
entry.run = [i](const Model &, const Values &args, const Output &out) {
|
|
// While a dedicated server syncs layers, its admins change them for everyone.
|
|
if (multiplayer::model().server_admin && local_profile_world_layers().controlled_by_host) {
|
|
const bool sent = multiplayer::queue_command(
|
|
"server", "layer " + world_layers()[i].key + " " + std::get<std::string>(args[0]), "");
|
|
out(sent ? "Sent to the server." : "error: The request queue is busy; try again.");
|
|
return;
|
|
}
|
|
const bool saved = set_local_world_layer(world_layers()[i].key, std::get<std::string>(args[0]));
|
|
out((saved ? "" : "error: ") + local_profile_world_layers().feedback);
|
|
};
|
|
entry.reset = [i](const Model &, const Output &out) {
|
|
const bool saved = set_local_world_layer(world_layers()[i].key, "default");
|
|
out((saved ? "" : "error: ") + local_profile_world_layers().feedback);
|
|
};
|
|
registry.add(std::move(entry));
|
|
}
|
|
// Time of day: each map has seven time layers, "<map>_tod_<n>_<name>"; one is forced on
|
|
// and the rest off, or all seven go back to the level's own choice.
|
|
static constexpr std::array<std::string_view, 8> times{"default", "morning", "noon", "afternoon",
|
|
"evening", "night", "weatherday", "weathernight"};
|
|
auto time = argument("default|morning|noon|afternoon|evening|night|weatherday|weathernight|next");
|
|
time.choices.assign(times.begin(), times.end());
|
|
time.choices.emplace_back("next"); // the one after the current: for a button that steps through them
|
|
auto tod = variable("tod", "Time of day on the current map", Group::world, time);
|
|
// The current map's time layers in slot order, or empty when it has none.
|
|
const auto time_layers = [](const Model &m) {
|
|
std::vector<std::size_t> layers;
|
|
for (unsigned slot = 1; slot < times.size(); ++slot) {
|
|
const auto prefix = std::string(world_map_key(m.world.map)) + "_tod_" + std::to_string(slot) + "_";
|
|
const auto found = std::find_if(world_layers().begin(), world_layers().end(),
|
|
[&](const auto &layer) { return layer.key.starts_with(prefix); });
|
|
if (m.world.map == WorldMap::none || found == world_layers().end()) return std::vector<std::size_t>{};
|
|
layers.push_back(static_cast<std::size_t>(found - world_layers().begin()));
|
|
}
|
|
return layers;
|
|
};
|
|
tod.inspect = [time_layers](const Model &m) {
|
|
const auto layers = time_layers(m);
|
|
std::size_t forced{};
|
|
for (std::size_t i = 0; i < layers.size(); ++i) {
|
|
const auto &choice = m.world.choices[layers[i]];
|
|
if (choice == "on") forced = forced ? times.size() : i + 1;
|
|
else if (choice != "off") forced = times.size();
|
|
}
|
|
const auto value = layers.empty() || forced >= times.size() ? std::string("default") : std::string(times[forced]);
|
|
return State{m.world.available && !layers.empty(), value,
|
|
m.world.map == WorldMap::none ? "Load a map first." : "This map has no time-of-day layers.",
|
|
"Saved for this map", value != "default"};
|
|
};
|
|
tod.run = [time_layers](const Model &m, const Values &args, const Output &out) {
|
|
auto wanted = lower(std::get<std::string>(args[0]));
|
|
if (wanted == "next") {
|
|
// As inspect reads it: the one layer forced on, or the level's own.
|
|
const auto now_layers = time_layers(m);
|
|
std::size_t forced{};
|
|
for (std::size_t i = 0; i < now_layers.size(); ++i) {
|
|
const auto &choice = m.world.choices[now_layers[i]];
|
|
if (choice == "on") forced = forced ? times.size() : i + 1;
|
|
else if (choice != "off") forced = times.size();
|
|
}
|
|
wanted = std::string(times[forced >= times.size() ? 1 : (forced + 1) % times.size()]);
|
|
}
|
|
const auto found = std::find(times.begin(), times.end(), wanted);
|
|
if (found == times.end()) { out("error: Choose default, morning, noon, afternoon, evening, night, weatherday or weathernight."); return; }
|
|
const auto slot = static_cast<std::size_t>(found - times.begin());
|
|
// While a dedicated server syncs layers, its admins set the time for everyone.
|
|
if (multiplayer::model().server_admin && local_profile_world_layers().controlled_by_host) {
|
|
const bool sent = multiplayer::queue_command("server", "tod " + wanted, "");
|
|
out(sent ? "Sent to the server." : "error: The request queue is busy; try again.");
|
|
return;
|
|
}
|
|
const auto layers = time_layers(m);
|
|
if (layers.empty()) { out(m.world.map == WorldMap::none ? "error: Load a map first." : "error: This map has no time-of-day layers."); return; }
|
|
// The others switch off before the new one comes on, so two never load together.
|
|
bool saved = true;
|
|
for (std::size_t i = 0; i < layers.size(); ++i)
|
|
if (i + 1 != slot) saved = saved && set_local_world_layer(world_layers()[layers[i]].key, slot ? "off" : "default");
|
|
if (slot) saved = saved && set_local_world_layer(world_layers()[layers[slot - 1]].key, "on");
|
|
out(saved ? "Time of day: " + wanted + "." : "error: " + local_profile_world_layers().feedback);
|
|
};
|
|
registry.add(std::move(tod));
|
|
auto map = argument("map");
|
|
for (const auto anchor : world_map_anchors())
|
|
map.choices.emplace_back(world_map_key(world_layer_nodes()[anchor].map));
|
|
auto defaults = action("world defaults", "Restore authored world layers for a map", Group::world, {map});
|
|
defaults.run = [](const Model &, const Values &args, const Output &out) {
|
|
const bool saved = restore_local_world_layers(std::get<std::string>(args[0]));
|
|
out((saved ? "" : "error: ") + local_profile_world_layers().feedback);
|
|
};
|
|
registry.add(std::move(defaults));
|
|
auto random_parks = action("park random", "Load a random layout in every park slot", Group::world);
|
|
random_parks.inspect = [](const Model &m) {
|
|
return State{m.parks.available && (!m.parks.controlled_by_host || m.multiplayer.server_admin), {},
|
|
"The host controls park layouts, or park controls are unavailable.", {}, false};
|
|
};
|
|
random_parks.run = [](const Model &, const Values &, const Output &out) {
|
|
if (multiplayer::model().server_admin) {
|
|
const bool sent = multiplayer::queue_command("server", "park random", "");
|
|
out(sent ? "Sent to the server." : "error: The request queue is busy; try again.");
|
|
return;
|
|
}
|
|
const bool saved = load_random_local_parks();
|
|
out((saved ? "" : "error: ") + local_profile_parks().feedback);
|
|
};
|
|
registry.add(std::move(random_parks));
|
|
auto random_on_launch = variable("park random-on-launch", "Randomize local parks on the next game launch",
|
|
Group::world, argument("0|1", Type::boolean));
|
|
random_on_launch.inspect = [](const Model &m) {
|
|
return boolean_state(m.parks.available, m.parks.randomize_on_launch, "Park controls are unavailable.",
|
|
"Local preference; the host still controls multiplayer layouts");
|
|
};
|
|
random_on_launch.run = [](const Model &, const Values &args, const Output &out) {
|
|
const bool saved = set_local_park_randomize_on_launch(std::get<bool>(args[0]));
|
|
out((saved ? "" : "error: ") + local_profile_parks().feedback);
|
|
};
|
|
registry.add(std::move(random_on_launch));
|
|
for (unsigned lot = 0; lot < park_lots.size(); ++lot) {
|
|
auto layout = argument("layout|empty");
|
|
layout.choices = {"empty"};
|
|
for (unsigned family = 0; family < park_families.size(); ++family)
|
|
for (unsigned i = 1; i <= park_lots[lot].counts[family]; ++i)
|
|
layout.choices.push_back(park_id(family, i));
|
|
auto entry = variable("park " + std::string(park_lots[lot].key),
|
|
"Choose the layout for " + std::string(park_lots[lot].label), Group::world, layout);
|
|
entry.inspect = [lot](const Model &m) {
|
|
return State{m.parks.available, m.parks.choices[lot].empty() ? "default" : m.parks.choices[lot],
|
|
"Park controls are unavailable.", "Saved layout", false};
|
|
};
|
|
entry.run = [lot](const Model &, const Values &args, const Output &out) {
|
|
// A lobby's parks follow its host; a dedicated server's admins choose them for everyone.
|
|
if (multiplayer::model().server_admin) {
|
|
const bool sent = multiplayer::queue_command(
|
|
"server", "park " + std::string(park_lots[lot].key) + " " + std::get<std::string>(args[0]), "");
|
|
out(sent ? "Sent to the server." : "error: The request queue is busy; try again.");
|
|
return;
|
|
}
|
|
const bool saved = set_local_park(park_lots[lot].key, std::get<std::string>(args[0]));
|
|
out((saved ? "" : "error: ") + local_profile_parks().feedback);
|
|
};
|
|
registry.add(std::move(entry));
|
|
}
|
|
struct Control {
|
|
const char *key;
|
|
const char *description;
|
|
double maximum;
|
|
double (*read)(const WorldControls &);
|
|
bool population;
|
|
};
|
|
const Control controls[]{
|
|
{"traffic", "Traffic density: -1 default, 0 off, 1 light, 2 normal, 3 busy", 3,
|
|
[](const WorldControls &c) { return double(c.traffic); }, true},
|
|
{"pedestrians", "Pedestrian density: -1 default, 0 off, 1 light, 2 normal, 3 busy", 3,
|
|
[](const WorldControls &c) { return double(c.pedestrians); }, true},
|
|
{"fog", "Fog: -1 default, 0 off, 1 on", 1, [](const WorldControls &c) { return double(c.fog); }, false},
|
|
{"fog_distance", "Fog visibility multiplier; 0.1 to 5, or -1 for default", 5,
|
|
[](const WorldControls &c) { return double(c.fog_distance); }, false},
|
|
{"sky_brightness", "Sky brightness multiplier; 0 to 3, or -1 for default", 3,
|
|
[](const WorldControls &c) { return double(c.sky_brightness); }, false},
|
|
{"clouds", "Cloud opacity multiplier; 0 to 2, or -1 for default", 2,
|
|
[](const WorldControls &c) { return double(c.clouds); }, false},
|
|
{"wind_strength", "Wind strength; 0 to 30, or -1 for default", 30,
|
|
[](const WorldControls &c) { return double(c.wind_strength); }, false},
|
|
{"wind_direction", "Wind direction in degrees; 0 to 360, or -1 for default", 360,
|
|
[](const WorldControls &c) { return double(c.wind_direction); }, false}};
|
|
for (const auto &control : controls) {
|
|
auto value = argument("value|-1", Type::number);
|
|
value.minimum = -1;
|
|
value.maximum = control.maximum;
|
|
if (control.population)
|
|
value.choices = {"-1", "0", "1", "2", "3"};
|
|
if (equal(control.key, "fog"))
|
|
value.choices = {"-1", "0", "1"};
|
|
auto entry = variable(control.key, control.description, Group::world, value);
|
|
entry.aliases = {"environment " + std::string(control.key)};
|
|
entry.inspect = [control](const Model &m) {
|
|
const auto v = control.read(m.world_controls.choices);
|
|
return State{
|
|
control.population ? m.world_controls.population_available : m.world_controls.environment_available,
|
|
v == -1 ? "default" : value_text(v), "World controls are unavailable.", "Saved override", v != -1};
|
|
};
|
|
entry.run = [control](const Model &, const Values &args, const Output &out) {
|
|
const bool saved = set_local_world_control(control.key, static_cast<float>(std::get<double>(args[0])));
|
|
out(saved ? local_profile_world_controls().status
|
|
: "error: Invalid world value, unavailable control, or save failed.");
|
|
};
|
|
entry.reset = [control](const Model &, const Output &out) {
|
|
const bool saved = set_local_world_control(control.key, -1);
|
|
out(saved ? local_profile_world_controls().status : "error: World control could not be restored.");
|
|
};
|
|
registry.add(std::move(entry));
|
|
}
|
|
for (unsigned i=0;i<dingosdk::atmosphere_controls.size();++i) {
|
|
const auto& c=dingosdk::atmosphere_controls[i];
|
|
auto input=argument(c.type==AtmosphereType::texture ? "asset|default" : c.lanes>1 ? "x,y,z,w|default" : "value|default");
|
|
input.complete=[i](const Model& m, auto) {
|
|
std::vector<std::string> result{"default"};
|
|
const auto& c=dingosdk::atmosphere_controls[i];
|
|
if (c.type==AtmosphereType::texture) result.insert(result.end(),m.world_controls.textures[i].begin(),m.world_controls.textures[i].end());
|
|
if (c.type==AtmosphereType::toggle) { result.push_back("0");result.push_back("1"); }
|
|
for (unsigned j=0;j<c.option_count;++j) result.push_back(std::to_string(atmosphere_options[c.option_start+j].value));
|
|
return result;
|
|
};
|
|
auto entry=variable(c.key,std::string(c.section)+" / "+c.label+"; default restores the authored value",Group::world,input);
|
|
entry.inspect=[i](const Model& m) {
|
|
const auto& c=dingosdk::atmosphere_controls[i];const auto& w=m.world_controls;
|
|
const auto& reading=w.atmosphere[i];
|
|
std::optional<std::string> current;
|
|
if (reading.value) current=atmosphere_value_text(c,*reading.value);
|
|
const auto saved=w.choices.atmosphere.find(c.key);
|
|
std::string detail=reading.varies ? "Values vary across the level's environments. " : "";
|
|
if (saved!=w.choices.atmosphere.end()) detail+="Custom: "+atmosphere_value_text(c,saved->second)+(reading.applied ? "" : " (pending)");
|
|
else detail+="Authored values / active convenience commands";
|
|
return State{w.environment_available,current,"Environment bindings are unavailable.",detail,saved!=w.choices.atmosphere.end()};
|
|
};
|
|
entry.run=[i](const Model&,const Values& values,const Output& out) {
|
|
const bool ok=set_local_atmosphere_control(dingosdk::atmosphere_controls[i].key,std::get<std::string>(values[0]));
|
|
out(ok ? local_profile_world_controls().status : "error: Invalid value, texture not loaded, or save failed.");
|
|
};
|
|
entry.reset=[i](const Model&,const Output& out) {
|
|
out(set_local_atmosphere_control(dingosdk::atmosphere_controls[i].key,"default") ? local_profile_world_controls().status : "error: Could not restore this control.");
|
|
};
|
|
registry.add(std::move(entry));
|
|
}
|
|
for (unsigned kind=0;kind<3;++kind) {
|
|
auto entry=action(std::string(atmosphere_groups[kind])+".reset","Restore this group's authored values",Group::world);
|
|
entry.run=[kind](const Model&,const Values&,const Output& out) {
|
|
out(reset_local_atmosphere_controls(kind) ? local_profile_world_controls().status : "error: Could not restore this group.");
|
|
};
|
|
registry.add(std::move(entry));
|
|
}
|
|
auto reset_value = argument("-1", Type::number);
|
|
reset_value.choices = {"-1"};
|
|
reset_value.optional = true;
|
|
auto environment_reset = action("environment reset_environment", "Restore all authored atmosphere settings",
|
|
Group::world, {reset_value});
|
|
environment_reset.run = [](const Model &, const Values &, const Output &out) {
|
|
out(set_local_world_control("reset_environment", -1) ? local_profile_world_controls().status
|
|
: "error: Could not restore environment.");
|
|
};
|
|
registry.add(std::move(environment_reset));
|
|
}
|
|
} // namespace dingosdk::console
|