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 enum entity_type
{ {
type_any, entity,
actor, actor,
spawner, spawner,
weapon, weapon,
vehicle,
node, node,
vehicle_node,
count, count,
}; };
@ -49,7 +51,7 @@ namespace entity_list
struct entity_info_t struct entity_info_t
{ {
unsigned int id; unsigned int id;
unsigned int num; game::scr_entref_t entref;
std::unordered_map<std::string, std::string> fields; std::unordered_map<std::string, std::string> fields;
}; };
@ -278,7 +280,7 @@ namespace entity_list
}; };
utils::concurrency::container<data_t> data_{}; utils::concurrency::container<data_t> data_{};
unsigned int selected_entity{}; game::scr_entref_t selected_entity{};
bool set_field_window{}; bool set_field_window{};
bool selected_fields_window{}; bool selected_fields_window{};
int selected_type = game::SCRIPT_INTEGER; int selected_type = game::SCRIPT_INTEGER;
@ -288,88 +290,60 @@ namespace entity_list
std::string field_value_buffer{}; std::string field_value_buffer{};
std::string vector_input[3]{}; 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) std::optional<scripting::array> get_entity_array(const entity_type type, const entity_team team)
{ {
const auto value = scripting::call("getentarray"); if (type == entity_type::entity)
if (!value.is<scripting::array>())
{ {
return {}; return {scripting::call("getentarray").as<scripting::array>()};
}
const auto all = value.as<scripting::array>();
if (type == entity_type::type_any)
{
return {all};
} }
if (type == entity_type::actor) if (type == entity_type::actor)
{ {
scripting::array result{}; 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(); const auto entity = actors[i].as<scripting::entity>();
if (raw.type != game::SCRIPT_OBJECT) const auto team_string = entity.get("team").as<std::string>();
{
continue;
}
if (!verify_entity(raw.u.uintValue)) if (team == entity_team::team_any || team_string == team_names[team])
{
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]))
{ {
result.push(entity); result.push(entity);
} }
} }
return result; return {result};
} }
if (type == entity_type::spawner && team == entity_team::team_any) 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) 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) if (type == entity_type::weapon)
{ {
return scripting::call("getweaponarray").as<scripting::array>(); return {scripting::call("getweaponarray").as<scripting::array>()};
} }
if (type == entity_type::node) 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 {}; return {};
@ -404,29 +378,18 @@ namespace entity_list
data.entity_info.clear(); data.entity_info.clear();
const auto array = value.value(); const auto array = value.value();
const scripting::entity player{{0, 0}};
for (auto i = 0; i < array.size(); i++) 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>(); const auto entity = array[i].as<scripting::entity>();
entity_info_t info{}; entity_info_t info{};
info.id = raw.u.uintValue; info.id = entity.get_entity_id();
info.num = entity.get_entity_reference().entnum; info.entref = entity.get_entity_reference();
if (data.filters.filter_by_range) 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>(); const auto distance = scripting::call("distance", {player.get("origin"), entity.get("origin")}).as<float>();
if (distance > data.filters.range) 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 try
{ {
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>(); const scripting::entity entity{entref};
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>(); const scripting::entity player{{0, 0}};
player.call("setorigin", {entity.get("origin")}); player.call("setorigin", {entity.get("origin")});
} }
catch (...) 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 try
{ {
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>(); const scripting::entity entity{entref};
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>(); const scripting::entity player{{0, 0}};
entity.set("origin", player.get("origin")); entity.set("origin", player.get("origin"));
} }
catch (...) 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 try
{ {
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>(); const scripting::entity entity{entref};
entity.call("delete"); entity.call("delete");
} }
catch (...) 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) 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{}; scripting::script_value value{};
@ -546,7 +509,7 @@ namespace entity_list
try try
{ {
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>(); const scripting::entity entity{entref};
scripting::set_entity_field(entity, name, value); scripting::set_entity_field(entity, name, value);
data.force_update = true; 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) 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 try
{ {
const auto entity = scripting::call("getentbynum", {num}).as<scripting::entity>(); const scripting::entity entity{entref};
scripting::set_entity_field(entity, name, value); scripting::set_entity_field(entity, name, value);
data.force_update = true; data.force_update = true;
} }
@ -665,11 +628,13 @@ namespace entity_list
{ {
auto result = 0; 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("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("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("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) if (result)
{ {
@ -679,7 +644,8 @@ namespace entity_list
ImGui::TreePop(); 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; auto result = 0;
@ -728,20 +694,33 @@ namespace entity_list
for (const auto& info : data.entity_info) 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"); 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(); ImGui::SameLine();
if (ImGui::Button(num_str)) if (ImGui::Button(num_str))
{ {
gui::copy_to_clipboard(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)) if (ImGui::Button(entity_code))
{ {
gui::copy_to_clipboard(entity_code); gui::copy_to_clipboard(entity_code);
@ -752,24 +731,24 @@ namespace entity_list
if (ImGui::Button("Set field")) if (ImGui::Button("Set field"))
{ {
set_field_window = true; set_field_window = true;
selected_entity = info.num; selected_entity = info.entref;
} }
if (ImGui::Button("Teleport to")) if (ImGui::Button("Teleport to"))
{ {
teleport_to(data, info.num); teleport_to(data, info.entref);
data.force_update = true; data.force_update = true;
} }
if (ImGui::Button("Teleport to you")) if (ImGui::Button("Teleport to you"))
{ {
teleport_to_reverse(data, info.num); teleport_to_reverse(data, info.entref);
data.force_update = true; 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; data.force_update = true;
} }
@ -835,7 +814,7 @@ namespace entity_list
{ {
if (!game::CL_IsCgameInitialized()) if (!game::CL_IsCgameInitialized())
{ {
selected_entity = 0; selected_entity = {};
data.entity_info = {}; data.entity_info = {};
data.tasks = {}; 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<int, std::unordered_map<std::string, int>> fields_table;
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_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 namespace
{ {
@ -54,14 +55,24 @@ namespace scripting
vm_notify_hook.invoke<void>(notify_list_owner_id, string_value, top); 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) void player_spawn_stub(const game::gentity_s* player)
{ {
player_spawn_hook.invoke<void>(player); player_spawn_hook.invoke<void>(player);
clear_shared_table();
lua::engine::start(); lua::engine::start();
} }
void g_shutdown_game_stub(const int free_scripts) void g_shutdown_game_stub(const int free_scripts)
{ {
clear_shared_table();
lua::engine::stop(); lua::engine::stop();
g_shutdown_game_hook.invoke<void>(free_scripts); g_shutdown_game_hook.invoke<void>(free_scripts);
} }

View File

@ -1,7 +1,11 @@
#pragma once #pragma once
#include <utils/concurrency.hpp>
namespace scripting 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<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 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::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) 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["player"] = call("getentbynum", {0}).as<entity>();
state["io"]["fileexists"] = utils::io::file_exists; state["io"]["fileexists"] = utils::io::file_exists;
@ -301,6 +301,13 @@ namespace scripting::lua
return result; 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 struct game
{ {
}; };
@ -530,6 +537,38 @@ namespace scripting::lua
return table; 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<const float* (const float* v)> Scr_AllocVector{0x5C3220};
WEAK symbol<void()> Scr_ClearOutParams{0x5C6E50}; WEAK symbol<void()> Scr_ClearOutParams{0x5C6E50};
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x5C56C0}; 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<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<void(unsigned int id, scr_string_t stringValue, unsigned int paramcount)> Scr_NotifyId{0x5C8240};
WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x5C57C0}; WEAK symbol<unsigned int(unsigned int threadId)> Scr_GetSelf{0x5C57C0};

View File

@ -7,6 +7,7 @@
#include "../execution.hpp" #include "../execution.hpp"
#include "../../../component/ui_scripting.hpp" #include "../../../component/ui_scripting.hpp"
#include "../../../component/scripting.hpp"
#include "../../../component/command.hpp" #include "../../../component/command.hpp"
#include "component/game_console.hpp" #include "component/game_console.hpp"
@ -1006,6 +1007,24 @@ namespace ui_scripting::lua
return sol::as_returns(returns); 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_"); auto userdata_type = state.new_usertype<userdata>("userdata_");
userdata_type["new"] = sol::property( userdata_type["new"] = sol::property(