LiteLoaderBDS-1.16.40/ScriptEngine/API/BlockAPI.cpp
2022-09-21 19:47:03 +08:00

413 lines
14 KiB
C++

#include "APIHelp.h"
#include "BaseAPI.h"
#include "McAPI.h"
#include "BlockAPI.h"
#include "ContainerAPI.h"
#include "EntityAPI.h"
#include "BlockEntityAPI.h"
#include "NbtAPI.h"
#include "NativeAPI.h"
#include <MC/Level.hpp>
#include <MC/Block.hpp>
#include <MC/CompoundTag.hpp>
#include <MC/BlockSource.hpp>
#include <MC/BlockInstance.hpp>
#include <MC/BlockActor.hpp>
#include <exception>
//////////////////// Class Definition ////////////////////
ClassDefine<BlockClass> BlockClassBuilder =
defineClass<BlockClass>("LLSE_Block")
.constructor(nullptr)
.instanceFunction("asPointer", &BlockClass::asPointer)
.instanceProperty("name", &BlockClass::getName)
.instanceProperty("type", &BlockClass::getType)
.instanceProperty("id", &BlockClass::getId)
.instanceProperty("pos", &BlockClass::getPos)
.instanceProperty("tileData", &BlockClass::getTileData)
.instanceFunction("setNbt", &BlockClass::setNbt)
.instanceFunction("getNbt", &BlockClass::getNbt)
.instanceFunction("getBlockState", &BlockClass::getBlockState)
.instanceFunction("hasContainer", &BlockClass::hasContainer)
.instanceFunction("getContainer", &BlockClass::getContainer)
.instanceFunction("hasBlockEntity", &BlockClass::hasBlockEntity)
.instanceFunction("getBlockEntity", &BlockClass::getBlockEntity)
.instanceFunction("removeBlockEntity", &BlockClass::removeBlockEntity)
.instanceFunction("destroy", &BlockClass::destroyBlock)
// For Compatibility
.instanceFunction("setTag", &BlockClass::setNbt)
.instanceFunction("getTag", &BlockClass::getNbt)
.build();
//////////////////// Classes ////////////////////
BlockClass::BlockClass(Block const* p)
: ScriptClass(ScriptClass::ConstructFromCpp<BlockClass>{}), block(const_cast<Block*>(p)) {
preloadData({0, 0, 0}, -1);
}
BlockClass::BlockClass(Block const* p, BlockPos bp, int dim)
: ScriptClass(ScriptClass::ConstructFromCpp<BlockClass>{}), block(const_cast<Block*>(p)) {
preloadData(bp, dim);
}
// generating function
Local<Object> BlockClass::newBlock(Block const* p, BlockPos const* pos, int dim) {
auto newp = new BlockClass(p, *pos, dim);
return newp->getScriptObject();
}
Local<Object> BlockClass::newBlock(BlockPos const* pos, int dim) {
return BlockClass::newBlock(Level::getBlock(const_cast<BlockPos*>(pos), dim), pos, dim);
}
Local<Object> BlockClass::newBlock(const BlockPos& pos, int dim) {
return newBlock((BlockPos*)&pos, dim);
}
Local<Object> BlockClass::newBlock(Block const* p, BlockPos const* pos, BlockSource const* bs) {
auto newp = new BlockClass(p, *pos, bs->getDimensionId());
return newp->getScriptObject();
}
Local<Object> BlockClass::newBlock(IntVec4 pos) {
BlockPos bp = {pos.x, pos.y, pos.z};
return BlockClass::newBlock(Level::getBlock(bp, pos.dim), &bp, pos.dim);
}
Local<Object> BlockClass::newBlock(BlockInstance block) {
BlockPos bp = block.getPosition();
return BlockClass::newBlock(block.getBlock(), &bp, block.getDimensionId());
}
Block* BlockClass::extract(Local<Value> v) {
if (EngineScope::currentEngine()->isInstanceOf<BlockClass>(v))
return EngineScope::currentEngine()->getNativeInstance<BlockClass>(v)->get();
else
return nullptr;
}
// member function
void BlockClass::preloadData(BlockPos bp, int dim) {
name = block->getTypeName(); // TODO
type = block->getTypeName();
id = block->getId();
pos = {bp.x, bp.y, bp.z, dim};
}
Local<Value> BlockClass::getName() {
try {
// preloaded
return String::newString(name);
}
CATCH("Fail in getBlockName!");
}
Local<Value> BlockClass::getType() {
try {
// preloaded
return String::newString(type);
}
CATCH("Fail in getBlockType!");
}
Local<Value> BlockClass::getId() {
try {
// preloaded
return Number::newNumber(id);
}
CATCH("Fail in getBlockId!");
}
Local<Value> BlockClass::getPos() {
try {
// preloaded
return IntPos::newPos(pos);
}
CATCH("Fail in getBlockPos!");
}
Local<Value> BlockClass::getTileData() {
try {
// preloaded
return Number::newNumber(block->getTileData());
}
CATCH("Fail in getTileData!");
}
Local<Value> BlockClass::asPointer(const Arguments& args) {
try {
return NativePointer::newNativePointer(block);
}
CATCH("Fail in asPointer!");
}
Local<Value> BlockClass::destroyBlock(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1);
CHECK_ARG_TYPE(args[0], ValueKind::kBoolean);
try {
// same as `Level::getBlockInstance(pos.getBlockPos(), pos.dim).breakNaturally()` when drop
return Boolean::newBoolean(Global<Level>->destroyBlock(*Level::getBlockSource(pos.dim), pos.getBlockPos(), args[0].asBoolean().value()));
}
CATCH("Fail in destroyBlock!");
}
Local<Value> BlockClass::getNbt(const Arguments& args) {
try {
return NbtCompoundClass::pack(std::move(block->getNbt()));
}
CATCH("Fail in getNbt!");
}
Local<Value> BlockClass::setNbt(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1);
try {
auto nbt = NbtCompoundClass::extract(args[0]);
if (!nbt)
return Local<Value>(); // Null
// update Pre Data
Level::setBlock(pos.getBlockPos(), pos.dim, (CompoundTag*)nbt);
preloadData(pos.getBlockPos(), pos.getDimensionId());
return Boolean::newBoolean(true);
}
CATCH("Fail in setNbt!")
}
Local<Value> BlockClass::getBlockState(const Arguments& args) {
try {
auto list = block->getNbt();
try {
return Tag2Value((Tag*)list->get<Tag>("states"), true);
} catch (...) {
return Array::newArray();
}
} catch (const std::out_of_range& e) {
return Object::newObject();
}
CATCH("Fail in getBlockState!")
}
Local<Value> BlockClass::hasContainer(const Arguments& args) {
try {
auto bl = Level::getBlockInstance({pos.x, pos.y, pos.z}, pos.dim);
return Boolean::newBoolean(bl.hasContainer());
}
CATCH("Fail in hasContainer!");
}
Local<Value> BlockClass::getContainer(const Arguments& args) {
try {
Container* container = Level::getBlockInstance({pos.x, pos.y, pos.z}, pos.dim).getContainer();
return container ? ContainerClass::newContainer(container) : Local<Value>();
}
CATCH("Fail in getContainer!");
}
Local<Value> BlockClass::hasBlockEntity(const Arguments& args) {
try {
return Boolean::newBoolean(block->hasBlockEntity());
}
CATCH("Fail in hasBlockEntity!");
}
Local<Value> BlockClass::getBlockEntity(const Arguments& args) {
try {
BlockInstance bl = Level::getBlockInstance(pos.getBlockPos(), pos.dim);
BlockActor* be = bl.getBlockEntity();
return be ? BlockEntityClass::newBlockEntity(be, pos.dim) : Local<Value>();
}
CATCH("Fail in getBlockEntity!");
}
Local<Value> BlockClass::removeBlockEntity(const Arguments& args) {
try {
BlockSource* bs = Level::getBlockSource(pos.dim);
bs->removeBlockEntity(pos.getBlockPos()); //==========???
return Boolean::newBoolean(true);
}
CATCH("Fail in removeBlockEntity!");
}
// public API
Local<Value> McClass::getBlock(const Arguments& args) {
CHECK_ARGS_COUNT(args, 1);
try {
IntVec4 pos;
if (args.size() == 1) {
// IntPos
if (IsInstanceOf<IntPos>(args[0])) {
// IntPos
IntPos* posObj = IntPos::extractPos(args[0]);
if (posObj->dim < 0)
return Boolean::newBoolean(false);
else {
pos = *posObj;
}
} else if (IsInstanceOf<FloatPos>(args[0])) {
// FloatPos
FloatPos* posObj = FloatPos::extractPos(args[0]);
if (posObj->dim < 0)
return Boolean::newBoolean(false);
else {
pos = posObj->toIntVec4();
}
} else {
LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
} else if (args.size() == 4) {
// Number Pos
CHECK_ARG_TYPE(args[0], ValueKind::kNumber);
CHECK_ARG_TYPE(args[1], ValueKind::kNumber);
CHECK_ARG_TYPE(args[2], ValueKind::kNumber);
CHECK_ARG_TYPE(args[3], ValueKind::kNumber);
pos = {args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()};
} else {
LOG_WRONG_ARGS_COUNT();
return Local<Value>();
}
auto block = Level::getBlockEx(pos.getBlockPos(), pos.dim);
if (!block) {
// LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
BlockPos bp{pos.x, pos.y, pos.z};
return BlockClass::newBlock(block, &bp, pos.dim);
}
CATCH("Fail in GetBlock!")
}
Local<Value> McClass::setBlock(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2);
try {
IntVec4 pos;
Local<Value> block;
unsigned short tileData = 0;
if (args.size() == 2 || args.size() == 3) {
if (args.size() == 3) {
CHECK_ARG_TYPE(args[1], ValueKind::kString);
CHECK_ARG_TYPE(args[2], ValueKind::kNumber);
tileData = args[2].toInt();
}
if (IsInstanceOf<IntPos>(args[0])) {
// IntPos
IntPos* posObj = IntPos::extractPos(args[0]);
if (posObj->dim < 0)
return Boolean::newBoolean(false);
else {
pos = *posObj;
block = args[1];
}
} else if (IsInstanceOf<FloatPos>(args[0])) {
// FloatPos
FloatPos* posObj = FloatPos::extractPos(args[0]);
if (posObj->dim < 0)
return Boolean::newBoolean(false);
else {
pos = posObj->toIntVec4();
block = args[1];
}
} else {
LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
} else if (args.size() == 5 || args.size() == 6) {
// Number Pos
CHECK_ARG_TYPE(args[0], ValueKind::kNumber);
CHECK_ARG_TYPE(args[1], ValueKind::kNumber);
CHECK_ARG_TYPE(args[2], ValueKind::kNumber);
CHECK_ARG_TYPE(args[3], ValueKind::kNumber);
pos = {args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()};
block = args[4];
if (args.size() == 6) {
CHECK_ARG_TYPE(args[4], ValueKind::kString);
CHECK_ARG_TYPE(args[5], ValueKind::kNumber);
tileData = args[5].toInt();
}
} else {
LOG_WRONG_ARGS_COUNT();
return Local<Value>();
}
if (block.isString()) {
// block name
return Boolean::newBoolean(Level::setBlock(pos.getBlockPos(), pos.dim, block.toStr(), tileData));
} else if (IsInstanceOf<NbtCompoundClass>(block)) {
// Nbt
Tag* nbt = NbtCompoundClass::extract(block);
return Boolean::newBoolean(Level::setBlock(pos.getBlockPos(), pos.dim, (CompoundTag*)nbt));
} else {
// other block object
Block* bl = BlockClass::extract(block);
if (!bl) {
LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
return Boolean::newBoolean(Level::setBlock(pos.getBlockPos(), pos.dim, bl));
}
}
CATCH("Fail in SetBlock!")
}
Local<Value> McClass::spawnParticle(const Arguments& args) {
CHECK_ARGS_COUNT(args, 2)
try {
FloatVec4 pos;
Local<Value> type;
if (args.size() == 2) {
// IntPos
CHECK_ARG_TYPE(args[1], ValueKind::kString);
if (IsInstanceOf<IntPos>(args[0])) {
// IntPos
IntPos* posObj = IntPos::extractPos(args[0]);
if (posObj->dim < 0)
return Boolean::newBoolean(false);
else {
pos.x = posObj->x;
pos.y = posObj->y;
pos.z = posObj->z;
pos.dim = posObj->dim;
type = args[1];
}
} else if (IsInstanceOf<FloatPos>(args[0])) {
// FloatPos
FloatPos* posObj = FloatPos::extractPos(args[0]);
if (posObj->dim < 0)
return Boolean::newBoolean(false);
else {
pos = *posObj;
type = args[1];
}
} else {
LOG_WRONG_ARG_TYPE();
return Local<Value>();
}
} else if (args.size() == 5) {
// Number Pos
CHECK_ARG_TYPE(args[0], ValueKind::kNumber);
CHECK_ARG_TYPE(args[1], ValueKind::kNumber);
CHECK_ARG_TYPE(args[2], ValueKind::kNumber);
CHECK_ARG_TYPE(args[3], ValueKind::kNumber);
CHECK_ARG_TYPE(args[4], ValueKind::kString);
pos = {args[0].asNumber().toFloat(), args[1].asNumber().toFloat(), args[2].asNumber().toFloat(), args[3].toInt()};
type = args[4];
} else {
LOG_WRONG_ARGS_COUNT();
return Local<Value>();
}
Global<Level>->spawnParticleEffect(type.toStr(), pos.getVec3(), Global<Level>->getDimension(pos.dim));
return Boolean::newBoolean(true);
}
CATCH("Fail in SpawnParticle!")
}