mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-10 05:21:49 +00:00
Merge remote-tracking branch 'origin/main' into sdl-try-2
This commit is contained in:
commit
9dad26232a
264 changed files with 8609 additions and 2047 deletions
|
|
@ -127,7 +127,7 @@ void BlocksController::update(float delta, uint padding) {
|
|||
onBlocksTick(blocksTickClock.getPart(), blocksTickClock.getParts());
|
||||
}
|
||||
if (worldTickClock.update(delta)) {
|
||||
scripting::on_world_tick();
|
||||
scripting::on_world_tick(worldTickClock.getTickRate());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "objects/Player.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "voxels/Pathfinding.hpp"
|
||||
#include "scripting/scripting.hpp"
|
||||
#include "lighting/Lighting.hpp"
|
||||
#include "settings.hpp"
|
||||
|
|
@ -69,6 +70,9 @@ LevelController::LevelController(
|
|||
}
|
||||
|
||||
void LevelController::update(float delta, bool pause) {
|
||||
level->pathfinding->performAllAsync(
|
||||
settings.pathfinding.stepsPerAsyncAgent.get()
|
||||
);
|
||||
for (const auto& [_, player] : *level->players) {
|
||||
if (player->isSuspended()) {
|
||||
continue;
|
||||
|
|
@ -91,7 +95,6 @@ void LevelController::update(float delta, bool pause) {
|
|||
if (!pause) {
|
||||
// update all objects that needed
|
||||
blocks->update(delta, settings.chunks.padding.get());
|
||||
level->entities->updatePhysics(delta);
|
||||
level->entities->update(delta);
|
||||
for (const auto& [_, player] : *level->players) {
|
||||
if (player->isSuspended()) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "items/ItemStack.hpp"
|
||||
#include "lighting/Lighting.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "objects/Players.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
|
|
@ -270,7 +271,6 @@ void PlayerController::update(float delta, const Input* inputEvents) {
|
|||
} else {
|
||||
resetKeyboard();
|
||||
}
|
||||
updatePlayer(delta);
|
||||
}
|
||||
|
||||
void PlayerController::postUpdate(
|
||||
|
|
@ -309,20 +309,7 @@ void PlayerController::updateKeyboard(const Input& inputEvents) {
|
|||
}
|
||||
|
||||
void PlayerController::resetKeyboard() {
|
||||
input.zoom = false;
|
||||
input.moveForward = false;
|
||||
input.moveBack = false;
|
||||
input.moveLeft = false;
|
||||
input.moveRight = false;
|
||||
input.sprint = false;
|
||||
input.shift = false;
|
||||
input.cheat = false;
|
||||
input.jump = false;
|
||||
input.delta = {};
|
||||
}
|
||||
|
||||
void PlayerController::updatePlayer(float delta) {
|
||||
player.updateInput(input, delta);
|
||||
input = {};
|
||||
}
|
||||
|
||||
static int determine_rotation(
|
||||
|
|
@ -338,7 +325,8 @@ static int determine_rotation(
|
|||
if (norm.z > 0.0f) return BLOCK_DIR_NORTH;
|
||||
if (norm.z < 0.0f) return BLOCK_DIR_SOUTH;
|
||||
} else if (name == "pane" || name == "stairs") {
|
||||
int verticalBit = (name == "stairs" && (norm.y - camDir.y * 0.5f) < 0.0) ? 4 : 0;
|
||||
int verticalBit =
|
||||
(name == "stairs" && (norm.y - camDir.y * 0.5f) < 0.0) ? 4 : 0;
|
||||
if (abs(camDir.x) > abs(camDir.z)) {
|
||||
if (camDir.x > 0.0f) return BLOCK_DIR_EAST | verticalBit;
|
||||
if (camDir.x < 0.0f) return BLOCK_DIR_WEST | verticalBit;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ class PlayerController {
|
|||
|
||||
void updateKeyboard(const Input& inputEvents);
|
||||
void resetKeyboard();
|
||||
void updatePlayer(float delta);
|
||||
void updateEntityInteraction(entityid_t eid, bool lclick, bool rclick);
|
||||
void updateInteraction(const Input& inputEvents, float delta);
|
||||
|
||||
|
|
|
|||
104
src/logic/scripting/descriptors_manager.cpp
Normal file
104
src/logic/scripting/descriptors_manager.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#include "logic/scripting/descriptors_manager.hpp"
|
||||
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
static debug::Logger logger("descriptors-manager");
|
||||
|
||||
namespace scripting {
|
||||
|
||||
std::vector<std::optional<StreamDescriptor>> descriptors_manager::descriptors;
|
||||
|
||||
std::istream* descriptors_manager::get_input(int descriptor) {
|
||||
if (!is_readable(descriptor))
|
||||
return nullptr;
|
||||
|
||||
return descriptors[descriptor]->in.get();
|
||||
}
|
||||
|
||||
std::ostream* descriptors_manager::get_output(int descriptor) {
|
||||
if (!is_writeable(descriptor))
|
||||
return nullptr;
|
||||
|
||||
return descriptors[descriptor]->out.get();
|
||||
}
|
||||
|
||||
void descriptors_manager::flush(int descriptor) {
|
||||
if (is_writeable(descriptor)) {
|
||||
descriptors[descriptor]->out->flush();
|
||||
}
|
||||
}
|
||||
|
||||
bool descriptors_manager::has_descriptor(int descriptor) {
|
||||
return is_readable(descriptor) || is_writeable(descriptor);
|
||||
}
|
||||
|
||||
bool descriptors_manager::is_readable(int descriptor) {
|
||||
return descriptor >= 0 && descriptor < static_cast<int>(descriptors.size())
|
||||
&& descriptors[descriptor].has_value()
|
||||
&& descriptors[descriptor]->in != nullptr;
|
||||
}
|
||||
|
||||
bool descriptors_manager::is_writeable(int descriptor) {
|
||||
return descriptor >= 0 && descriptor < static_cast<int>(descriptors.size())
|
||||
&& descriptors[descriptor].has_value()
|
||||
&& descriptors[descriptor]->out != nullptr;
|
||||
}
|
||||
|
||||
void descriptors_manager::close(int descriptor) {
|
||||
if (descriptor >= 0 && descriptor < static_cast<int>(descriptors.size())) {
|
||||
if (descriptors[descriptor].has_value()) {
|
||||
auto& desc = descriptors[descriptor].value();
|
||||
|
||||
if (desc.out)
|
||||
desc.out->flush();
|
||||
|
||||
desc.in.reset();
|
||||
desc.out.reset();
|
||||
}
|
||||
|
||||
descriptors[descriptor].reset();
|
||||
|
||||
descriptors[descriptor] = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
int descriptors_manager::open_descriptor(const io::path& path, bool write, bool read) {
|
||||
std::unique_ptr<std::istream> in;
|
||||
std::unique_ptr<std::ostream> out;
|
||||
|
||||
try {
|
||||
if (read)
|
||||
in = io::read(path);
|
||||
|
||||
if (write)
|
||||
out = io::write(path);
|
||||
} catch (const std::exception& e) {
|
||||
logger.error() << "failed to open descriptor for " << path.string()
|
||||
<< ": " << e.what();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(descriptors.size()); ++i) {
|
||||
if (!descriptors[i].has_value()) {
|
||||
descriptors[i] = StreamDescriptor{ std::move(in), std::move(out) };
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
descriptors.emplace_back(StreamDescriptor{ std::move(in), std::move(out) });
|
||||
|
||||
return static_cast<int>(descriptors.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
void descriptors_manager::close_all_descriptors() {
|
||||
for (int i = 0; i < static_cast<int>(descriptors.size()); ++i) {
|
||||
if (descriptors[i].has_value()) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
|
||||
descriptors.clear();
|
||||
}
|
||||
}
|
||||
39
src/logic/scripting/descriptors_manager.hpp
Normal file
39
src/logic/scripting/descriptors_manager.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
#include "io/io.hpp"
|
||||
|
||||
namespace scripting {
|
||||
|
||||
struct StreamDescriptor {
|
||||
std::unique_ptr<std::istream> in;
|
||||
std::unique_ptr<std::ostream> out;
|
||||
};
|
||||
|
||||
class descriptors_manager {
|
||||
private:
|
||||
static std::vector<std::optional<StreamDescriptor>> descriptors;
|
||||
|
||||
public:
|
||||
static std::istream* get_input(int descriptor);
|
||||
static std::ostream* get_output(int descriptor);
|
||||
|
||||
static void flush(int descriptor);
|
||||
|
||||
static bool has_descriptor(int descriptor);
|
||||
|
||||
static bool is_readable(int descriptor);
|
||||
static bool is_writeable(int descriptor);
|
||||
|
||||
static void close(int descriptor);
|
||||
static int open_descriptor(const io::path& path, bool write, bool read);
|
||||
|
||||
static void close_all_descriptors();
|
||||
};
|
||||
}
|
||||
|
|
@ -38,9 +38,11 @@ extern const luaL_Reg mat4lib[];
|
|||
extern const luaL_Reg networklib[];
|
||||
extern const luaL_Reg packlib[];
|
||||
extern const luaL_Reg particleslib[]; // gfx.particles
|
||||
extern const luaL_Reg pathfindinglib[];
|
||||
extern const luaL_Reg playerlib[];
|
||||
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
||||
extern const luaL_Reg quatlib[];
|
||||
extern const luaL_Reg randomlib[];
|
||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||
extern const luaL_Reg timelib[];
|
||||
extern const luaL_Reg tomllib[];
|
||||
|
|
|
|||
|
|
@ -54,7 +54,12 @@ static int l_get_gravity_scale(lua::State* L) {
|
|||
|
||||
static int l_set_gravity_scale(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
entity->getRigidbody().hitbox.gravityScale = lua::tonumber(L, 2);
|
||||
auto& hitbox = entity->getRigidbody().hitbox;
|
||||
if (lua::istable(L, 2)) {
|
||||
hitbox.gravityScale = lua::tovec3(L, 2).y;
|
||||
} else {
|
||||
hitbox.gravityScale = lua::tonumber(L, 2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -62,6 +67,15 @@ static int l_set_gravity_scale(lua::State* L) {
|
|||
static int l_is_vdamping(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
return lua::pushboolean(
|
||||
L, entity->getRigidbody().hitbox.verticalDamping > 0.0
|
||||
);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_vdamping(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
return lua::pushnumber(
|
||||
L, entity->getRigidbody().hitbox.verticalDamping
|
||||
);
|
||||
}
|
||||
|
|
@ -70,7 +84,11 @@ static int l_is_vdamping(lua::State* L) {
|
|||
|
||||
static int l_set_vdamping(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
entity->getRigidbody().hitbox.verticalDamping = lua::toboolean(L, 2);
|
||||
if (lua::isboolean(L, 2)) {
|
||||
entity->getRigidbody().hitbox.verticalDamping = lua::toboolean(L, 2);
|
||||
} else {
|
||||
entity->getRigidbody().hitbox.verticalDamping = lua::tonumber(L, 2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -144,6 +162,7 @@ const luaL_Reg rigidbodylib[] = {
|
|||
{"get_linear_damping", lua::wrap<l_get_linear_damping>},
|
||||
{"set_linear_damping", lua::wrap<l_set_linear_damping>},
|
||||
{"is_vdamping", lua::wrap<l_is_vdamping>},
|
||||
{"get_vdamping", lua::wrap<l_get_vdamping>},
|
||||
{"set_vdamping", lua::wrap<l_set_vdamping>},
|
||||
{"is_grounded", lua::wrap<l_is_grounded>},
|
||||
{"is_crouching", lua::wrap<l_is_crouching>},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
#include "objects/rigging.hpp"
|
||||
#include "libentity.hpp"
|
||||
|
||||
#include "graphics/render/WorldRenderer.hpp"
|
||||
#include "graphics/render/NamedSkeletons.hpp"
|
||||
|
||||
namespace scripting {
|
||||
extern WorldRenderer* renderer;
|
||||
}
|
||||
|
||||
static int index_range_check(
|
||||
const rigging::Skeleton& skeleton, lua::Integer index
|
||||
) {
|
||||
|
|
@ -13,25 +20,33 @@ static int index_range_check(
|
|||
return static_cast<int>(index);
|
||||
}
|
||||
|
||||
static int l_get_model(lua::State* L) {
|
||||
static rigging::Skeleton* get_skeleton(lua::State* L) {
|
||||
if (lua::isstring(L, 1)) {
|
||||
return scripting::renderer->skeletons->getSkeleton(lua::tostring(L, 1));
|
||||
}
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
auto* rigConfig = skeleton.config;
|
||||
auto index = index_range_check(skeleton, lua::tointeger(L, 2));
|
||||
const auto& modelOverride = skeleton.modelOverrides[index];
|
||||
return &entity->getSkeleton();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int l_get_model(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
auto& rigConfig = *skeleton->config;
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
const auto& modelOverride = skeleton->modelOverrides[index];
|
||||
if (!modelOverride.model) {
|
||||
return lua::pushstring(L, modelOverride.name);
|
||||
}
|
||||
return lua::pushstring(L, rigConfig->getBones()[index]->model.name);
|
||||
return lua::pushstring(L, rigConfig.getBones()[index]->model.name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_model(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
auto index = index_range_check(skeleton, lua::tointeger(L, 2));
|
||||
auto& modelOverride = skeleton.modelOverrides[index];
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
auto& modelOverride = skeleton->modelOverrides[index];
|
||||
if (lua::isnoneornil(L, 3)) {
|
||||
modelOverride = {"", nullptr, true};
|
||||
} else {
|
||||
|
|
@ -42,28 +57,25 @@ static int l_set_model(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_get_matrix(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
auto index = index_range_check(skeleton, lua::tointeger(L, 2));
|
||||
return lua::pushmat4(L, skeleton.pose.matrices[index]);
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
return lua::pushmat4(L, skeleton->pose.matrices[index]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_matrix(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
auto index = index_range_check(skeleton, lua::tointeger(L, 2));
|
||||
skeleton.pose.matrices[index] = lua::tomat4(L, 3);
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
skeleton->pose.matrices[index] = lua::tomat4(L, 3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_texture(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
const auto& found = skeleton.textures.find(lua::require_string(L, 2));
|
||||
if (found != skeleton.textures.end()) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
const auto& found = skeleton->textures.find(lua::require_string(L, 2));
|
||||
if (found != skeleton->textures.end()) {
|
||||
return lua::pushstring(L, found->second);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,18 +83,16 @@ static int l_get_texture(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_set_texture(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
skeleton.textures[lua::require_string(L, 2)] =
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
skeleton->textures[lua::require_string(L, 2)] =
|
||||
lua::require_string(L, 3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_index(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
if (auto bone = skeleton.config->find(lua::require_string(L, 2))) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
if (auto bone = skeleton->config->find(lua::require_string(L, 2))) {
|
||||
return lua::pushinteger(L, bone->getIndex());
|
||||
}
|
||||
}
|
||||
|
|
@ -90,62 +100,60 @@ static int l_index(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_is_visible(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
if (!lua::isnoneornil(L, 2)) {
|
||||
auto index = index_range_check(skeleton, lua::tointeger(L, 2));
|
||||
return lua::pushboolean(L, skeleton.flags.at(index).visible);
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
return lua::pushboolean(L, skeleton->flags.at(index).visible);
|
||||
}
|
||||
return lua::pushboolean(L, skeleton.visible);
|
||||
return lua::pushboolean(L, skeleton->visible);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_visible(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
if (!lua::isnoneornil(L, 3)) {
|
||||
auto index = index_range_check(skeleton, lua::tointeger(L, 2));
|
||||
skeleton.flags.at(index).visible = lua::toboolean(L, 3);
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
skeleton->flags.at(index).visible = lua::toboolean(L, 3);
|
||||
} else {
|
||||
skeleton.visible = lua::toboolean(L, 2);
|
||||
skeleton->visible = lua::toboolean(L, 2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_color(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
return lua::pushvec(L, skeleton.tint);
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
return lua::pushvec(L, skeleton->tint);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_color(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
skeleton.tint = lua::tovec3(L, 2);
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
skeleton->tint = lua::tovec3(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_interpolated(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
return lua::pushboolean(L, skeleton.interpolation.isEnabled());
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
return lua::pushboolean(L, skeleton->interpolation.isEnabled());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_interpolated(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
auto& skeleton = entity->getSkeleton();
|
||||
skeleton.interpolation.setEnabled(lua::toboolean(L, 2));
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
skeleton->interpolation.setEnabled(lua::toboolean(L, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_exists(lua::State* L) {
|
||||
return lua::pushboolean(L, get_skeleton(L));
|
||||
}
|
||||
|
||||
const luaL_Reg skeletonlib[] = {
|
||||
{"get_model", lua::wrap<l_get_model>},
|
||||
{"set_model", lua::wrap<l_set_model>},
|
||||
|
|
@ -160,4 +168,6 @@ const luaL_Reg skeletonlib[] = {
|
|||
{"set_color", lua::wrap<l_set_color>},
|
||||
{"is_interpolated", lua::wrap<l_is_interpolated>},
|
||||
{"set_interpolated", lua::wrap<l_set_interpolated>},
|
||||
{NULL, NULL}};
|
||||
{"exists", lua::wrap<l_exists>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ static void load_texture(
|
|||
}
|
||||
|
||||
static int l_load_texture(lua::State* L) {
|
||||
if (lua::isstring(L, 3) && lua::require_lstring(L, 3) != "png") {
|
||||
throw std::runtime_error("unsupportd image format");
|
||||
}
|
||||
if (lua::istable(L, 1)) {
|
||||
lua::pushvalue(L, 1);
|
||||
size_t size = lua::objlen(L, 1);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "util/stringutil.hpp"
|
||||
|
||||
template<std::string(*encode_func)(const ubyte*, size_t)>
|
||||
static int l_encode(lua::State* L) {
|
||||
if (lua::istable(L, 1)) {
|
||||
lua::pushvalue(L, 1);
|
||||
|
|
@ -13,12 +14,12 @@ static int l_encode(lua::State* L) {
|
|||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
return lua::pushstring(L, util::base64_encode(
|
||||
return lua::pushstring(L, encode_func(
|
||||
reinterpret_cast<const ubyte*>(buffer.data()), buffer.size()
|
||||
));
|
||||
} else {
|
||||
auto string = lua::bytearray_as_string(L, 1);
|
||||
auto out = util::base64_encode(
|
||||
auto out = encode_func(
|
||||
reinterpret_cast<const ubyte*>(string.data()),
|
||||
string.size()
|
||||
);
|
||||
|
|
@ -28,8 +29,9 @@ static int l_encode(lua::State* L) {
|
|||
throw std::runtime_error("array or ByteArray expected");
|
||||
}
|
||||
|
||||
template<util::Buffer<ubyte>(*decode_func)(std::string_view)>
|
||||
static int l_decode(lua::State* L) {
|
||||
auto buffer = util::base64_decode(lua::require_lstring(L, 1));
|
||||
auto buffer = decode_func(lua::require_lstring(L, 1));
|
||||
if (lua::toboolean(L, 2)) {
|
||||
lua::createtable(L, buffer.size(), 0);
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
|
|
@ -43,7 +45,9 @@ static int l_decode(lua::State* L) {
|
|||
}
|
||||
|
||||
const luaL_Reg base64lib[] = {
|
||||
{"encode", lua::wrap<l_encode>},
|
||||
{"decode", lua::wrap<l_decode>},
|
||||
{"encode", lua::wrap<l_encode<util::base64_encode>>},
|
||||
{"decode", lua::wrap<l_decode<util::base64_decode>>},
|
||||
{"encode_urlsafe", lua::wrap<l_encode<util::base64_urlsafe_encode>>},
|
||||
{"decode_urlsafe", lua::wrap<l_decode<util::base64_urlsafe_decode>>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,21 +20,21 @@
|
|||
|
||||
using namespace scripting;
|
||||
|
||||
static inline const Block* require_block(lua::State* L) {
|
||||
static inline const Block* get_block_def(lua::State* L) {
|
||||
auto indices = content->getIndices();
|
||||
auto id = lua::tointeger(L, 1);
|
||||
return indices->blocks.get(id);
|
||||
}
|
||||
|
||||
static inline int l_get_def(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushstring(L, def->name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_material(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushstring(L, def->material);
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -59,14 +59,14 @@ static int l_index(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_is_extended(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushboolean(L, def->rt.extended);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_size(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushivec_stack(L, glm::ivec3(def->size));
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -101,12 +101,9 @@ static int l_set(lua::State* L) {
|
|||
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
||||
return 0;
|
||||
}
|
||||
int cx = floordiv<CHUNK_W>(x);
|
||||
int cz = floordiv<CHUNK_D>(z);
|
||||
if (!blocks_agent::get_chunk(*level->chunks, cx, cz)) {
|
||||
if (!blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state))) {
|
||||
return 0;
|
||||
}
|
||||
blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state));
|
||||
|
||||
auto chunksController = controller->getChunksController();
|
||||
if (chunksController == nullptr) {
|
||||
|
|
@ -346,14 +343,14 @@ static int l_is_replaceable_at(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_caption(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushstring(L, def->caption);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_textures(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
lua::createtable(L, 6, 0);
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
lua::pushstring(L, def->defaults.textureFaces[i]); // TODO: variant argument
|
||||
|
|
@ -364,8 +361,21 @@ static int l_get_textures(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int l_model_name(lua::State* L) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
// TODO: variant argument
|
||||
const auto& modelName = def->defaults.model.name;
|
||||
if (modelName.empty()) {
|
||||
return lua::pushlstring(L, def->name + ".model");
|
||||
}
|
||||
return lua::pushlstring(L, modelName);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_model(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
// TODO: variant argument
|
||||
return lua::pushlstring(L, BlockModelTypeMeta.getName(def->defaults.model.type));
|
||||
}
|
||||
|
|
@ -373,7 +383,7 @@ static int l_get_model(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_get_hitbox(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
size_t rotation = lua::tointeger(L, 2);
|
||||
if (def->rotatable) {
|
||||
rotation %= def->rotations.MAX_COUNT;
|
||||
|
|
@ -394,14 +404,14 @@ static int l_get_hitbox(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_get_rotation_profile(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushstring(L, def->rotations.name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_picking_item(lua::State* L) {
|
||||
if (auto def = require_block(L)) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
return lua::pushinteger(L, def->rt.pickingItem);
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -688,6 +698,49 @@ static int l_reload_script(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_has_tag(lua::State* L) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
auto tag = lua::require_string(L, 2);
|
||||
const auto& tags = def->rt.tags;
|
||||
return lua::pushboolean(L, tags.find(content->getTagIndex(tag)) != tags.end());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_tags(lua::State* L) {
|
||||
if (auto def = get_block_def(L)) {
|
||||
if (def->tags.empty()) {
|
||||
return 0;
|
||||
}
|
||||
lua::createtable(L, 0, def->tags.size());
|
||||
for (const auto& tag : def->tags) {
|
||||
lua::pushboolean(L, true);
|
||||
lua::setfield(L, tag);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_pull_register_events(lua::State* L) {
|
||||
auto events = blocks_agent::pull_register_events();
|
||||
if (events.empty())
|
||||
return 0;
|
||||
|
||||
lua::createtable(L, events.size() * 4, 0);
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
const auto& event = events[i];
|
||||
lua::pushinteger(L, static_cast<int>(event.type) | event.id << 16);
|
||||
lua::rawseti(L, i * 4 + 1);
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
lua::pushinteger(L, event.coord[j]);
|
||||
lua::rawseti(L, i * 4 + j + 2);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg blocklib[] = {
|
||||
{"index", lua::wrap<l_index>},
|
||||
{"name", lua::wrap<l_get_def>},
|
||||
|
|
@ -713,6 +766,7 @@ const luaL_Reg blocklib[] = {
|
|||
{"get_size", lua::wrap<l_get_size>},
|
||||
{"is_segment", lua::wrap<l_is_segment>},
|
||||
{"seek_origin", lua::wrap<l_seek_origin>},
|
||||
{"model_name", lua::wrap<l_model_name>},
|
||||
{"get_textures", lua::wrap<l_get_textures>},
|
||||
{"get_model", lua::wrap<l_get_model>},
|
||||
{"get_hitbox", lua::wrap<l_get_hitbox>},
|
||||
|
|
@ -726,5 +780,8 @@ const luaL_Reg blocklib[] = {
|
|||
{"get_field", lua::wrap<l_get_field>},
|
||||
{"set_field", lua::wrap<l_set_field>},
|
||||
{"reload_script", lua::wrap<l_reload_script>},
|
||||
{"has_tag", lua::wrap<l_has_tag>},
|
||||
{"__get_tags", lua::wrap<l_get_tags>},
|
||||
{"__pull_register_events", lua::wrap<l_pull_register_events>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -197,9 +197,16 @@ static int l_tpack(lua::State* L) {
|
|||
return pack(L, format, true);
|
||||
}
|
||||
|
||||
static int l_get_size(lua::State* L) {
|
||||
return lua::pushinteger(
|
||||
L, static_cast<int>(calc_size(lua::require_string(L, 1)))
|
||||
);
|
||||
}
|
||||
|
||||
const luaL_Reg byteutillib[] = {
|
||||
{"pack", lua::wrap<l_pack>},
|
||||
{"tpack", lua::wrap<l_tpack>},
|
||||
{"unpack", lua::wrap<l_unpack>},
|
||||
{"get_size", lua::wrap<l_get_size>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "engine/Engine.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/EntityDef.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "objects/Rigidbody.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "objects/rigging.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "frontend/hud.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "logic/LevelController.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "util/stringutil.hpp"
|
||||
#include "api_lua.hpp"
|
||||
#include "../lua_engine.hpp"
|
||||
#include "logic/scripting/descriptors_manager.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace scripting;
|
||||
|
|
@ -258,6 +259,149 @@ static int l_create_zip(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_open_descriptor(lua::State* L) {
|
||||
io::path path = lua::require_string(L, 1);
|
||||
auto mode = lua::require_lstring(L, 2);
|
||||
|
||||
bool write = mode.find('w') != std::string::npos;
|
||||
bool read = mode.find('r') != std::string::npos;
|
||||
|
||||
if (write && !is_writeable(path.entryPoint())) {
|
||||
throw std::runtime_error("access denied");
|
||||
}
|
||||
|
||||
if(!write && !read) {
|
||||
throw std::runtime_error("mode must contain read or write flag");
|
||||
}
|
||||
|
||||
if(write && read) {
|
||||
throw std::runtime_error("random access file i/o is not supported");
|
||||
}
|
||||
|
||||
bool wplusMode = write && mode.find('+') != std::string::npos;
|
||||
|
||||
std::vector<char> buffer;
|
||||
|
||||
if(wplusMode) {
|
||||
int temp_descriptor = scripting::descriptors_manager::open_descriptor(path, false, true);
|
||||
|
||||
if (temp_descriptor == -1) {
|
||||
throw std::runtime_error("failed to open descriptor for initial reading");
|
||||
}
|
||||
|
||||
auto* in_stream = scripting::descriptors_manager::get_input(temp_descriptor);
|
||||
|
||||
in_stream->seekg(0, std::ios::end);
|
||||
std::streamsize size = in_stream->tellg();
|
||||
in_stream->seekg(0, std::ios::beg);
|
||||
|
||||
buffer.resize(size);
|
||||
in_stream->read(buffer.data(), size);
|
||||
|
||||
scripting::descriptors_manager::close(temp_descriptor);
|
||||
}
|
||||
|
||||
int descriptor = scripting::descriptors_manager::open_descriptor(path, write, read);
|
||||
|
||||
if(descriptor == -1) {
|
||||
throw std::runtime_error("failed to open descriptor");
|
||||
}
|
||||
|
||||
if(wplusMode) {
|
||||
auto* out_stream = scripting::descriptors_manager::get_output(descriptor);
|
||||
out_stream->write(buffer.data(), buffer.size());
|
||||
out_stream->flush();
|
||||
}
|
||||
|
||||
return lua::pushinteger(L, descriptor);
|
||||
}
|
||||
|
||||
static int l_has_descriptor(lua::State* L) {
|
||||
return lua::pushboolean(L, scripting::descriptors_manager::has_descriptor(lua::tointeger(L, 1)));
|
||||
}
|
||||
|
||||
static int l_read_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!scripting::descriptors_manager::is_readable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not readable");
|
||||
}
|
||||
|
||||
int maxlen = lua::tointeger(L, 2);
|
||||
|
||||
auto* stream = scripting::descriptors_manager::get_input(descriptor);
|
||||
|
||||
util::Buffer<char> buffer(maxlen);
|
||||
|
||||
stream->read(buffer.data(), maxlen);
|
||||
|
||||
std::streamsize read_len = stream->gcount();
|
||||
|
||||
return lua::create_bytearray(L, buffer.data(), read_len);
|
||||
}
|
||||
|
||||
static int l_write_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!scripting::descriptors_manager::is_writeable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not writeable");
|
||||
}
|
||||
|
||||
auto data = lua::bytearray_as_string(L, 2);
|
||||
|
||||
auto* stream = scripting::descriptors_manager::get_output(descriptor);
|
||||
|
||||
stream->write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
|
||||
if (!stream->good()) {
|
||||
throw std::runtime_error("failed to write to stream");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_flush_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!scripting::descriptors_manager::is_writeable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not writeable");
|
||||
}
|
||||
|
||||
scripting::descriptors_manager::flush(descriptor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_close_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
scripting::descriptors_manager::close(descriptor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_close_all_descriptors(lua::State* L) {
|
||||
scripting::descriptors_manager::close_all_descriptors();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg filelib[] = {
|
||||
{"exists", lua::wrap<l_exists>},
|
||||
{"find", lua::wrap<l_find>},
|
||||
|
|
@ -283,5 +427,12 @@ const luaL_Reg filelib[] = {
|
|||
{"mount", lua::wrap<l_mount>},
|
||||
{"unmount", lua::wrap<l_unmount>},
|
||||
{"create_zip", lua::wrap<l_create_zip>},
|
||||
{"__open_descriptor", lua::wrap<l_open_descriptor>},
|
||||
{"__has_descriptor", lua::wrap<l_has_descriptor>},
|
||||
{"__read_descriptor", lua::wrap<l_read_descriptor>},
|
||||
{"__write_descriptor", lua::wrap<l_write_descriptor>},
|
||||
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
||||
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
||||
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -43,11 +43,19 @@ static int l_create_fragment(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_load_fragment(lua::State* L) {
|
||||
io::path path = lua::require_string(L, 1);
|
||||
if (!io::exists(path)) {
|
||||
throw std::runtime_error("file "+path.string()+" does not exist");
|
||||
dv::value map;
|
||||
if (!lua::isstring(L, 1)) {
|
||||
io::path path = lua::require_string(L, 1);
|
||||
if (!io::exists(path)) {
|
||||
throw std::runtime_error("file "+path.string()+" does not exist");
|
||||
}
|
||||
map = io::read_binary_json(path);
|
||||
} else {
|
||||
auto bytearray = lua::bytearray_as_string(L, 1);
|
||||
map = json::from_binary(
|
||||
reinterpret_cast<const ubyte*>(bytearray.data()), bytearray.size()
|
||||
);
|
||||
}
|
||||
auto map = io::read_binary_json(path);
|
||||
|
||||
auto fragment = std::make_shared<VoxelFragment>();
|
||||
fragment->deserialize(map);
|
||||
|
|
|
|||
|
|
@ -79,9 +79,12 @@ static int l_textbox_paste(lua::State* L) {
|
|||
|
||||
static int l_container_add(lua::State* L) {
|
||||
auto docnode = get_document_node(L);
|
||||
if (docnode.document == nullptr) {
|
||||
throw std::runtime_error("target document not found");
|
||||
}
|
||||
auto node = dynamic_cast<Container*>(docnode.node.get());
|
||||
if (node == nullptr) {
|
||||
return 0;
|
||||
throw std::runtime_error("target container not found");
|
||||
}
|
||||
auto xmlsrc = lua::require_string(L, 2);
|
||||
try {
|
||||
|
|
@ -99,7 +102,7 @@ static int l_container_add(lua::State* L) {
|
|||
UINode::getIndices(subnode, docnode.document->getMapWriteable());
|
||||
node->add(std::move(subnode));
|
||||
} catch (const std::exception& err) {
|
||||
throw std::runtime_error(err.what());
|
||||
throw std::runtime_error("container:add(...): " + std::string(err.what()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ static int l_mousecode(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_add_callback(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
std::string bindname = lua::require_string(L, 1);
|
||||
size_t pos = bindname.find(':');
|
||||
|
||||
|
|
@ -75,10 +77,14 @@ static int l_add_callback(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_get_mouse_pos(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
return lua::pushvec2(L, engine->getInput().getCursor().pos);
|
||||
}
|
||||
|
||||
static int l_get_bindings(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
const auto& bindings = engine->getInput().getBindings().getAll();
|
||||
lua::createtable(L, bindings.size(), 0);
|
||||
|
||||
|
|
@ -92,18 +98,24 @@ static int l_get_bindings(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_get_binding_text(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
auto bindname = lua::require_string(L, 1);
|
||||
const auto& bind = engine->getInput().getBindings().require(bindname);
|
||||
return lua::pushstring(L, bind.text());
|
||||
}
|
||||
|
||||
static int l_is_active(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
auto bindname = lua::require_string(L, 1);
|
||||
auto& bind = engine->getInput().getBindings().require(bindname);
|
||||
return lua::pushboolean(L, bind.active());
|
||||
}
|
||||
|
||||
static int l_is_pressed(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
std::string code = lua::require_string(L, 1);
|
||||
size_t sep = code.find(':');
|
||||
if (sep == std::string::npos) {
|
||||
|
|
@ -136,6 +148,8 @@ static void reset_pack_bindings(const io::path& packFolder) {
|
|||
}
|
||||
|
||||
static int l_reset_bindings(lua::State*) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
reset_pack_bindings("res:");
|
||||
for (const auto& pack : content_control->getContentPacks()) {
|
||||
reset_pack_bindings(pack.folder);
|
||||
|
|
@ -144,6 +158,8 @@ static int l_reset_bindings(lua::State*) {
|
|||
}
|
||||
|
||||
static int l_set_enabled(lua::State* L) {
|
||||
if (engine->isHeadless())
|
||||
return 0;
|
||||
std::string bindname = lua::require_string(L, 1);
|
||||
bool enabled = lua::toboolean(L, 2);
|
||||
engine->getInput().getBindings().require(bindname).enabled = enabled;
|
||||
|
|
@ -161,4 +177,5 @@ const luaL_Reg inputlib[] = {
|
|||
{"is_pressed", lua::wrap<l_is_pressed>},
|
||||
{"reset_bindings", lua::wrap<l_reset_bindings>},
|
||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||
{NULL, NULL}};
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@ namespace {
|
|||
|
||||
template <SlotFunc func>
|
||||
int wrap_slot(lua::State* L) {
|
||||
if (lua::isnoneornil(L, 1)) {
|
||||
throw std::runtime_error("inventory id is nil");
|
||||
}
|
||||
if (lua::isnoneornil(L, 2)) {
|
||||
throw std::runtime_error("slot index is nil");
|
||||
}
|
||||
auto invid = lua::tointeger(L, 1);
|
||||
auto slotid = lua::tointeger(L, 2);
|
||||
auto& inv = get_inventory(invid);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,13 @@ static int l_caption(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_description(lua::State* L) {
|
||||
if (auto def = get_item_def(L, 1)) {
|
||||
return lua::pushstring(L, def->description);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_placing_block(lua::State* L) {
|
||||
if (auto def = get_item_def(L, 1)) {
|
||||
return lua::pushinteger(L, def->rt.placingBlock);
|
||||
|
|
@ -101,6 +108,30 @@ static int l_reload_script(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_has_tag(lua::State* L) {
|
||||
if (auto def = get_item_def(L, 1)) {
|
||||
auto tag = lua::require_string(L, 2);
|
||||
const auto& tags = def->rt.tags;
|
||||
return lua::pushboolean(L, tags.find(content->getTagIndex(tag)) != tags.end());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_tags(lua::State* L) {
|
||||
if (auto def = get_item_def(L, 1)) {
|
||||
if (def->tags.empty()) {
|
||||
return 0;
|
||||
}
|
||||
lua::createtable(L, 0, def->tags.size());
|
||||
for (const auto& tag : def->tags) {
|
||||
lua::pushboolean(L, true);
|
||||
lua::setfield(L, tag);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg itemlib[] = {
|
||||
{"index", lua::wrap<l_index>},
|
||||
{"name", lua::wrap<l_name>},
|
||||
|
|
@ -108,10 +139,13 @@ const luaL_Reg itemlib[] = {
|
|||
{"defs_count", lua::wrap<l_defs_count>},
|
||||
{"icon", lua::wrap<l_get_icon>},
|
||||
{"caption", lua::wrap<l_caption>},
|
||||
{"description", lua::wrap<l_description>},
|
||||
{"placing_block", lua::wrap<l_placing_block>},
|
||||
{"model_name", lua::wrap<l_model_name>},
|
||||
{"emission", lua::wrap<l_emission>},
|
||||
{"uses", lua::wrap<l_uses>},
|
||||
{"reload_script", lua::wrap<l_reload_script>},
|
||||
{"has_tag", lua::wrap<l_has_tag>},
|
||||
{"__get_tags", lua::wrap<l_get_tags>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,76 +1,126 @@
|
|||
#include "api_lua.hpp"
|
||||
|
||||
#include "coders/json.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "network/Network.hpp"
|
||||
#include "coders/json.hpp"
|
||||
|
||||
#include <variant>
|
||||
#include <utility>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static int l_get(lua::State* L, network::Network& network) {
|
||||
std::string url(lua::require_lstring(L, 1));
|
||||
enum NetworkEventType {
|
||||
CLIENT_CONNECTED = 1,
|
||||
CONNECTED_TO_SERVER,
|
||||
DATAGRAM,
|
||||
RESPONSE,
|
||||
};
|
||||
|
||||
lua::pushvalue(L, 2);
|
||||
auto onResponse = lua::create_lambda_nothrow(L);
|
||||
struct ConnectionEventDto {
|
||||
u64id_t server;
|
||||
u64id_t client;
|
||||
};
|
||||
|
||||
network::OnReject onReject = nullptr;
|
||||
if (lua::gettop(L) >= 3) {
|
||||
lua::pushvalue(L, 3);
|
||||
auto callback = lua::create_lambda_nothrow(L);
|
||||
onReject = [callback](int code) {
|
||||
callback({code});
|
||||
};
|
||||
struct ResponseEventDto {
|
||||
int status;
|
||||
bool binary;
|
||||
int requestId;
|
||||
std::vector<char> bytes;
|
||||
};
|
||||
|
||||
enum NetworkDatagramSide {
|
||||
ON_SERVER = 1,
|
||||
ON_CLIENT
|
||||
};
|
||||
|
||||
struct NetworkDatagramEventDto {
|
||||
NetworkDatagramSide side;
|
||||
u64id_t server;
|
||||
u64id_t client;
|
||||
std::string addr;
|
||||
int port;
|
||||
std::vector<char> buffer;
|
||||
};
|
||||
|
||||
struct NetworkEvent {
|
||||
using Payload = std::variant<
|
||||
ConnectionEventDto,
|
||||
ResponseEventDto,
|
||||
NetworkDatagramEventDto
|
||||
>;
|
||||
NetworkEventType type;
|
||||
|
||||
Payload payload;
|
||||
|
||||
NetworkEvent(
|
||||
NetworkEventType type,
|
||||
Payload payload
|
||||
) : type(type), payload(std::move(payload)) {}
|
||||
|
||||
virtual ~NetworkEvent() = default;
|
||||
};
|
||||
|
||||
static std::vector<NetworkEvent> events_queue {};
|
||||
static std::mutex events_queue_mutex;
|
||||
|
||||
static void push_event(NetworkEvent&& event) {
|
||||
std::lock_guard lock(events_queue_mutex);
|
||||
events_queue.push_back(std::move(event));
|
||||
}
|
||||
|
||||
static std::vector<std::string> read_headers(lua::State* L, int index) {
|
||||
std::vector<std::string> headers;
|
||||
if (lua::istable(L, index)) {
|
||||
int len = lua::objlen(L, index);
|
||||
for (int i = 1; i <= len; i++) {
|
||||
lua::rawgeti(L, i, index);
|
||||
headers.push_back(lua::tostring(L, -1));
|
||||
lua::pop(L);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
network.get(url, [onResponse](std::vector<char> bytes) {
|
||||
engine->postRunnable([=]() {
|
||||
onResponse({std::string(bytes.data(), bytes.size())});
|
||||
});
|
||||
}, std::move(onReject));
|
||||
return 0;
|
||||
static int request_id = 1;
|
||||
|
||||
static int perform_get(lua::State* L, network::Network& network, bool binary) {
|
||||
std::string url(lua::require_lstring(L, 1));
|
||||
auto headers = read_headers(L, 2);
|
||||
|
||||
int currentRequestId = request_id++;
|
||||
|
||||
network.get(
|
||||
url,
|
||||
[currentRequestId, binary](std::vector<char> bytes) {
|
||||
push_event(NetworkEvent(
|
||||
RESPONSE,
|
||||
ResponseEventDto {
|
||||
200, binary, currentRequestId, std::move(bytes)}
|
||||
));
|
||||
},
|
||||
[currentRequestId, binary](int code, std::vector<char> bytes) {
|
||||
push_event(NetworkEvent(
|
||||
RESPONSE,
|
||||
ResponseEventDto {
|
||||
code, binary, currentRequestId, std::move(bytes)}
|
||||
));
|
||||
},
|
||||
std::move(headers)
|
||||
);
|
||||
return lua::pushinteger(L, currentRequestId);
|
||||
}
|
||||
|
||||
static int l_get(lua::State* L, network::Network& network) {
|
||||
return perform_get(L, network, false);
|
||||
}
|
||||
|
||||
static int l_get_binary(lua::State* L, network::Network& network) {
|
||||
std::string url(lua::require_lstring(L, 1));
|
||||
|
||||
lua::pushvalue(L, 2);
|
||||
auto onResponse = lua::create_lambda_nothrow(L);
|
||||
|
||||
network::OnReject onReject = nullptr;
|
||||
if (lua::gettop(L) >= 3) {
|
||||
lua::pushvalue(L, 3);
|
||||
auto callback = lua::create_lambda_nothrow(L);
|
||||
onReject = [callback](int code) {
|
||||
callback({code});
|
||||
};
|
||||
}
|
||||
|
||||
network.get(url, [onResponse](std::vector<char> bytes) {
|
||||
auto buffer = std::make_shared<util::Buffer<ubyte>>(
|
||||
reinterpret_cast<const ubyte*>(bytes.data()), bytes.size()
|
||||
);
|
||||
engine->postRunnable([=]() {
|
||||
onResponse({buffer});
|
||||
});
|
||||
}, std::move(onReject));
|
||||
return 0;
|
||||
return perform_get(L, network, true);
|
||||
}
|
||||
|
||||
static int l_post(lua::State* L, network::Network& network) {
|
||||
std::string url(lua::require_lstring(L, 1));
|
||||
auto data = lua::tovalue(L, 2);
|
||||
|
||||
lua::pushvalue(L, 3);
|
||||
auto onResponse = lua::create_lambda_nothrow(L);
|
||||
|
||||
network::OnReject onReject = nullptr;
|
||||
if (lua::gettop(L) >= 4) {
|
||||
lua::pushvalue(L, 4);
|
||||
auto callback = lua::create_lambda_nothrow(L);
|
||||
onReject = [callback](int code) {
|
||||
callback({code});
|
||||
};
|
||||
}
|
||||
|
||||
std::string string;
|
||||
if (data.isString()) {
|
||||
string = data.asString();
|
||||
|
|
@ -78,15 +128,29 @@ static int l_post(lua::State* L, network::Network& network) {
|
|||
string = json::stringify(data, false);
|
||||
}
|
||||
|
||||
engine->getNetwork().post(url, string, [onResponse](std::vector<char> bytes) {
|
||||
auto buffer = std::make_shared<util::Buffer<ubyte>>(
|
||||
reinterpret_cast<const ubyte*>(bytes.data()), bytes.size()
|
||||
);
|
||||
engine->postRunnable([=]() {
|
||||
onResponse({std::string(bytes.data(), bytes.size())});
|
||||
});
|
||||
}, std::move(onReject));
|
||||
return 0;
|
||||
auto headers = read_headers(L, 3);
|
||||
int currentRequestId = request_id++;
|
||||
|
||||
engine->getNetwork().post(
|
||||
url,
|
||||
string,
|
||||
[currentRequestId](std::vector<char> bytes) {
|
||||
push_event(NetworkEvent(
|
||||
RESPONSE,
|
||||
ResponseEventDto {
|
||||
200, false, currentRequestId, std::move(bytes)}
|
||||
));
|
||||
},
|
||||
[currentRequestId](int code, std::vector<char> bytes) {
|
||||
push_event(NetworkEvent(
|
||||
RESPONSE,
|
||||
ResponseEventDto {
|
||||
code, false, currentRequestId, std::move(bytes)}
|
||||
));
|
||||
},
|
||||
std::move(headers)
|
||||
);
|
||||
return lua::pushinteger(L, currentRequestId);
|
||||
}
|
||||
|
||||
static int l_close(lua::State* L, network::Network& network) {
|
||||
|
|
@ -134,17 +198,58 @@ static int l_send(lua::State* L, network::Network& network) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_udp_server_send_to(lua::State* L, network::Network& network) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
|
||||
if (auto server = network.getServer(id)) {
|
||||
if (server->getTransportType() != network::TransportType::UDP)
|
||||
throw std::runtime_error("the server must work on UDP transport");
|
||||
|
||||
const std::string& addr = lua::tostring(L, 2);
|
||||
const int& port = lua::tointeger(L, 3);
|
||||
|
||||
auto udpServer = dynamic_cast<network::UdpServer*>(server);
|
||||
|
||||
if (lua::istable(L, 4)) {
|
||||
lua::pushvalue(L, 4);
|
||||
size_t size = lua::objlen(L, 4);
|
||||
util::Buffer<char> buffer(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
lua::rawgeti(L, i + 1);
|
||||
buffer[i] = lua::tointeger(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
udpServer->sendTo(addr, port, buffer.data(), size);
|
||||
} else if (lua::isstring(L, 4)) {
|
||||
auto string = lua::tolstring(L, 4);
|
||||
udpServer->sendTo(addr, port, string.data(), string.length());
|
||||
} else {
|
||||
auto string = lua::bytearray_as_string(L, 4);
|
||||
udpServer->sendTo(addr, port, string.data(), string.length());
|
||||
lua::pop(L);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_recv(lua::State* L, network::Network& network) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
int length = lua::tointeger(L, 2);
|
||||
|
||||
auto connection = engine->getNetwork().getConnection(id);
|
||||
if (connection == nullptr) {
|
||||
|
||||
if (connection == nullptr || connection->getTransportType() != network::TransportType::TCP) {
|
||||
return 0;
|
||||
}
|
||||
length = glm::min(length, connection->available());
|
||||
|
||||
auto tcpConnection = dynamic_cast<network::TcpConnection*>(connection);
|
||||
|
||||
length = glm::min(length, tcpConnection->available());
|
||||
util::Buffer<char> buffer(length);
|
||||
|
||||
int size = connection->recv(buffer.data(), length);
|
||||
int size = tcpConnection->recv(buffer.data(), length);
|
||||
if (size == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -162,38 +267,78 @@ static int l_recv(lua::State* L, network::Network& network) {
|
|||
|
||||
static int l_available(lua::State* L, network::Network& network) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
|
||||
if (auto connection = network.getConnection(id)) {
|
||||
return lua::pushinteger(L, connection->available());
|
||||
return lua::pushinteger(L, dynamic_cast<network::TcpConnection*>(connection)->available());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum NetworkEventType {
|
||||
CLIENT_CONNECTED = 1,
|
||||
CONNECTED_TO_SERVER
|
||||
};
|
||||
|
||||
struct NetworkEvent {
|
||||
NetworkEventType type;
|
||||
u64id_t server;
|
||||
u64id_t client;
|
||||
};
|
||||
|
||||
static std::vector<NetworkEvent> events_queue {};
|
||||
|
||||
static int l_connect(lua::State* L, network::Network& network) {
|
||||
static int l_connect_tcp(lua::State* L, network::Network& network) {
|
||||
std::string address = lua::require_string(L, 1);
|
||||
int port = lua::tointeger(L, 2);
|
||||
u64id_t id = network.connect(address, port, [](u64id_t cid) {
|
||||
events_queue.push_back({CONNECTED_TO_SERVER, 0, cid});
|
||||
u64id_t id = network.connectTcp(address, port, [](u64id_t cid) {
|
||||
push_event(NetworkEvent(
|
||||
CONNECTED_TO_SERVER,
|
||||
ConnectionEventDto {0, cid}
|
||||
));
|
||||
});
|
||||
return lua::pushinteger(L, id);
|
||||
}
|
||||
|
||||
static int l_open(lua::State* L, network::Network& network) {
|
||||
static int l_open_tcp(lua::State* L, network::Network& network) {
|
||||
int port = lua::tointeger(L, 1);
|
||||
u64id_t id = network.openServer(port, [](u64id_t sid, u64id_t id) {
|
||||
events_queue.push_back({CLIENT_CONNECTED, sid, id});
|
||||
u64id_t id = network.openTcpServer(port, [](u64id_t sid, u64id_t id) {
|
||||
push_event(NetworkEvent(
|
||||
CLIENT_CONNECTED,
|
||||
ConnectionEventDto {sid, id}
|
||||
));
|
||||
});
|
||||
return lua::pushinteger(L, id);
|
||||
}
|
||||
|
||||
static int l_connect_udp(lua::State* L, network::Network& network) {
|
||||
std::string address = lua::require_string(L, 1);
|
||||
int port = lua::tointeger(L, 2);
|
||||
u64id_t id = network.connectUdp(address, port, [](u64id_t cid) {
|
||||
push_event(NetworkEvent(
|
||||
CONNECTED_TO_SERVER,
|
||||
ConnectionEventDto {0, cid}
|
||||
));
|
||||
}, [address, port](
|
||||
u64id_t cid,
|
||||
const char* buffer,
|
||||
size_t length
|
||||
) {
|
||||
push_event(NetworkEvent(
|
||||
DATAGRAM,
|
||||
NetworkDatagramEventDto {
|
||||
ON_CLIENT, 0, cid,
|
||||
address, port, std::vector<char>(buffer, buffer + length)
|
||||
}
|
||||
));
|
||||
});
|
||||
return lua::pushinteger(L, id);
|
||||
}
|
||||
|
||||
static int l_open_udp(lua::State* L, network::Network& network) {
|
||||
int port = lua::tointeger(L, 1);
|
||||
u64id_t id = network.openUdpServer(port, [](
|
||||
u64id_t sid,
|
||||
const std::string& addr,
|
||||
int port,
|
||||
const char* buffer,
|
||||
size_t length) {
|
||||
push_event(
|
||||
NetworkEvent(
|
||||
DATAGRAM,
|
||||
NetworkDatagramEventDto {
|
||||
ON_SERVER, sid, 0,
|
||||
addr, port, std::vector<char>(buffer, buffer + length)
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
return lua::pushinteger(L, id);
|
||||
}
|
||||
|
|
@ -204,7 +349,10 @@ static int l_is_alive(lua::State* L, network::Network& network) {
|
|||
return lua::pushboolean(
|
||||
L,
|
||||
connection->getState() != network::ConnectionState::CLOSED ||
|
||||
connection->available() > 0
|
||||
(
|
||||
connection->getTransportType() == network::TransportType::TCP &&
|
||||
dynamic_cast<network::TcpConnection*>(connection)->available() > 0
|
||||
)
|
||||
);
|
||||
}
|
||||
return lua::pushboolean(L, false);
|
||||
|
|
@ -255,22 +403,78 @@ static int l_get_total_download(lua::State* L, network::Network& network) {
|
|||
}
|
||||
|
||||
static int l_pull_events(lua::State* L, network::Network& network) {
|
||||
lua::createtable(L, events_queue.size(), 0);
|
||||
for (size_t i = 0; i < events_queue.size(); i++) {
|
||||
lua::createtable(L, 3, 0);
|
||||
std::vector<NetworkEvent> local_queue;
|
||||
{
|
||||
std::lock_guard lock(events_queue_mutex);
|
||||
local_queue.swap(events_queue);
|
||||
}
|
||||
|
||||
lua::pushinteger(L, events_queue[i].type);
|
||||
lua::rawseti(L, 1);
|
||||
lua::createtable(L, local_queue.size(), 0);
|
||||
|
||||
lua::pushinteger(L, events_queue[i].server);
|
||||
lua::rawseti(L, 2);
|
||||
for (size_t i = 0; i < local_queue.size(); i++) {
|
||||
lua::createtable(L, 7, 0);
|
||||
|
||||
lua::pushinteger(L, events_queue[i].client);
|
||||
lua::rawseti(L, 3);
|
||||
|
||||
const auto& event = local_queue[i];
|
||||
switch (event.type) {
|
||||
case CLIENT_CONNECTED:
|
||||
case CONNECTED_TO_SERVER: {
|
||||
const auto& dto = std::get<ConnectionEventDto>(event.payload);
|
||||
lua::pushinteger(L, event.type);
|
||||
lua::rawseti(L, 1);
|
||||
|
||||
lua::pushinteger(L, dto.server);
|
||||
lua::rawseti(L, 2);
|
||||
|
||||
lua::pushinteger(L, dto.client);
|
||||
lua::rawseti(L, 3);
|
||||
break;
|
||||
}
|
||||
case DATAGRAM: {
|
||||
const auto& dto = std::get<NetworkDatagramEventDto>(event.payload);
|
||||
lua::pushinteger(L, event.type);
|
||||
lua::rawseti(L, 1);
|
||||
|
||||
lua::pushinteger(L, dto.server);
|
||||
lua::rawseti(L, 2);
|
||||
|
||||
lua::pushinteger(L, dto.client);
|
||||
lua::rawseti(L, 3);
|
||||
|
||||
lua::pushstring(L, dto.addr);
|
||||
lua::rawseti(L, 4);
|
||||
|
||||
lua::pushinteger(L, dto.port);
|
||||
lua::rawseti(L, 5);
|
||||
|
||||
lua::pushinteger(L, dto.side);
|
||||
lua::rawseti(L, 6);
|
||||
|
||||
lua::create_bytearray(L, dto.buffer.data(), dto.buffer.size());
|
||||
lua::rawseti(L, 7);
|
||||
break;
|
||||
}
|
||||
case RESPONSE: {
|
||||
const auto& dto = std::get<ResponseEventDto>(event.payload);
|
||||
lua::pushinteger(L, event.type);
|
||||
lua::rawseti(L, 1);
|
||||
|
||||
lua::pushinteger(L, dto.status);
|
||||
lua::rawseti(L, 2);
|
||||
|
||||
lua::pushinteger(L, dto.requestId);
|
||||
lua::rawseti(L, 3);
|
||||
|
||||
if (dto.binary) {
|
||||
lua::create_bytearray(L, dto.bytes.data(), dto.bytes.size());
|
||||
} else {
|
||||
lua::pushlstring(L, std::string_view(dto.bytes.data(), dto.bytes.size()));
|
||||
}
|
||||
lua::rawseti(L, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lua::rawseti(L, i + 1);
|
||||
}
|
||||
events_queue.clear();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -292,15 +496,18 @@ int wrap(lua_State* L) {
|
|||
}
|
||||
|
||||
const luaL_Reg networklib[] = {
|
||||
{"get", wrap<l_get>},
|
||||
{"get_binary", wrap<l_get_binary>},
|
||||
{"post", wrap<l_post>},
|
||||
{"__get", wrap<l_get>},
|
||||
{"__get_binary", wrap<l_get_binary>},
|
||||
{"__post", wrap<l_post>},
|
||||
{"get_total_upload", wrap<l_get_total_upload>},
|
||||
{"get_total_download", wrap<l_get_total_download>},
|
||||
{"__pull_events", wrap<l_pull_events>},
|
||||
{"__open", wrap<l_open>},
|
||||
{"__open_tcp", wrap<l_open_tcp>},
|
||||
{"__open_udp", wrap<l_open_udp>},
|
||||
{"__closeserver", wrap<l_closeserver>},
|
||||
{"__connect", wrap<l_connect>},
|
||||
{"__udp_server_send_to", wrap<l_udp_server_send_to>},
|
||||
{"__connect_tcp", wrap<l_connect_tcp>},
|
||||
{"__connect_udp", wrap<l_connect_udp>},
|
||||
{"__close", wrap<l_close>},
|
||||
{"__send", wrap<l_send>},
|
||||
{"__recv", wrap<l_recv>},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#define VC_ENABLE_REFLECTION
|
||||
|
||||
#include "assets/AssetsLoader.hpp"
|
||||
#include "content/Content.hpp"
|
||||
|
|
@ -19,6 +15,12 @@
|
|||
#include "world/World.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static int l_pack_get_folder(lua::State* L) {
|
||||
|
|
@ -102,19 +104,28 @@ static int l_pack_get_info(
|
|||
auto& dpack = pack.dependencies[i];
|
||||
std::string prefix;
|
||||
switch (dpack.level) {
|
||||
case DependencyLevel::required:
|
||||
case DependencyLevel::REQUIRED:
|
||||
prefix = "!";
|
||||
break;
|
||||
case DependencyLevel::optional:
|
||||
case DependencyLevel::OPTIONAL:
|
||||
prefix = "?";
|
||||
break;
|
||||
case DependencyLevel::weak:
|
||||
case DependencyLevel::WEAK:
|
||||
prefix = "~";
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("");
|
||||
}
|
||||
lua::pushfstring(L, "%s%s", prefix.c_str(), dpack.id.c_str());
|
||||
auto opString = VersionOperatorMeta.getNameString(dpack.op);
|
||||
|
||||
lua::pushfstring(
|
||||
L,
|
||||
"%s%s@%s%s",
|
||||
prefix.c_str(),
|
||||
dpack.id.c_str(),
|
||||
(dpack.op == VersionOperator::EQUAL ? "" : opString).c_str(),
|
||||
dpack.version.c_str()
|
||||
);
|
||||
lua::rawseti(L, i + 1);
|
||||
}
|
||||
lua::setfield(L, "dependencies");
|
||||
|
|
|
|||
130
src/logic/scripting/lua/libs/libpathfinding.cpp
Normal file
130
src/logic/scripting/lua/libs/libpathfinding.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#include "api_lua.hpp"
|
||||
|
||||
#include "content/Content.hpp"
|
||||
#include "voxels/Pathfinding.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static voxels::Agent* get_agent(lua::State* L) {
|
||||
return level->pathfinding->getAgent(lua::tointeger(L, 1));
|
||||
}
|
||||
|
||||
static int l_create_agent(lua::State* L) {
|
||||
return lua::pushinteger(L, level->pathfinding->createAgent());
|
||||
}
|
||||
|
||||
static int l_remove_agent(lua::State* L) {
|
||||
int id = lua::tointeger(L, 1);
|
||||
return lua::pushboolean(L, level->pathfinding->removeAgent(id));
|
||||
}
|
||||
|
||||
static int l_set_enabled(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
agent->enabled = lua::toboolean(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_is_enabled(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
return lua::pushboolean(L, agent->enabled);
|
||||
}
|
||||
return lua::pushboolean(L, false);
|
||||
}
|
||||
|
||||
static int push_route(lua::State* L, const voxels::Route& route) {
|
||||
lua::createtable(L, route.nodes.size(), 1);
|
||||
for (int i = 0; i < route.nodes.size(); i++) {
|
||||
lua::pushvec3(L, route.nodes[i].pos);
|
||||
lua::rawseti(L, i + 1);
|
||||
}
|
||||
lua::pushinteger(L, route.totalVisited);
|
||||
lua::setfield(L, "total_visited");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_make_route(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
auto start = lua::tovec3(L, 2);
|
||||
auto target = lua::tovec3(L, 3);
|
||||
agent->state = {};
|
||||
agent->start = glm::floor(start);
|
||||
agent->target = target;
|
||||
auto route = level->pathfinding->perform(*agent);
|
||||
if (!route.found) {
|
||||
return 0;
|
||||
}
|
||||
return push_route(L, route);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_make_route_async(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
auto start = lua::tovec3(L, 2);
|
||||
auto target = lua::tovec3(L, 3);
|
||||
agent->state = {};
|
||||
agent->start = glm::floor(start);
|
||||
agent->target = target;
|
||||
level->pathfinding->perform(*agent, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_pull_route(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
auto& route = agent->route;
|
||||
if (!agent->state.finished) {
|
||||
return 0;
|
||||
}
|
||||
if (!route.found && !agent->mayBeIncomplete) {
|
||||
return lua::createtable(L, 0, 0);
|
||||
}
|
||||
return push_route(L, route);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_max_visited_blocks(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
agent->maxVisitedBlocks = lua::tointeger(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_jump_height(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
agent->jumpHeight = lua::tointeger(L, 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_avoid_tag(lua::State* L) {
|
||||
if (auto agent = get_agent(L)) {
|
||||
int index =
|
||||
content->getTagIndex(std::string(lua::require_lstring(L, 2)));
|
||||
if (index != -1) {
|
||||
int cost = lua::tonumber(L, 3);
|
||||
if (cost == 0) {
|
||||
cost = 10;
|
||||
}
|
||||
agent->avoidTags.insert({index, cost});
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg pathfindinglib[] = {
|
||||
{"create_agent", lua::wrap<l_create_agent>},
|
||||
{"remove_agent", lua::wrap<l_remove_agent>},
|
||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||
{"is_enabled", lua::wrap<l_is_enabled>},
|
||||
{"make_route", lua::wrap<l_make_route>},
|
||||
{"make_route_async", lua::wrap<l_make_route_async>},
|
||||
{"pull_route", lua::wrap<l_pull_route>},
|
||||
{"set_max_visited", lua::wrap<l_set_max_visited_blocks>},
|
||||
{"set_jump_height", lua::wrap<l_set_jump_height>},
|
||||
{"avoid_tag", lua::wrap<l_avoid_tag>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include "objects/Entities.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "objects/Players.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
|
|
|||
46
src/logic/scripting/lua/libs/librandom.cpp
Normal file
46
src/logic/scripting/lua/libs/librandom.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "api_lua.hpp"
|
||||
|
||||
#include "util/random.hpp"
|
||||
|
||||
static std::random_device random_device;
|
||||
|
||||
static int l_random(lua::State* L) {
|
||||
int argc = lua::gettop(L);
|
||||
|
||||
auto randomEngine = util::seeded_random_engine(random_device);
|
||||
if (argc == 0) {
|
||||
std::uniform_real_distribution<> dist(0.0, 1.0);
|
||||
return lua::pushnumber(L, dist(randomEngine));
|
||||
} else if (argc == 1) {
|
||||
std::uniform_int_distribution<integer_t> dist(1, lua::tointeger(L, 1));
|
||||
return lua::pushinteger(L, dist(randomEngine));
|
||||
} else {
|
||||
std::uniform_int_distribution<integer_t> dist(
|
||||
lua::tointeger(L, 1), lua::tointeger(L, 2)
|
||||
);
|
||||
return lua::pushinteger(L, dist(randomEngine));
|
||||
}
|
||||
}
|
||||
|
||||
static int l_bytes(lua::State* L) {
|
||||
size_t size = lua::tointeger(L, 1);
|
||||
|
||||
auto randomEngine = util::seeded_random_engine(random_device);
|
||||
static std::uniform_int_distribution<integer_t> dist(0, 0xFF);
|
||||
std::vector<ubyte> bytes (size);
|
||||
for (size_t i = 0; i < bytes.size(); i++) {
|
||||
bytes[i] = dist(randomEngine);
|
||||
}
|
||||
return lua::create_bytearray(L, bytes);
|
||||
}
|
||||
|
||||
static int l_uuid(lua::State* L) {
|
||||
return lua::pushlstring(L, util::generate_uuid());
|
||||
}
|
||||
|
||||
const luaL_Reg randomlib[] = {
|
||||
{"random", lua::wrap<l_random>},
|
||||
{"bytes", lua::wrap<l_bytes>},
|
||||
{"uuid", lua::wrap<l_uuid>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
#include "engine/Engine.hpp"
|
||||
#include "api_lua.hpp"
|
||||
#include <ctime>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define USE_MSVC_TIME_SAFE
|
||||
#endif
|
||||
|
||||
static int l_uptime(lua::State* L) {
|
||||
return lua::pushnumber(L, engine->getTime().getTime());
|
||||
}
|
||||
|
|
@ -11,8 +16,57 @@ static int l_delta(lua::State* L) {
|
|||
return lua::pushnumber(L, engine->getTime().getDelta());
|
||||
}
|
||||
|
||||
static int l_utc_time(lua::State* L) {
|
||||
return lua::pushnumber(L, std::time(nullptr));
|
||||
}
|
||||
|
||||
static int l_local_time(lua::State* L) {
|
||||
std::time_t t = std::time(nullptr);
|
||||
|
||||
std::tm gmt_tm{};
|
||||
std::tm local_tm{};
|
||||
|
||||
#if defined(USE_MSVC_TIME_SAFE)
|
||||
gmtime_s(&gmt_tm, &t);
|
||||
localtime_s(&local_tm, &t);
|
||||
#else
|
||||
gmtime_r(&t, &gmt_tm);
|
||||
localtime_r(&t, &local_tm);
|
||||
#endif
|
||||
|
||||
std::time_t utc_time = std::mktime(&gmt_tm);
|
||||
std::time_t local_time = std::mktime(&local_tm);
|
||||
std::time_t offset = local_time - utc_time;
|
||||
|
||||
return lua::pushnumber(L, t + offset);
|
||||
}
|
||||
|
||||
static int l_utc_offset(lua::State* L) {
|
||||
std::time_t t = std::time(nullptr);
|
||||
|
||||
std::tm gmt_tm{};
|
||||
std::tm local_tm{};
|
||||
|
||||
#if defined(USE_MSVC_TIME_SAFE)
|
||||
gmtime_s(&gmt_tm, &t);
|
||||
localtime_s(&local_tm, &t);
|
||||
#else
|
||||
gmtime_r(&t, &gmt_tm);
|
||||
localtime_r(&t, &local_tm);
|
||||
#endif
|
||||
|
||||
std::time_t utc_time = std::mktime(&gmt_tm);
|
||||
std::time_t local_time = std::mktime(&local_tm);
|
||||
std::time_t offset = local_time - utc_time;
|
||||
|
||||
return lua::pushnumber(L, offset);
|
||||
}
|
||||
|
||||
const luaL_Reg timelib[] = {
|
||||
{"uptime", lua::wrap<l_uptime>},
|
||||
{"delta", lua::wrap<l_delta>},
|
||||
{"utc_time", lua::wrap<l_utc_time>},
|
||||
{"utc_offset", lua::wrap<l_utc_offset>},
|
||||
{"local_time", lua::wrap<l_local_time>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ static int l_tostring(lua::State* L) {
|
|||
lua::pop(L);
|
||||
return lua::pushlstring(L, buffer.data(), size);
|
||||
} else {
|
||||
lua::bytearray_as_string(L, 1);
|
||||
return 1;
|
||||
return lua::pushlstring(L, lua::bytearray_as_string(L, 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,24 @@ inline T angle(glm::vec<2, T> vec) {
|
|||
return val;
|
||||
}
|
||||
|
||||
template <int n>
|
||||
static int l_mix(lua::State* L) {
|
||||
uint argc = lua::check_argc(L, 3, 4);
|
||||
auto a = lua::tovec<n, number_t>(L, 1);
|
||||
auto b = lua::tovec<n, number_t>(L, 2);
|
||||
auto t = lua::tonumber(L, 3);
|
||||
|
||||
if (argc == 3) {
|
||||
return lua::pushvec(L, a * (1.0 - t) + b * t);
|
||||
} else {
|
||||
return lua::setvec(L, 4, a * (1.0 - t) + b * t);
|
||||
}
|
||||
}
|
||||
|
||||
template <int n, template <class> class Op>
|
||||
static int l_binop(lua::State* L) {
|
||||
uint argc = lua::check_argc(L, 2, 3);
|
||||
auto a = lua::tovec<n>(L, 1);
|
||||
auto a = lua::tovec<n, number_t>(L, 1);
|
||||
|
||||
if (lua::isnumber(L, 2)) { // scalar second operand overload
|
||||
auto b = lua::tonumber(L, 2);
|
||||
|
|
@ -31,10 +45,10 @@ static int l_binop(lua::State* L) {
|
|||
}
|
||||
return 1;
|
||||
} else {
|
||||
return lua::setvec(L, 3, op(a, glm::vec<n, float>(b)));
|
||||
return lua::setvec(L, 3, op(a, glm::vec<n, number_t>(b)));
|
||||
}
|
||||
} else {
|
||||
auto b = lua::tovec<n>(L, 2);
|
||||
auto b = lua::tovec<n, number_t>(L, 2);
|
||||
Op op;
|
||||
if (argc == 2) {
|
||||
lua::createtable(L, n, 0);
|
||||
|
|
@ -49,10 +63,10 @@ static int l_binop(lua::State* L) {
|
|||
}
|
||||
}
|
||||
|
||||
template <int n, glm::vec<n, float> (*func)(const glm::vec<n, float>&)>
|
||||
template <int n, glm::vec<n, number_t> (*func)(const glm::vec<n, number_t>&)>
|
||||
static int l_unaryop(lua::State* L) {
|
||||
uint argc = lua::check_argc(L, 1, 2);
|
||||
auto vec = func(lua::tovec<n>(L, 1));
|
||||
auto vec = func(lua::tovec<n, number_t>(L, 1));
|
||||
switch (argc) {
|
||||
case 1:
|
||||
lua::createtable(L, n, 0);
|
||||
|
|
@ -67,17 +81,25 @@ static int l_unaryop(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
template <int n, float (*func)(const glm::vec<n, float>&)>
|
||||
template <int n, number_t (*func)(const glm::vec<n, number_t>&)>
|
||||
static int l_scalar_op(lua::State* L) {
|
||||
lua::check_argc(L, 1);
|
||||
auto vec = lua::tovec<n>(L, 1);
|
||||
auto vec = lua::tovec<n, number_t>(L, 1);
|
||||
return lua::pushnumber(L, func(vec));
|
||||
}
|
||||
|
||||
template <int n>
|
||||
static int l_distance(lua::State* L) {
|
||||
lua::check_argc(L, 2);
|
||||
auto a = lua::tovec<n, number_t>(L, 1);
|
||||
auto b = lua::tovec<n, number_t>(L, 2);
|
||||
return lua::pushnumber(L,glm::distance(a, b));
|
||||
}
|
||||
|
||||
template <int n>
|
||||
static int l_pow(lua::State* L) {
|
||||
uint argc = lua::check_argc(L, 2, 3);
|
||||
auto a = lua::tovec<n>(L, 1);
|
||||
auto a = lua::tovec<n, number_t>(L, 1);
|
||||
|
||||
if (lua::isnumber(L, 2)) {
|
||||
auto b = lua::tonumber(L, 2);
|
||||
|
|
@ -89,10 +111,10 @@ static int l_pow(lua::State* L) {
|
|||
}
|
||||
return 1;
|
||||
} else {
|
||||
return lua::setvec(L, 3, pow(a, glm::vec<n, float>(b)));
|
||||
return lua::setvec(L, 3, pow(a, glm::vec<n, number_t>(b)));
|
||||
}
|
||||
} else {
|
||||
auto b = lua::tovec<n>(L, 2);
|
||||
auto b = lua::tovec<n, number_t>(L, 2);
|
||||
if (argc == 2) {
|
||||
lua::createtable(L, n, 0);
|
||||
for (uint i = 0; i < n; i++) {
|
||||
|
|
@ -109,15 +131,15 @@ static int l_pow(lua::State* L) {
|
|||
template <int n>
|
||||
static int l_dot(lua::State* L) {
|
||||
lua::check_argc(L, 2);
|
||||
const auto& a = lua::tovec<n>(L, 1);
|
||||
const auto& b = lua::tovec<n>(L, 2);
|
||||
auto a = lua::tovec<n, number_t>(L, 1);
|
||||
auto b = lua::tovec<n, number_t>(L, 2);
|
||||
return lua::pushnumber(L, glm::dot(a, b));
|
||||
}
|
||||
|
||||
template <int n>
|
||||
static int l_inverse(lua::State* L) {
|
||||
uint argc = lua::check_argc(L, 1, 2);
|
||||
auto vec = lua::tovec<n>(L, 1);
|
||||
auto vec = lua::tovec<n, number_t>(L, 1);
|
||||
switch (argc) {
|
||||
case 1:
|
||||
lua::createtable(L, n, 0);
|
||||
|
|
@ -141,7 +163,7 @@ static int l_spherical_rand(lua::State* L) {
|
|||
return lua::setvec(
|
||||
L,
|
||||
2,
|
||||
glm::sphericalRand(static_cast<float>(lua::tonumber(L, 1)))
|
||||
glm::sphericalRand(lua::tonumber(L, 1))
|
||||
);
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -161,10 +183,22 @@ static int l_vec2_angle(lua::State* L) {
|
|||
}
|
||||
}
|
||||
|
||||
static int l_vec2_rotate(lua::State* L) {
|
||||
uint argc = lua::check_argc(L, 2, 3);
|
||||
auto vec = lua::tovec<2, number_t>(L, 1);
|
||||
auto angle = glm::radians(lua::tonumber(L, 2));
|
||||
|
||||
if (argc == 2) {
|
||||
return lua::pushvec(L, glm::rotate(vec, angle));
|
||||
} else {
|
||||
return lua::setvec(L, 3, glm::rotate(vec, angle));
|
||||
}
|
||||
}
|
||||
|
||||
template <int n>
|
||||
static int l_tostring(lua::State* L) {
|
||||
lua::check_argc(L, 1);
|
||||
auto vec = lua::tovec<n>(L, 1);
|
||||
auto vec = lua::tovec<n, number_t>(L, 1);
|
||||
std::stringstream ss;
|
||||
ss << "vec" << std::to_string(n) << "{";
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
|
@ -182,6 +216,7 @@ const luaL_Reg vec2lib[] = {
|
|||
{"sub", lua::wrap<l_binop<2, std::minus>>},
|
||||
{"mul", lua::wrap<l_binop<2, std::multiplies>>},
|
||||
{"div", lua::wrap<l_binop<2, std::divides>>},
|
||||
{"distance", lua::wrap<l_distance<2>>},
|
||||
{"normalize", lua::wrap<l_unaryop<2, glm::normalize>>},
|
||||
{"length", lua::wrap<l_scalar_op<2, glm::length>>},
|
||||
{"tostring", lua::wrap<l_tostring<2>>},
|
||||
|
|
@ -191,6 +226,8 @@ const luaL_Reg vec2lib[] = {
|
|||
{"pow", lua::wrap<l_pow<2>>},
|
||||
{"dot", lua::wrap<l_dot<2>>},
|
||||
{"angle", lua::wrap<l_vec2_angle>},
|
||||
{"mix", lua::wrap<l_mix<2>>},
|
||||
{"rotate", lua::wrap<l_vec2_rotate>},
|
||||
{NULL, NULL}};
|
||||
|
||||
const luaL_Reg vec3lib[] = {
|
||||
|
|
@ -198,6 +235,7 @@ const luaL_Reg vec3lib[] = {
|
|||
{"sub", lua::wrap<l_binop<3, std::minus>>},
|
||||
{"mul", lua::wrap<l_binop<3, std::multiplies>>},
|
||||
{"div", lua::wrap<l_binop<3, std::divides>>},
|
||||
{"distance", lua::wrap<l_distance<3>>},
|
||||
{"normalize", lua::wrap<l_unaryop<3, glm::normalize>>},
|
||||
{"length", lua::wrap<l_scalar_op<3, glm::length>>},
|
||||
{"tostring", lua::wrap<l_tostring<3>>},
|
||||
|
|
@ -207,6 +245,7 @@ const luaL_Reg vec3lib[] = {
|
|||
{"pow", lua::wrap<l_pow<3>>},
|
||||
{"dot", lua::wrap<l_dot<3>>},
|
||||
{"spherical_rand", lua::wrap<l_spherical_rand>},
|
||||
{"mix", lua::wrap<l_mix<3>>},
|
||||
{NULL, NULL}};
|
||||
|
||||
const luaL_Reg vec4lib[] = {
|
||||
|
|
@ -214,6 +253,7 @@ const luaL_Reg vec4lib[] = {
|
|||
{"sub", lua::wrap<l_binop<4, std::minus>>},
|
||||
{"mul", lua::wrap<l_binop<4, std::multiplies>>},
|
||||
{"div", lua::wrap<l_binop<4, std::divides>>},
|
||||
{"distance", lua::wrap<l_distance<4>>},
|
||||
{"normalize", lua::wrap<l_unaryop<4, glm::normalize>>},
|
||||
{"length", lua::wrap<l_scalar_op<4, glm::length>>},
|
||||
{"tostring", lua::wrap<l_tostring<4>>},
|
||||
|
|
@ -222,4 +262,5 @@ const luaL_Reg vec4lib[] = {
|
|||
{"inverse", lua::wrap<l_inverse<4>>},
|
||||
{"pow", lua::wrap<l_pow<4>>},
|
||||
{"dot", lua::wrap<l_dot<4>>},
|
||||
{"mix", lua::wrap<l_mix<4>>},
|
||||
{NULL, NULL}};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <random>
|
||||
|
||||
#include "lua_commons.hpp"
|
||||
|
||||
|
|
@ -114,4 +115,20 @@ namespace lua {
|
|||
std::shared_ptr<ImageData> mData;
|
||||
};
|
||||
static_assert(!std::is_abstract<LuaCanvas>());
|
||||
|
||||
class LuaRandom : public Userdata {
|
||||
public:
|
||||
std::mt19937 rng;
|
||||
|
||||
explicit LuaRandom(uint64_t seed) : rng(seed) {}
|
||||
virtual ~LuaRandom() override = default;
|
||||
|
||||
const std::string& getTypeName() const override {
|
||||
return TYPENAME;
|
||||
}
|
||||
|
||||
static int createMetatable(lua::State*);
|
||||
inline static std::string TYPENAME = "__vc_Random";
|
||||
};
|
||||
static_assert(!std::is_abstract<LuaRandom>());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ static void create_libs(State* L, StateType stateType) {
|
|||
openlib(L, "mat4", mat4lib);
|
||||
openlib(L, "pack", packlib);
|
||||
openlib(L, "quat", quatlib);
|
||||
openlib(L, "random", randomlib);
|
||||
openlib(L, "toml", tomllib);
|
||||
openlib(L, "utf8", utf8lib);
|
||||
openlib(L, "vec2", vec2lib);
|
||||
|
|
@ -72,6 +73,7 @@ static void create_libs(State* L, StateType stateType) {
|
|||
openlib(L, "input", inputlib);
|
||||
openlib(L, "inventory", inventorylib);
|
||||
openlib(L, "network", networklib);
|
||||
openlib(L, "pathfinding", pathfindinglib);
|
||||
openlib(L, "player", playerlib);
|
||||
openlib(L, "time", timelib);
|
||||
openlib(L, "world", worldlib);
|
||||
|
|
@ -167,5 +169,13 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) {
|
|||
auto file = "res:scripts/stdmin.lua";
|
||||
auto src = io::read_string(file);
|
||||
lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua"));
|
||||
|
||||
newusertype<LuaRandom>(L);
|
||||
if (getglobal(L, "random")) {
|
||||
if (getglobal(L, "__vc_Random")) {
|
||||
setfield(L, "Random");
|
||||
}
|
||||
pop(L);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ dv::value lua::tovalue(State* L, int idx) {
|
|||
auto bytes = std::make_shared<dv::objects::Bytes>(
|
||||
reinterpret_cast<const ubyte*>(data.data()), data.size()
|
||||
);
|
||||
pop(L);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ namespace lua {
|
|||
return true;
|
||||
}
|
||||
|
||||
template <int n>
|
||||
inline int pushvec(lua::State* L, const glm::vec<n, float>& vec) {
|
||||
template <int n, typename T = float>
|
||||
inline int pushvec(lua::State* L, const glm::vec<n, T>& vec) {
|
||||
createtable(L, n, 0);
|
||||
for (int i = 0; i < n; i++) {
|
||||
pushnumber(L, vec[i]);
|
||||
|
|
@ -161,8 +161,8 @@ namespace lua {
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
template <int n>
|
||||
inline int setvec(lua::State* L, int idx, glm::vec<n, float> vec) {
|
||||
template <int n, typename T = float>
|
||||
inline int setvec(lua::State* L, int idx, glm::vec<n, T> vec) {
|
||||
pushvalue(L, idx);
|
||||
for (int i = 0; i < n; i++) {
|
||||
pushnumber(L, vec[i]);
|
||||
|
|
@ -275,6 +275,15 @@ namespace lua {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T& require_userdata(lua::State* L, int idx) {
|
||||
if (void* rawptr = lua_touserdata(L, idx)) {
|
||||
return *static_cast<T*>(rawptr);
|
||||
}
|
||||
throw std::runtime_error("invalid 'self' value");
|
||||
}
|
||||
|
||||
template <class T, typename... Args>
|
||||
inline int newuserdata(lua::State* L, Args&&... args) {
|
||||
const auto& found = usertypeNames.find(typeid(T));
|
||||
|
|
@ -305,15 +314,15 @@ namespace lua {
|
|||
setglobal(L, name);
|
||||
}
|
||||
|
||||
template <int n>
|
||||
inline glm::vec<n, float> tovec(lua::State* L, int idx) {
|
||||
template <int n, typename T = float>
|
||||
inline glm::vec<n, T> tovec(lua::State* L, int idx) {
|
||||
pushvalue(L, idx);
|
||||
if (!istable(L, idx) || objlen(L, idx) < n) {
|
||||
throw std::runtime_error(
|
||||
"value must be an array of " + std::to_string(n) + " numbers"
|
||||
);
|
||||
}
|
||||
glm::vec<n, float> vec;
|
||||
glm::vec<n, T> vec;
|
||||
for (int i = 0; i < n; i++) {
|
||||
rawgeti(L, i + 1);
|
||||
vec[i] = tonumber(L, -1);
|
||||
|
|
@ -455,7 +464,7 @@ namespace lua {
|
|||
|
||||
inline bool getfield(lua::State* L, const std::string& name, int idx = -1) {
|
||||
lua_getfield(L, idx, name.c_str());
|
||||
if (isnil(L, idx)) {
|
||||
if (isnoneornil(L, -1)) {
|
||||
pop(L);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -759,9 +768,12 @@ namespace lua {
|
|||
}
|
||||
|
||||
inline std::string_view bytearray_as_string(lua::State* L, int idx) {
|
||||
lua::requireglobal(L, "Bytearray_as_string");
|
||||
lua::pushvalue(L, idx);
|
||||
lua::requireglobal(L, "Bytearray_as_string");
|
||||
lua::pushvalue(L, -2);
|
||||
lua::call(L, 1, 1);
|
||||
return lua::tolstring(L, -1);
|
||||
auto view = lua::tolstring(L, -1);
|
||||
lua::pop(L, 2);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
src/logic/scripting/lua/usertypes/lua_type_random.cpp
Normal file
56
src/logic/scripting/lua/usertypes/lua_type_random.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "../lua_custom_types.hpp"
|
||||
#include "../lua_util.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace lua;
|
||||
using namespace std::chrono;
|
||||
|
||||
static int l_random(lua::State* L) {
|
||||
std::uniform_int_distribution<> dist(0, std::numeric_limits<int>::max());
|
||||
|
||||
auto& rng = require_userdata<LuaRandom>(L, 1).rng;
|
||||
size_t n = touinteger(L, 2);
|
||||
createtable(L, n, 0);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
pushnumber(L, dist(rng) / (double)std::numeric_limits<int>::max());
|
||||
rawseti(L, i + 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_seed(lua::State* L) {
|
||||
require_userdata<LuaRandom>(L, 1).rng = std::mt19937(lua::touinteger(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_meta_meta_call(lua::State* L) {
|
||||
integer_t seed;
|
||||
if (lua::isnoneornil(L, 1)) {
|
||||
seed = system_clock::now().time_since_epoch().count();
|
||||
} else {
|
||||
seed = tointeger(L, 1);
|
||||
}
|
||||
return newuserdata<LuaRandom>(L, seed);
|
||||
}
|
||||
|
||||
int LuaRandom::createMetatable(lua::State* L) {
|
||||
createtable(L, 0, 3);
|
||||
|
||||
requireglobal(L, "__vc_create_random_methods");
|
||||
createtable(L, 0, 0);
|
||||
pushcfunction(L, wrap<l_random>);
|
||||
setfield(L, "random");
|
||||
pushcfunction(L, wrap<l_seed>);
|
||||
setfield(L, "seed");
|
||||
call(L, 1, 1);
|
||||
|
||||
setfield(L, "__index");
|
||||
|
||||
createtable(L, 0, 1);
|
||||
pushcfunction(L, wrap<l_meta_meta_call>);
|
||||
setfield(L, "__call");
|
||||
setmetatable(L);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -19,22 +19,20 @@
|
|||
#include "lua/lua_engine.hpp"
|
||||
#include "lua/lua_custom_types.hpp"
|
||||
#include "maths/Heightmap.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/EntityDef.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
#include "util/timeutil.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
#include "voxels/blocks_agent.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static debug::Logger logger("scripting");
|
||||
|
||||
static inline const std::string STDCOMP = "stdcomp";
|
||||
|
||||
std::ostream* scripting::output_stream = &std::cout;
|
||||
std::ostream* scripting::error_stream = &std::cerr;
|
||||
Engine* scripting::engine = nullptr;
|
||||
|
|
@ -116,11 +114,47 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Process> scripting::start_coroutine(
|
||||
class LuaProjectScript : public IClientProjectScript {
|
||||
public:
|
||||
LuaProjectScript(lua::State* L, scriptenv env) : L(L), env(std::move(env)) {}
|
||||
|
||||
void onScreenChange(const std::string& name, bool show) override {
|
||||
if (!lua::pushenv(L, *env)) {
|
||||
return;
|
||||
}
|
||||
if (!lua::getfield(L, "on_" + name + (show ? "_setup" : "_clear"))) {
|
||||
lua::pop(L);
|
||||
return;
|
||||
}
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
private:
|
||||
lua::State* L;
|
||||
scriptenv env;
|
||||
};
|
||||
|
||||
std::unique_ptr<IClientProjectScript> scripting::load_client_project_script(
|
||||
const io::path& script
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc_start_coroutine")) {
|
||||
auto source = io::read_string(script);
|
||||
auto env = create_environment(nullptr);
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::getglobal(L, "__vc_app")) {
|
||||
lua::setfield(L, "app");
|
||||
}
|
||||
lua::pop(L);
|
||||
|
||||
lua::loadbuffer(L, *env, source, script.name());
|
||||
lua::call(L, 0);
|
||||
return std::make_unique<LuaProjectScript>(L, std::move(env));
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> scripting::start_coroutine(const io::path& script) {
|
||||
auto L = lua::get_main_state();
|
||||
auto method = "__vc_start_coroutine";
|
||||
if (lua::getglobal(L, method)) {
|
||||
auto source = io::read_string(script);
|
||||
lua::loadbuffer(L, 0, source, script.name());
|
||||
if (lua::call(L, 1)) {
|
||||
|
|
@ -197,36 +231,6 @@ std::unique_ptr<Process> scripting::start_coroutine(
|
|||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] static scriptenv create_component_environment(
|
||||
const scriptenv& parent, int entityIdx, const std::string& name
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
int id = lua::create_environment(L, *parent);
|
||||
|
||||
lua::pushvalue(L, entityIdx);
|
||||
|
||||
lua::pushenv(L, id);
|
||||
|
||||
lua::pushvalue(L, -1);
|
||||
lua::setfield(L, "this");
|
||||
|
||||
lua::pushvalue(L, -2);
|
||||
lua::setfield(L, "entity");
|
||||
|
||||
lua::pop(L);
|
||||
if (lua::getfield(L, "components")) {
|
||||
lua::pushenv(L, id);
|
||||
lua::setfield(L, name);
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
|
||||
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
|
||||
lua::remove_environment(L, *id);
|
||||
delete id;
|
||||
});
|
||||
}
|
||||
|
||||
void scripting::process_post_runnables() {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__process_post_runnables")) {
|
||||
|
|
@ -293,12 +297,20 @@ void scripting::on_world_load(LevelController* controller) {
|
|||
}
|
||||
|
||||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(L, pack.id + ":.worldopen");
|
||||
lua::emit_event(L, pack.id + ":.worldopen", [](auto L) {
|
||||
return lua::pushboolean(
|
||||
L, !scripting::level->getWorld()->getInfo().isLoaded
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_world_tick() {
|
||||
void scripting::on_world_tick(int tps) {
|
||||
auto L = lua::get_main_state();
|
||||
if (lua::getglobal(L, "__vc_on_world_tick")) {
|
||||
lua::pushinteger(L, tps);
|
||||
lua::call_nothrow(L, 1, 0);
|
||||
}
|
||||
for (auto& pack : content_control->getAllContentPacks()) {
|
||||
lua::emit_event(L, pack.id + ":.worldtick");
|
||||
}
|
||||
|
|
@ -453,6 +465,7 @@ void scripting::on_chunk_present(const Chunk& chunk, bool loaded) {
|
|||
);
|
||||
}
|
||||
}
|
||||
blocks_agent::on_chunk_present(*content->getIndices(), chunk);
|
||||
}
|
||||
|
||||
void scripting::on_chunk_remove(const Chunk& chunk) {
|
||||
|
|
@ -467,6 +480,7 @@ void scripting::on_chunk_remove(const Chunk& chunk) {
|
|||
);
|
||||
}
|
||||
}
|
||||
blocks_agent::on_chunk_remove(*content->getIndices(), chunk);
|
||||
}
|
||||
|
||||
void scripting::on_inventory_open(const Player* player, const Inventory& inventory) {
|
||||
|
|
@ -554,244 +568,6 @@ bool scripting::on_item_break_block(
|
|||
);
|
||||
}
|
||||
|
||||
dv::value scripting::get_component_value(
|
||||
const scriptenv& env, const std::string& name
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::getfield(L, name)) {
|
||||
return lua::tovalue(L, -1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void scripting::on_entity_spawn(
|
||||
const EntityDef&,
|
||||
entityid_t eid,
|
||||
const std::vector<std::unique_ptr<UserComponent>>& components,
|
||||
const dv::value& args,
|
||||
const dv::value& saved
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::stackguard guard(L);
|
||||
lua::requireglobal(L, STDCOMP);
|
||||
if (lua::getfield(L, "new_Entity")) {
|
||||
lua::pushinteger(L, eid);
|
||||
lua::call(L, 1);
|
||||
}
|
||||
if (components.size() > 1) {
|
||||
for (size_t i = 0; i < components.size() - 1; i++) {
|
||||
lua::pushvalue(L, -1);
|
||||
}
|
||||
}
|
||||
for (auto& component : components) {
|
||||
auto compenv = create_component_environment(
|
||||
get_root_environment(), -1, component->name
|
||||
);
|
||||
lua::get_from(L, lua::CHUNKS_TABLE, component->name, true);
|
||||
lua::pushenv(L, *compenv);
|
||||
|
||||
if (args != nullptr) {
|
||||
std::string compfieldname = component->name;
|
||||
util::replaceAll(compfieldname, ":", "__");
|
||||
if (args.has(compfieldname)) {
|
||||
lua::pushvalue(L, args[compfieldname]);
|
||||
} else {
|
||||
lua::createtable(L, 0, 0);
|
||||
}
|
||||
} else {
|
||||
lua::createtable(L, 0, 0);
|
||||
}
|
||||
lua::setfield(L, "ARGS");
|
||||
|
||||
if (saved == nullptr) {
|
||||
lua::createtable(L, 0, 0);
|
||||
} else {
|
||||
if (saved.has(component->name)) {
|
||||
lua::pushvalue(L, saved[component->name]);
|
||||
} else {
|
||||
lua::createtable(L, 0, 0);
|
||||
}
|
||||
}
|
||||
lua::setfield(L, "SAVED_DATA");
|
||||
|
||||
lua::setfenv(L);
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
|
||||
lua::pushenv(L, *compenv);
|
||||
auto& funcsset = component->funcsset;
|
||||
funcsset.on_grounded = lua::hasfield(L, "on_grounded");
|
||||
funcsset.on_fall = lua::hasfield(L, "on_fall");
|
||||
funcsset.on_despawn = lua::hasfield(L, "on_despawn");
|
||||
funcsset.on_sensor_enter = lua::hasfield(L, "on_sensor_enter");
|
||||
funcsset.on_sensor_exit = lua::hasfield(L, "on_sensor_exit");
|
||||
funcsset.on_save = lua::hasfield(L, "on_save");
|
||||
funcsset.on_aim_on = lua::hasfield(L, "on_aim_on");
|
||||
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
|
||||
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
|
||||
funcsset.on_used = lua::hasfield(L, "on_used");
|
||||
lua::pop(L, 2);
|
||||
|
||||
component->env = compenv;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_entity_callback(
|
||||
const scriptenv& env,
|
||||
const std::string& name,
|
||||
std::function<int(lua::State*)> args
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::hasfield(L, "__disabled")) {
|
||||
lua::pop(L);
|
||||
return;
|
||||
}
|
||||
if (lua::getfield(L, name)) {
|
||||
if (args) {
|
||||
lua::call_nothrow(L, args(L), 0);
|
||||
} else {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
static void process_entity_callback(
|
||||
const Entity& entity,
|
||||
const std::string& name,
|
||||
bool EntityFuncsSet::*flag,
|
||||
std::function<int(lua::State*)> args
|
||||
) {
|
||||
const auto& script = entity.getScripting();
|
||||
for (auto& component : script.components) {
|
||||
if (component->funcsset.*flag) {
|
||||
process_entity_callback(component->env, name, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_entity_despawn(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
|
||||
);
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, "stdcomp", "remove_Entity", true);
|
||||
lua::pushinteger(L, entity.getUID());
|
||||
lua::call(L, 1, 0);
|
||||
}
|
||||
|
||||
void scripting::on_entity_grounded(const Entity& entity, float force) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_grounded",
|
||||
&EntityFuncsSet::on_grounded,
|
||||
[force](auto L) { return lua::pushnumber(L, force); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_fall(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_save(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_save", &EntityFuncsSet::on_save, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_sensor_enter(
|
||||
const Entity& entity, size_t index, entityid_t oid
|
||||
) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_sensor_enter",
|
||||
&EntityFuncsSet::on_sensor_enter,
|
||||
[index, oid](auto L) {
|
||||
lua::pushinteger(L, index);
|
||||
lua::pushinteger(L, oid);
|
||||
return 2;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_sensor_exit(
|
||||
const Entity& entity, size_t index, entityid_t oid
|
||||
) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_sensor_exit",
|
||||
&EntityFuncsSet::on_sensor_exit,
|
||||
[index, oid](auto L) {
|
||||
lua::pushinteger(L, index);
|
||||
lua::pushinteger(L, oid);
|
||||
return 2;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_aim_on(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_aim_on",
|
||||
&EntityFuncsSet::on_aim_on,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_aim_off(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_aim_off",
|
||||
&EntityFuncsSet::on_aim_off,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_attacked(
|
||||
const Entity& entity, Player* player, entityid_t attacker
|
||||
) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_attacked",
|
||||
&EntityFuncsSet::on_attacked,
|
||||
[player, attacker](auto L) {
|
||||
lua::pushinteger(L, attacker);
|
||||
lua::pushinteger(L, player->getId());
|
||||
return 2;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_used(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_used",
|
||||
&EntityFuncsSet::on_used,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entities_update(int tps, int parts, int part) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, STDCOMP, "update", true);
|
||||
lua::pushinteger(L, tps);
|
||||
lua::pushinteger(L, parts);
|
||||
lua::pushinteger(L, part);
|
||||
lua::call_nothrow(L, 3, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::on_entities_render(float delta) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, STDCOMP, "render", true);
|
||||
lua::pushnumber(L, delta);
|
||||
lua::call_nothrow(L, 1, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::on_ui_open(
|
||||
UiDocument* layout, std::vector<dv::value> args
|
||||
) {
|
||||
|
|
@ -875,6 +651,8 @@ void scripting::load_content_script(
|
|||
register_event(env, "on_replaced", prefix + ".replaced");
|
||||
funcsset.oninteract =
|
||||
register_event(env, "on_interact", prefix + ".interact");
|
||||
funcsset.onblocktick =
|
||||
register_event(env, "on_block_tick", prefix + ".blocktick");
|
||||
funcsset.onblockstick =
|
||||
register_event(env, "on_blocks_tick", prefix + ".blockstick");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,12 +65,21 @@ namespace scripting {
|
|||
|
||||
void process_post_runnables();
|
||||
|
||||
std::unique_ptr<Process> start_coroutine(
|
||||
class IClientProjectScript {
|
||||
public:
|
||||
virtual ~IClientProjectScript() {}
|
||||
|
||||
virtual void onScreenChange(const std::string& name, bool show) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IClientProjectScript> load_client_project_script(
|
||||
const io::path& script
|
||||
);
|
||||
|
||||
std::unique_ptr<Process> start_coroutine(const io::path& script);
|
||||
|
||||
void on_world_load(LevelController* controller);
|
||||
void on_world_tick();
|
||||
void on_world_tick(int tps);
|
||||
void on_world_save();
|
||||
void on_world_quit();
|
||||
void cleanup();
|
||||
|
|
@ -130,6 +139,7 @@ namespace scripting {
|
|||
void on_entity_fall(const Entity& entity);
|
||||
void on_entity_save(const Entity& entity);
|
||||
void on_entities_update(int tps, int parts, int part);
|
||||
void on_entities_physics_update(float delta);
|
||||
void on_entities_render(float delta);
|
||||
void on_sensor_enter(const Entity& entity, size_t index, entityid_t oid);
|
||||
void on_sensor_exit(const Entity& entity, size_t index, entityid_t oid);
|
||||
|
|
|
|||
296
src/logic/scripting/scripting_entities.cpp
Normal file
296
src/logic/scripting/scripting_entities.cpp
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
#include "scripting.hpp"
|
||||
|
||||
#include "lua/lua_engine.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/EntityDef.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static inline const std::string STDCOMP = "stdcomp";
|
||||
|
||||
[[nodiscard]] static scriptenv create_component_environment(
|
||||
const scriptenv& parent, int entityIdx, const std::string& name
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
int id = lua::create_environment(L, *parent);
|
||||
|
||||
lua::pushvalue(L, entityIdx);
|
||||
|
||||
lua::pushenv(L, id);
|
||||
|
||||
lua::pushvalue(L, -1);
|
||||
lua::setfield(L, "this");
|
||||
|
||||
lua::pushvalue(L, -2);
|
||||
lua::setfield(L, "entity");
|
||||
|
||||
lua::pop(L);
|
||||
if (lua::getfield(L, "components")) {
|
||||
lua::pushenv(L, id);
|
||||
lua::setfield(L, name);
|
||||
lua::pop(L);
|
||||
}
|
||||
lua::pop(L);
|
||||
|
||||
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
|
||||
lua::remove_environment(L, *id);
|
||||
delete id;
|
||||
});
|
||||
}
|
||||
|
||||
dv::value scripting::get_component_value(
|
||||
const scriptenv& env, const std::string& name
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::getfield(L, name)) {
|
||||
return lua::tovalue(L, -1);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void create_component(
|
||||
lua::State* L,
|
||||
int entityIdx,
|
||||
UserComponent& component,
|
||||
const dv::value& args,
|
||||
const dv::value& saved
|
||||
) {
|
||||
lua::pushvalue(L, entityIdx);
|
||||
auto compenv = create_component_environment(
|
||||
get_root_environment(), -1, component.name
|
||||
);
|
||||
lua::get_from(L, lua::CHUNKS_TABLE, component.name, true);
|
||||
lua::pushenv(L, *compenv);
|
||||
|
||||
if (args != nullptr) {
|
||||
std::string compfieldname = component.name;
|
||||
util::replaceAll(compfieldname, ":", "__");
|
||||
if (args.has(compfieldname)) {
|
||||
lua::pushvalue(L, args[compfieldname]);
|
||||
} else {
|
||||
lua::createtable(L, 0, 0);
|
||||
}
|
||||
} else if (component.params != nullptr) {
|
||||
lua::pushvalue(L, component.params);
|
||||
} else {
|
||||
lua::createtable(L, 0, 0);
|
||||
}
|
||||
lua::setfield(L, "ARGS");
|
||||
|
||||
if (saved == nullptr) {
|
||||
lua::createtable(L, 0, 0);
|
||||
} else {
|
||||
if (saved.has(component.name)) {
|
||||
lua::pushvalue(L, saved[component.name]);
|
||||
} else {
|
||||
lua::createtable(L, 0, 0);
|
||||
}
|
||||
}
|
||||
lua::setfield(L, "SAVED_DATA");
|
||||
|
||||
lua::setfenv(L);
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
|
||||
lua::pushenv(L, *compenv);
|
||||
auto& funcsset = component.funcsset;
|
||||
funcsset.on_grounded = lua::hasfield(L, "on_grounded");
|
||||
funcsset.on_fall = lua::hasfield(L, "on_fall");
|
||||
funcsset.on_despawn = lua::hasfield(L, "on_despawn");
|
||||
funcsset.on_sensor_enter = lua::hasfield(L, "on_sensor_enter");
|
||||
funcsset.on_sensor_exit = lua::hasfield(L, "on_sensor_exit");
|
||||
funcsset.on_save = lua::hasfield(L, "on_save");
|
||||
funcsset.on_aim_on = lua::hasfield(L, "on_aim_on");
|
||||
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
|
||||
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
|
||||
funcsset.on_used = lua::hasfield(L, "on_used");
|
||||
lua::pop(L, 2);
|
||||
|
||||
component.env = compenv;
|
||||
}
|
||||
|
||||
void scripting::on_entity_spawn(
|
||||
const EntityDef&,
|
||||
entityid_t eid,
|
||||
const std::vector<std::unique_ptr<UserComponent>>& components,
|
||||
const dv::value& args,
|
||||
const dv::value& saved
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::stackguard guard(L);
|
||||
lua::requireglobal(L, STDCOMP);
|
||||
if (lua::getfield(L, "new_Entity")) {
|
||||
lua::pushinteger(L, eid);
|
||||
lua::call(L, 1);
|
||||
}
|
||||
for (auto& component : components) {
|
||||
create_component(L, -1, *component, args, saved);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_entity_callback(
|
||||
const scriptenv& env,
|
||||
const std::string& name,
|
||||
std::function<int(lua::State*)> args
|
||||
) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::hasfield(L, "__disabled")) {
|
||||
lua::pop(L);
|
||||
return;
|
||||
}
|
||||
if (lua::getfield(L, name)) {
|
||||
if (args) {
|
||||
lua::call_nothrow(L, args(L), 0);
|
||||
} else {
|
||||
lua::call_nothrow(L, 0, 0);
|
||||
}
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
static void process_entity_callback(
|
||||
const Entity& entity,
|
||||
const std::string& name,
|
||||
bool EntityFuncsSet::*flag,
|
||||
std::function<int(lua::State*)> args
|
||||
) {
|
||||
const auto& script = entity.getScripting();
|
||||
for (auto& component : script.components) {
|
||||
if (component->funcsset.*flag) {
|
||||
process_entity_callback(component->env, name, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_entity_despawn(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
|
||||
);
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, "stdcomp", "remove_Entity", true);
|
||||
lua::pushinteger(L, entity.getUID());
|
||||
lua::call(L, 1, 0);
|
||||
}
|
||||
|
||||
void scripting::on_entity_grounded(const Entity& entity, float force) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_grounded",
|
||||
&EntityFuncsSet::on_grounded,
|
||||
[force](auto L) { return lua::pushnumber(L, force); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_fall(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_save(const Entity& entity) {
|
||||
process_entity_callback(
|
||||
entity, "on_save", &EntityFuncsSet::on_save, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_sensor_enter(
|
||||
const Entity& entity, size_t index, entityid_t oid
|
||||
) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_sensor_enter",
|
||||
&EntityFuncsSet::on_sensor_enter,
|
||||
[index, oid](auto L) {
|
||||
lua::pushinteger(L, index);
|
||||
lua::pushinteger(L, oid);
|
||||
return 2;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_sensor_exit(
|
||||
const Entity& entity, size_t index, entityid_t oid
|
||||
) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_sensor_exit",
|
||||
&EntityFuncsSet::on_sensor_exit,
|
||||
[index, oid](auto L) {
|
||||
lua::pushinteger(L, index);
|
||||
lua::pushinteger(L, oid);
|
||||
return 2;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_aim_on(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_aim_on",
|
||||
&EntityFuncsSet::on_aim_on,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_aim_off(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_aim_off",
|
||||
&EntityFuncsSet::on_aim_off,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_attacked(
|
||||
const Entity& entity, Player* player, entityid_t attacker
|
||||
) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_attacked",
|
||||
&EntityFuncsSet::on_attacked,
|
||||
[player, attacker](auto L) {
|
||||
lua::pushinteger(L, attacker);
|
||||
lua::pushinteger(L, player->getId());
|
||||
return 2;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entity_used(const Entity& entity, Player* player) {
|
||||
process_entity_callback(
|
||||
entity,
|
||||
"on_used",
|
||||
&EntityFuncsSet::on_used,
|
||||
[player](auto L) { return lua::pushinteger(L, player->getId()); }
|
||||
);
|
||||
}
|
||||
|
||||
void scripting::on_entities_update(int tps, int parts, int part) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, STDCOMP, "update", true);
|
||||
lua::pushinteger(L, tps);
|
||||
lua::pushinteger(L, parts);
|
||||
lua::pushinteger(L, part);
|
||||
lua::call_nothrow(L, 3, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::on_entities_physics_update(float delta) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, STDCOMP, "physics_update", true);
|
||||
lua::pushnumber(L, delta);
|
||||
lua::call_nothrow(L, 1, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::on_entities_render(float delta) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::get_from(L, STDCOMP, "render", true);
|
||||
lua::pushnumber(L, delta);
|
||||
lua::call_nothrow(L, 1, 0);
|
||||
lua::pop(L);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue