Entity list fixes + add sharedget/set functions to ui/server scripting

This commit is contained in:
Federico Cecchetto 2022-01-04 17:57:09 +01:00
parent ab027b7f3a
commit 019fed2baa
7 changed files with 155 additions and 102 deletions

View File

@ -21,11 +21,13 @@ namespace entity_list
{
enum entity_type
{
type_any,
entity,
actor,
spawner,
weapon,
vehicle,
node,
vehicle_node,
count,
};
@ -49,7 +51,7 @@ namespace entity_list
struct entity_info_t
{
unsigned int id;
unsigned int num;
game::scr_entref_t entref;
std::unordered_map<std::string, std::string> fields;
};
@ -278,7 +280,7 @@ namespace entity_list
};
utils::concurrency::container<data_t> data_{};
unsigned int selected_entity{};
game::scr_entref_t selected_entity{};
bool set_field_window{};
bool selected_fields_window{};
int selected_type = game::SCRIPT_INTEGER;
@ -288,88 +290,60 @@ namespace entity_list
std::string field_value_buffer{};
std::string vector_input[3]{};
bool verify_entity(unsigned int id)
{
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
return type == game::SCRIPT_ENTITY;
}
std::optional<scripting::array> get_entity_array(const entity_type type, const entity_team team)
{
const auto value = scripting::call("getentarray");
if (!value.is<scripting::array>())
if (type == entity_type::entity)
{
return {};
}
const auto all = value.as<scripting::array>();
if (type == entity_type::type_any)
{
return {all};
return {scripting::call("getentarray").as<scripting::array>()};
}
if (type == entity_type::actor)
{
scripting::array result{};
for (auto i = 0; i < all.size(); i++)
const auto actors = scripting::call("getaiarray").as<scripting::array>();
for (auto i = 0; i < actors.size(); i++)
{
const auto raw = all[i].get_raw();
if (raw.type != game::SCRIPT_OBJECT)
{
continue;
}
const auto entity = actors[i].as<scripting::entity>();
const auto team_string = entity.get("team").as<std::string>();
if (!verify_entity(raw.u.uintValue))
{
continue;
}
const auto entity = all[i].as<scripting::entity>();
const auto classname_value = entity.get("classname");
if (!classname_value.is<std::string>())
{
continue;
}
const auto team_value = entity.get("team");
if (!team_value.is<std::string>())
{
continue;
}
const auto classname = classname_value.as<std::string>();
const auto team_ = team_value.as<std::string>();
if (utils::string::find_lower(classname, "actor_") &&
(team == entity_team::team_any || team_ == team_names[team]))
if (team == entity_team::team_any || team_string == team_names[team])
{
result.push(entity);
}
}
return result;
return {result};
}
if (type == entity_type::spawner && team == entity_team::team_any)
{
return scripting::call("getspawnerarray").as<scripting::array>();
return {scripting::call("getspawnerarray").as<scripting::array>()};
}
if (type == entity_type::spawner)
{
return scripting::call("getspawnerteamarray", {team_names[team]}).as<scripting::array>();
return {scripting::call("getspawnerteamarray", {team_names[team]}).as<scripting::array>()};
}
if (type == entity_type::weapon)
{
return scripting::call("getweaponarray").as<scripting::array>();
return {scripting::call("getweaponarray").as<scripting::array>()};
}
if (type == entity_type::node)
{
return scripting::call("getnodearray").as<scripting::array>();
return {scripting::call("getallnodes").as<scripting::array>()};
}
if (type == entity_type::vehicle_node)
{
return {scripting::call("getallvehiclenodes").as<scripting::array>()};
}
if (type == entity_type::vehicle)
{
return {scripting::call("vehicle_getarray").as<scripting::array>()};
}
return {};
@ -404,29 +378,18 @@ namespace entity_list
data.entity_info.clear();
const auto array = value.value();
const scripting::entity player{{0, 0}};
for (auto i = 0; i < array.size(); i++)
{
const auto raw = array[i].get_raw();
if (raw.type != game::SCRIPT_OBJECT)
{
continue;
}
if (!verify_entity(raw.u.uintValue))
{
continue;
}
const auto entity = array[i].as<scripting::entity>();
entity_info_t info{};
info.id = raw.u.uintValue;
info.num = entity.get_entity_reference().entnum;
info.id = entity.get_entity_id();
info.entref = entity.get_entity_reference();
if (data.filters.filter_by_range)
{
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
const auto distance = scripting::call("distance", {player.get("origin"), entity.get("origin")}).as<float>();
if (distance > data.filters.range)
@ -472,60 +435,60 @@ namespace entity_list
});
}
void teleport_to(data_t& data, unsigned int num)
void teleport_to(data_t& data, game::scr_entref_t entref)
{
data.tasks.push_back([num]()
data.tasks.push_back([entref]()
{
try
{
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>();
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
const scripting::entity entity{entref};
const scripting::entity player{{0, 0}};
player.call("setorigin", {entity.get("origin")});
}
catch (...)
{
gui::notification("Error", utils::string::va("^1error teleporting player to entity num %i!", num));
gui::notification("Error", utils::string::va("^1error teleporting player to entity num %i!", entref.entnum));
}
});
}
void teleport_to_reverse(data_t& data, unsigned int num)
void teleport_to_reverse(data_t& data, game::scr_entref_t entref)
{
data.tasks.push_back([num]()
data.tasks.push_back([entref]()
{
try
{
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>();
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
const scripting::entity entity{entref};
const scripting::entity player{{0, 0}};
entity.set("origin", player.get("origin"));
}
catch (...)
{
gui::notification("Error", utils::string::va("^1error teleporting entity num %i to player!", num));
gui::notification("Error", utils::string::va("^1error teleporting entity num %i to player!", entref.entnum));
}
});
}
void delete_entity(data_t& data, unsigned int num)
void delete_entity(data_t& data, game::scr_entref_t entref)
{
data.tasks.push_back([num]()
data.tasks.push_back([entref]()
{
try
{
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>();
const scripting::entity entity{entref};
entity.call("delete");
}
catch (...)
{
gui::notification("Error", utils::string::va("^1error deleting entity num %i!", num));
gui::notification("Error", utils::string::va("^1error deleting entity num %i!", entref.entnum));
}
});
}
void set_entity_field(data_t& data, unsigned int num,
void set_entity_field(data_t& data, game::scr_entref_t entref,
const std::string& name, const std::string& string_value, int type)
{
data.tasks.push_back([num, name, type, string_value, &data]()
data.tasks.push_back([entref, name, type, string_value, &data]()
{
scripting::script_value value{};
@ -546,7 +509,7 @@ namespace entity_list
try
{
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>();
const scripting::entity entity{entref};
scripting::set_entity_field(entity, name, value);
data.force_update = true;
}
@ -557,14 +520,14 @@ namespace entity_list
});
}
void set_entity_field_vector(data_t& data, unsigned int num,
void set_entity_field_vector(data_t& data, game::scr_entref_t entref,
const std::string& name, const scripting::vector& value)
{
data.tasks.push_back([num, name, value, &data]()
data.tasks.push_back([entref, name, value, &data]()
{
try
{
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>();
const scripting::entity entity{entref};
scripting::set_entity_field(entity, name, value);
data.force_update = true;
}
@ -665,11 +628,13 @@ namespace entity_list
{
auto result = 0;
result += ImGui::RadioButton("any", reinterpret_cast<int*>(&data.filters.type), entity_type::type_any);
result += ImGui::RadioButton("entity", reinterpret_cast<int*>(&data.filters.type), entity_type::entity);
result += ImGui::RadioButton("actor", reinterpret_cast<int*>(&data.filters.type), entity_type::actor);
result += ImGui::RadioButton("spawner", reinterpret_cast<int*>(&data.filters.type), entity_type::spawner);
result += ImGui::RadioButton("weapon", reinterpret_cast<int*>(&data.filters.type), entity_type::weapon);
result += ImGui::RadioButton("node", reinterpret_cast<int*>(&data.filters.type), entity_type::node);
result += ImGui::RadioButton("vehicle", reinterpret_cast<int*>(&data.filters.type), entity_type::vehicle);
result += ImGui::RadioButton("path node", reinterpret_cast<int*>(&data.filters.type), entity_type::node);
result += ImGui::RadioButton("vehicle node", reinterpret_cast<int*>(&data.filters.type), entity_type::vehicle_node);
if (result)
{
@ -679,7 +644,8 @@ namespace entity_list
ImGui::TreePop();
}
if (ImGui::TreeNode("Entity team"))
if ((data.filters.type == entity_type::actor || data.filters.type == entity_type::spawner)
&& ImGui::TreeNode("Entity team"))
{
auto result = 0;
@ -728,20 +694,33 @@ namespace entity_list
for (const auto& info : data.entity_info)
{
if (ImGui::TreeNode(utils::string::va("Entity num %i", info.num)))
if (ImGui::TreeNode(utils::string::va("Entity num %i class %i",
info.entref.entnum, info.entref.classnum)))
{
ImGui::Text("Info");
const auto num_str = utils::string::va("%i", info.num);
const auto num_str = utils::string::va("%i", info.entref.entnum);
const auto classnum_str = utils::string::va("%i", info.entref.classnum);
ImGui::Text("Entity num");
ImGui::Text("Entity number");
ImGui::SameLine();
if (ImGui::Button(num_str))
{
gui::copy_to_clipboard(num_str);
}
const auto entity_code = utils::string::va("game:getentbynum(%i)", info.num);
ImGui::Text("Entity class");
ImGui::SameLine();
if (ImGui::Button(classnum_str))
{
gui::copy_to_clipboard(classnum_str);
}
const auto entity_code = utils::string::va("game:getentbyref(%i, %i)",
info.entref.entnum, info.entref.classnum);
if (ImGui::Button(entity_code))
{
gui::copy_to_clipboard(entity_code);
@ -752,24 +731,24 @@ namespace entity_list
if (ImGui::Button("Set field"))
{
set_field_window = true;
selected_entity = info.num;
selected_entity = info.entref;
}
if (ImGui::Button("Teleport to"))
{
teleport_to(data, info.num);
teleport_to(data, info.entref);
data.force_update = true;
}
if (ImGui::Button("Teleport to you"))
{
teleport_to_reverse(data, info.num);
teleport_to_reverse(data, info.entref);
data.force_update = true;
}
if (info.num != 0 && ImGui::Button("Delete"))
if (info.entref.classnum == 0 && info.entref.entnum != 0 && ImGui::Button("Delete"))
{
delete_entity(data, info.num);
delete_entity(data, info.entref);
data.force_update = true;
}
@ -835,7 +814,7 @@ namespace entity_list
{
if (!game::CL_IsCgameInitialized())
{
selected_entity = 0;
selected_entity = {};
data.entity_info = {};
data.tasks = {};
}

View File

@ -18,6 +18,7 @@ namespace scripting
{
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
utils::concurrency::container<shared_table_t> shared_table;
namespace
{
@ -54,14 +55,24 @@ namespace scripting
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top);
}
void clear_shared_table()
{
shared_table.access([](shared_table_t& table)
{
table.clear();
});
}
void player_spawn_stub(const game::gentity_s* player)
{
player_spawn_hook.invoke<void>(player);
clear_shared_table();
lua::engine::start();
}
void g_shutdown_game_stub(const int free_scripts)
{
clear_shared_table();
lua::engine::stop();
g_shutdown_game_hook.invoke<void>(free_scripts);
}

View File

@ -1,7 +1,11 @@
#pragma once
#include <utils/concurrency.hpp>
namespace scripting
{
using shared_table_t = std::unordered_map<std::string, std::string>;
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
extern utils::concurrency::container<shared_table_t> shared_table;
}

View File

@ -27,7 +27,7 @@ namespace scripting
}
entity::entity(game::scr_entref_t entref)
: entity(game::FindEntityId(entref.entnum, entref.classnum))
: entity(game::Scr_GetEntityId(entref.entnum, entref.classnum))
{
}

View File

@ -32,7 +32,7 @@ namespace scripting::lua
void setup_entity_type(sol::state& state, event_handler& handler, scheduler& scheduler)
{
state["level"] = entity{*game::levelEntityId};
state["level"] = entity{*::game::levelEntityId};
state["player"] = call("getentbynum", {0}).as<entity>();
state["io"]["fileexists"] = utils::io::file_exists;
@ -301,6 +301,13 @@ namespace scripting::lua
return result;
};
entity_type["getentref"] = [](const entity& entity)
{
const auto entref = entity.get_entity_reference();
std::vector<unsigned int> returns = {entref.entnum, entref.classnum};
return sol::as_returns(returns);
};
struct game
{
};
@ -530,6 +537,38 @@ namespace scripting::lua
return table;
};
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
{
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
{
table[key] = value;
});
};
game_type["sharedget"] = [](const game&, const std::string& key)
{
std::string result;
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
{
result = table[key];
});
return result;
};
game_type["getentbyref"] = [](const game&, const sol::this_state s,
const unsigned int entnum, const unsigned int classnum)
{
const auto id = ::game::Scr_GetEntityId(entnum, classnum);
if (id)
{
return convert(s, scripting::entity{id});
}
else
{
return sol::lua_value{s, sol::lua_nil};
}
};
}
}

View File

@ -86,6 +86,7 @@ namespace game
WEAK symbol<const float* (const float* v)> Scr_AllocVector{0x5C3220};
WEAK symbol<void()> Scr_ClearOutParams{0x5C6E50};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x5C56C0};
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> Scr_GetEntityId{0x5C5610};
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x512190};
WEAK symbol<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x5C8240};
WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x5C57C0};

View File

@ -7,6 +7,7 @@
#include "../execution.hpp"
#include "../../../component/ui_scripting.hpp"
#include "../../../component/scripting.hpp"
#include "../../../component/command.hpp"
#include "component/game_console.hpp"
@ -1006,6 +1007,24 @@ namespace ui_scripting::lua
return sol::as_returns(returns);
};
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
{
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
{
table[key] = value;
});
};
game_type["sharedget"] = [](const game&, const std::string& key)
{
std::string result;
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
{
result = table[key];
});
return result;
};
auto userdata_type = state.new_usertype<userdata>("userdata_");
userdata_type["new"] = sol::property(