Feat: PlayerSelector + MojangsonParser

not fully implemented yet. this is a solid start. i have to commit before i break something.

MojangsonParser: Parses strings in Mojangson format (the JSON variant Minecraft uses for commands) and converts them into NBT Tag objects.

PlayerSelector: Implements Minecraft selectors (@p, @a, @r, @e, @s) to filter entities based on criteria.

Supported clauses:
x,y,z - origin coordinates
r,rm - max/min radius (spherical distance)
dx,dy,dz - axis-aligned bounding box
c - number of entities to return
l,lm - player experience level
m - game mode (0=S,1=C,2=A,3=Spec)
name - exact entity name
type - entity type (with ! inversion support)
team - team (placeholder, not implemented)
rx,rxm,ry,rym - rotation (pitch/yaw)
nbt - NBT filter (first level only)

some of them not working yet. or have to be fixed, or misses something to implement.
This commit is contained in:
Lord_Cambion 2026-06-01 21:47:14 +02:00
parent f5f0a74828
commit 80227645c1
6 changed files with 1100 additions and 361 deletions

View file

@ -5,7 +5,7 @@
#include "net.minecraft.world.level.h"
#include "BasicTypeContainers.h"
#include "KillCommand.h"
#include "EntityTypeMap.h"
#include "PlayerSelector.h"
static void killEntity(shared_ptr<Entity> entity)
@ -18,9 +18,8 @@ static void killEntity(shared_ptr<Entity> entity)
living->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
return;
}
entity->remove();
return;
return;
}
entity->remove();
}
@ -39,14 +38,11 @@ void KillCommand::execute(shared_ptr<CommandSender> source, byteArray commandDat
{
shared_ptr<Player> senderPlayer = dynamic_pointer_cast<Player>(source);
if (senderPlayer == nullptr)
{
return;
}
Level *level = senderPlayer->level;
if (level == nullptr)
return;
//nothing is the same of @s
// /kill with no arguments kills the sender
if (commandData.length == 0 || commandData.data == nullptr)
{
senderPlayer->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
@ -57,139 +53,35 @@ void KillCommand::execute(shared_ptr<CommandSender> source, byteArray commandDat
DataInputStream dis(&bais);
wstring targetName = dis.readUTF();
wstring targetLower = targetName;
transform(targetLower.begin(), targetLower.end(), targetLower.begin(), towlower);
//@s
if (targetLower == L"@s")
// use PlayerSelector
if (PlayerSelector::hasArguments(targetName))
{
senderPlayer->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
return;
}
//@a
if (targetLower == L"@a")
{
vector<shared_ptr<Player>> toKill;
for (auto& p : level->players)
if (p != nullptr && !p->removed)
toKill.push_back(p);
for (auto& p : toKill)
p->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
return;
}
//@p
if (targetLower == L"@p")
{
shared_ptr<Player> nearest = level->getNearestPlayer(
dynamic_pointer_cast<Entity>(senderPlayer), 9999.0);
if (nearest != nullptr)
auto entities = PlayerSelector::matchEntities(source, targetName);
if (entities.empty())
{
nearest->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
source->sendMessage(L"Killed " + nearest->getName() + L".");
}
else
{
source->sendMessage(L"No player found.");
}
return;
}
//@r
if (targetLower == L"@r")
{
if (level->players.empty())
{
source->sendMessage(L"No player found.");
source->sendMessage(L"No entity was found.");
return;
}
int idx = rand() % (int)level->players.size();
auto& p = level->players[idx];
if (p != nullptr && !p->removed)
{
p->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
source->sendMessage(L"Killed " + p->getName() + L".");
}
return;
}
// @e
if (targetLower.size() >= 2 && targetLower.substr(0, 2) == L"@e")
{
eINSTANCEOF filterType = eTYPE_NOTSET;
bool invertFilter = false;
bool filterIsPlayer = false;
if (targetLower.size() > 3 && targetLower[2] == L'[')
{
wstring inner = targetLower.substr(3, targetLower.size() - 4);
wstring key = L"type=";
size_t pos = inner.find(key);
if (pos == wstring::npos)
{
source->sendMessage(L"Invalid selector syntax. Usage: @e[type=<entity_type>]");
return;
}
wstring typeStr = inner.substr(pos + key.size());
if (!typeStr.empty() && typeStr[0] == L'!')
{
invertFilter = true;
typeStr = typeStr.substr(1);
}
if (typeStr == L"player")
{
filterIsPlayer = true;
}
else
{
filterType = EntityTypeMap::getTypeFromName(typeStr);
if (filterType == eTYPE_NOTSET)
{
source->sendMessage(L"Unknown entity type: " + typeStr);
return;
}
}
}
vector<shared_ptr<Entity>> toKill;
for (auto& entity : level->entities)
{
if (entity == nullptr || entity->removed) continue;
bool isPlayer = entity->instanceof(eTYPE_PLAYER);
bool matchesFilter;
if (filterType == eTYPE_NOTSET && !filterIsPlayer)
matchesFilter = true;
else if (filterIsPlayer)
matchesFilter = isPlayer;
else
matchesFilter = entity->instanceof(filterType);
if (invertFilter) matchesFilter = !matchesFilter;
if (matchesFilter)
toKill.push_back(entity);
}
for (auto& entity : toKill)
for (auto& entity : entities)
if (!entity->removed)
killEntity(entity);
source->sendMessage(L"Killed " + to_wstring(toKill.size()) + L" entities.");
source->sendMessage(L"Killed " + to_wstring(entities.size()) + L" entities.");
return;
}
// By player name
Level* level = senderPlayer->level;
if (level == nullptr)
{
return;
}
// by player name
shared_ptr<Player> targetPlayer = level->getPlayerByName(targetName);
if (targetPlayer != nullptr)
{
targetPlayer->hurt(DamageSource::outOfWorld, Float::MAX_VALUE);
source->sendMessage(L"Killed " + targetName + L".");
return;
}

View file

@ -0,0 +1,463 @@
#include "stdafx.h"
#include "MojangsonParser.h"
#include "ByteTag.h"
#include "ShortTag.h"
#include "IntTag.h"
#include "LongTag.h"
#include "FloatTag.h"
#include "DoubleTag.h"
#include "StringTag.h"
#include "ByteArrayTag.h"
#include "IntArrayTag.h"
#include "ListTag.h"
#include "CompoundTag.h"
#include <cwchar>
#include <cwctype>
#include <algorithm>
#include <sstream>
#include <stack>
using std::wstring;
using std::vector;
using std::stack;
bool MojangsonPrimitiveParser::matchesDouble(const wstring& s) {
if (s.empty()) return false;
if (towupper(s.back()) != L'D') return false;
wstring num = s.substr(0, s.size() - 1);
if (num.empty()) return false;
int start = (num[0] == L'-' || num[0] == L'+') ? 1 : 0;
bool hasDot = false, hasDigit = false;
for (int i = start; i < (int)num.size(); i++) {
if (num[i] == L'.') { if (hasDot) return false; hasDot = true; }
else if (iswdigit(num[i])) hasDigit = true;
else return false;
}
return hasDigit;
}
bool MojangsonPrimitiveParser::matchesFloat(const wstring& s) {
if (s.empty()) return false;
if (towupper(s.back()) != L'F') return false;
wstring num = s.substr(0, s.size() - 1);
if (num.empty()) return false;
int start = (num[0] == L'-' || num[0] == L'+') ? 1 : 0;
bool hasDot = false, hasDigit = false;
for (int i = start; i < (int)num.size(); i++) {
if (num[i] == L'.') { if (hasDot) return false; hasDot = true; }
else if (iswdigit(num[i])) hasDigit = true;
else return false;
}
return hasDigit;
}
bool MojangsonPrimitiveParser::matchesByte(const wstring& s) {
if (s.empty()) return false;
if (towupper(s.back()) != L'B') return false;
wstring num = s.substr(0, s.size() - 1);
if (num.empty()) return false;
int start = (num[0] == L'-' || num[0] == L'+') ? 1 : 0;
if (start == (int)num.size()) return false;
for (int i = start; i < (int)num.size(); i++)
if (!iswdigit(num[i])) return false;
return true;
}
bool MojangsonPrimitiveParser::matchesLong(const wstring& s) {
if (s.empty()) return false;
if (towupper(s.back()) != L'L') return false;
wstring num = s.substr(0, s.size() - 1);
if (num.empty()) return false;
int start = (num[0] == L'-' || num[0] == L'+') ? 1 : 0;
if (start == (int)num.size()) return false;
for (int i = start; i < (int)num.size(); i++)
if (!iswdigit(num[i])) return false;
return true;
}
bool MojangsonPrimitiveParser::matchesShort(const wstring& s) {
if (s.empty()) return false;
if (towupper(s.back()) != L'S') return false;
wstring num = s.substr(0, s.size() - 1);
if (num.empty()) return false;
int start = (num[0] == L'-' || num[0] == L'+') ? 1 : 0;
if (start == (int)num.size()) return false;
for (int i = start; i < (int)num.size(); i++)
if (!iswdigit(num[i])) return false;
return true;
}
bool MojangsonPrimitiveParser::matchesInt(const wstring& s) {
if (s.empty()) return false;
int start = (s[0] == L'-' || s[0] == L'+') ? 1 : 0;
if (start == (int)s.size()) return false;
for (int i = start; i < (int)s.size(); i++)
if (!iswdigit(s[i])) return false;
return true;
}
bool MojangsonPrimitiveParser::matchesDecimal(const wstring& s) {
if (s.empty()) return false;
int start = (s[0] == L'-' || s[0] == L'+') ? 1 : 0;
bool hasDot = false, hasDigit = false;
for (int i = start; i < (int)s.size(); i++) {
if (s[i] == L'.') { if (hasDot) return false; hasDot = true; }
else if (iswdigit(s[i])) hasDigit = true;
else return false;
}
return hasDigit;
}
bool MojangsonParser::matchesSimpleIntArray(const wstring& s) {
if (s.empty() || s.front() != L'[' || s.back() != L']') return false;
for (int i = 1; i < (int)s.size() - 1; i++) {
wchar_t c = s[i];
if (!iswdigit(c) && c != L'-' && c != L'+' && c != L',' && c != L'|' && !iswspace(c))
return false;
}
return true;
}
Tag* MojangsonPrimitiveParser::parse() {
wstring val = b;
wstring result;
try {
if (matchesDouble(val))
return new DoubleTag(L"", stod(val.substr(0, val.size() - 1)));
if (matchesFloat(val))
return new FloatTag(L"", stof(val.substr(0, val.size() - 1)));
if (matchesByte(val))
return new ByteTag(L"", (byte)(stoi(val.substr(0, val.size() - 1)) & 0xFF));
if (matchesLong(val))
return new LongTag(L"", (int64_t)stoll(val.substr(0, val.size() - 1)));
if (matchesShort(val))
return new ShortTag(L"", (short)stoi(val.substr(0, val.size() - 1)));
if (matchesInt(val))
return new IntTag(L"", stoi(val));
if (matchesDecimal(val))
return new DoubleTag(L"", stod(val));
wstring lower = val;
transform(lower.begin(), lower.end(), lower.begin(), towlower);
if (lower == L"true") return new ByteTag(L"", 1);
if (lower == L"false") return new ByteTag(L"", 0);
}
catch (...) {
for (int j = 0; j < (int)val.size(); j++) {
if (j < (int)val.size() - 1 && val[j] == L'\\' && val[j+1] == L'\\') {
result += L'\\'; j++;
} else {
result += val[j];
}
}
return new StringTag(L"", result);
}
if (!val.empty() && val.front() == L'[' && val.back() == L']'
&& MojangsonParser::matchesSimpleIntArray(val))
{
wstring inner = val.substr(1, val.size() - 2);
vector<int> ints;
std::wistringstream ss(inner);
wstring tok;
bool failed = false;
while (getline(ss, tok, L',')) {
while (!tok.empty() && iswspace(tok.front())) tok.erase(tok.begin());
while (!tok.empty() && iswspace(tok.back())) tok.pop_back();
if (!tok.empty()) {
try { ints.push_back(stoi(tok)); }
catch (...) { failed = true; break; }
}
}
if (!failed) {
intArray ia((int)ints.size());
for (int i = 0; i < (int)ints.size(); i++) ia.data[i] = ints[i];
return new IntArrayTag(L"", ia);
}
}
if (val.size() >= 2 && val.front() == L'"' && val.back() == L'"')
val = val.substr(1, val.size() - 2);
result.clear();
for (int j = 0; j < (int)val.size(); j++) {
if (j < (int)val.size() - 1 && val[j] == L'\\' && val[j+1] == L'\\') {
result += L'\\'; j++;
} else {
result += val[j];
}
}
wstring final_result;
for (int j = 0; j < (int)result.size(); j++) {
if (j < (int)result.size() - 1 && result[j] == L'\\' && result[j+1] == L'"') {
final_result += L'"'; j++;
} else {
final_result += result[j];
}
}
return new StringTag(L"", final_result);
}
Tag* MojangsonListParser::parse() {
ListTag<Tag>* list = new ListTag<Tag>(L"");
for (auto* parser : b)
list->add(parser->parse());
return list;
}
Tag* MojangsonCompoundParser::parse() {
CompoundTag* compound = new CompoundTag(L"");
for (auto* parser : b) {
Tag* tag = parser->parse();
tag->setName(parser->a);
compound->put(parser->a, tag);
delete tag;
}
return compound;
}
bool MojangsonParser::isEscaped(const wstring& s, int i) {
return i > 0 && s[i-1] == L'\\' && !isEscaped(s, i-1);
}
int MojangsonParser::locateSeparator(const wstring& s, wchar_t c0) {
bool inQuote = true;
for (int i = 0; i < (int)s.size(); i++) {
wchar_t c1 = s[i];
if (c1 == L'"') {
if (!isEscaped(s, i))
inQuote = !inQuote;
} else if (inQuote) {
if (c1 == c0) return i;
if (c1 == L'{' || c1 == L'[') return -1;
}
}
return -1;
}
wstring MojangsonParser::getString(const wstring& s, int i) {
stack<wchar_t> st;
int j = i + 1;
bool inQuote = false;
bool hadQuote = false;
bool hadContent = false;
int lastQuoteEnd = -1;
for (; j < (int)s.size(); j++) {
wchar_t c0 = s[j];
if (c0 == L'"') {
if (isEscaped(s, j)) {
if (!inQuote)
throw MojangsonParseException(L"Illegal use of \\\": " + s);
} else {
inQuote = !inQuote;
if (inQuote && !hadContent) hadQuote = true;
if (!inQuote) lastQuoteEnd = j;
}
} else if (!inQuote) {
if (c0 == L'{' || c0 == L'[') {
st.push(c0);
} else if (c0 == L'}') {
if (st.empty() || st.top() != L'{')
throw MojangsonParseException(L"Unbalanced curly brackets {}: " + s);
st.pop();
} else if (c0 == L']') {
if (st.empty() || st.top() != L'[')
throw MojangsonParseException(L"Unbalanced square brackets []: " + s);
st.pop();
} else if (c0 == L',' && st.empty()) {
return s.substr(0, j);
}
}
if (!iswspace(c0)) {
if (!inQuote && hadQuote && lastQuoteEnd != j)
return s.substr(0, lastQuoteEnd + 1);
hadContent = true;
}
}
return s.substr(0, j);
}
wstring MojangsonParser::getKey(const wstring& s, bool flag) {
wstring trimmed = s;
while (!trimmed.empty() && iswspace(trimmed.front())) trimmed.erase(trimmed.begin());
if (flag && (!trimmed.empty() && (trimmed[0] == L'{' || trimmed[0] == L'[')))
return L"";
int i = locateSeparator(s, L':');
if (i == -1) {
if (flag) return L"";
throw MojangsonParseException(L"Unable to locate name/value separator for string: " + s);
}
wstring key = s.substr(0, i);
while (!key.empty() && iswspace(key.front())) key.erase(key.begin());
while (!key.empty() && iswspace(key.back())) key.pop_back();
if (key.size() >= 2 && key.front() == L'"' && key.back() == L'"')
key = key.substr(1, key.size() - 2);
return key;
}
wstring MojangsonParser::getValue(const wstring& s, bool flag) {
wstring trimmed = s;
while (!trimmed.empty() && iswspace(trimmed.front())) trimmed.erase(trimmed.begin());
if (flag && !trimmed.empty() && (trimmed[0] == L'{' || trimmed[0] == L'['))
return trimmed;
int i = locateSeparator(s, L':');
if (i == -1) {
if (flag) return s;
throw MojangsonParseException(L"Unable to locate name/value separator for string: " + s);
}
wstring val = s.substr(i + 1);
while (!val.empty() && iswspace(val.front())) val.erase(val.begin());
while (!val.empty() && iswspace(val.back())) val.pop_back();
return val;
}
wstring MojangsonParser::getEntry(const wstring& s, bool flag) {
int i = locateSeparator(s, L':');
int j = locateSeparator(s, L',');
if (flag) {
if (i == -1)
throw MojangsonParseException(L"Unable to locate name/value separator for string: " + s);
if (j != -1 && j < i)
throw MojangsonParseException(L"Name error at: " + s);
} else if (i == -1 || (j != -1 && i > j)) {
i = -1;
}
return getString(s, i);
}
MojangsonTypeParser* MojangsonParser::parseEntry(const wstring& s, bool flag) {
wstring key = getKey(s, flag);
wstring value = getValue(s, flag);
wstring arr[2] = { key, value };
return createParser(arr, 2);
}
MojangsonTypeParser* MojangsonParser::createParser(const wstring& name, const wstring& value) {
wstring s1 = value;
while (!s1.empty() && iswspace(s1.front())) s1.erase(s1.begin());
while (!s1.empty() && iswspace(s1.back())) s1.pop_back();
if (!s1.empty() && s1.front() == L'{') {
wstring inner = s1.substr(1, s1.size() - 1);
if (!inner.empty() && inner.back() == L'}')
inner = inner.substr(0, inner.size() - 1);
MojangsonCompoundParser* cp = new MojangsonCompoundParser(name);
while (!inner.empty()) {
wstring entry = getEntry(inner, true);
if (!entry.empty())
cp->b.push_back(parseEntry(entry, false));
if ((int)inner.size() < (int)entry.size() + 1) break;
wchar_t sep = inner[entry.size()];
if (sep != L',' && sep != L'{' && sep != L'}' && sep != L'[' && sep != L']')
throw MojangsonParseException(wstring(L"Unexpected token '") + sep + L"'");
inner = inner.substr(entry.size() + 1);
}
return cp;
}
if (!s1.empty() && s1.front() == L'[' && !matchesSimpleIntArray(s1)) {
wstring inner = s1.substr(1, s1.size() - 1);
if (!inner.empty() && inner.back() == L']')
inner = inner.substr(0, inner.size() - 1);
MojangsonListParser* lp = new MojangsonListParser(name);
while (!inner.empty()) {
wstring entry = getEntry(inner, false);
if (!entry.empty())
lp->b.push_back(parseEntry(entry, true));
if ((int)inner.size() < (int)entry.size() + 1) break;
wchar_t sep = inner[entry.size()];
if (sep != L',' && sep != L'{' && sep != L'}' && sep != L'[' && sep != L']')
throw MojangsonParseException(wstring(L"Unexpected token '") + sep + L"'");
inner = inner.substr(entry.size() + 1);
}
return lp;
}
return new MojangsonPrimitiveParser(name, s1);
}
MojangsonTypeParser* MojangsonParser::createParser(const wstring* arr, int len) {
return createParser(arr[0], arr[1]);
}
int MojangsonParser::countTopLevel(const wstring& s) {
int count = 0;
bool inQuote = false;
stack<wchar_t> st;
for (int j = 0; j < (int)s.size(); j++) {
wchar_t c0 = s[j];
if (c0 == L'"') {
if (isEscaped(s, j)) {
if (!inQuote)
throw MojangsonParseException(L"Illegal use of \\\": " + s);
} else {
inQuote = !inQuote;
}
} else if (!inQuote) {
if (c0 == L'{' || c0 == L'[') {
if (st.empty()) count++;
st.push(c0);
} else if (c0 == L'}') {
if (st.empty() || st.top() != L'{')
throw MojangsonParseException(L"Unbalanced curly brackets {}: " + s);
st.pop();
} else if (c0 == L']') {
if (st.empty() || st.top() != L'[')
throw MojangsonParseException(L"Unbalanced square brackets []: " + s);
st.pop();
}
}
}
if (inQuote)
throw MojangsonParseException(L"Unbalanced quotation: " + s);
if (!st.empty())
throw MojangsonParseException(L"Unbalanced brackets: " + s);
if (count == 0 && !s.empty()) count = 1;
return count;
}
CompoundTag* MojangsonParser::parse(const wstring& input) {
wstring s = input;
while (!s.empty() && iswspace(s.front())) s.erase(s.begin());
while (!s.empty() && iswspace(s.back())) s.pop_back();
if (s.empty() || s.front() != L'{')
throw MojangsonParseException(L"Invalid tag encountered, expected '{' as first char.");
if (countTopLevel(s) != 1)
throw MojangsonParseException(L"Encountered multiple top tags, only one expected");
MojangsonTypeParser* parser = createParser(L"tag", s);
Tag* tag = parser->parse();
delete parser;
if (tag->getId() != Tag::TAG_Compound) {
delete tag;
throw MojangsonParseException(L"Root tag must be a compound tag");
}
return static_cast<CompoundTag*>(tag);
}

View file

@ -0,0 +1,104 @@
#pragma once
#include "CompoundTag.h"
#include "ListTag.h"
#include "Tag.h"
#include <string>
#include <vector>
#include <stdexcept>
// this is the translation of net.minecraft.server.MojangsonParser (Spigot 1.8)
// from java to c++
class MojangsonParseException : public std::runtime_error {
public:
explicit MojangsonParseException(const std::string& msg) : std::runtime_error(msg) {}
explicit MojangsonParseException(const std::wstring& msg)
: std::runtime_error(std::string(msg.begin(), msg.end())) {}
};
class MojangsonTypeParser {
public:
std::wstring a;
virtual Tag* parse() = 0;
virtual ~MojangsonTypeParser() {}
};
class MojangsonParser {
public:
static CompoundTag* parse(const std::wstring& s);
static int countTopLevel(const std::wstring& s);
static MojangsonTypeParser* createParser(const std::wstring& name, const std::wstring& value);
static MojangsonTypeParser* createParser(const std::wstring* arr, int len);
public:
static MojangsonTypeParser* parseEntry(const std::wstring& s, bool flag);
static std::wstring getEntry(const std::wstring& s, bool flag);
static std::wstring getString(const std::wstring& s, int i);
static std::wstring getKey(const std::wstring& s, bool flag);
static std::wstring getValue(const std::wstring& s, bool flag);
static int locateSeparator(const std::wstring& s, wchar_t c0);
static bool isEscaped(const std::wstring& s, int i);
static bool matchesSimpleIntArray(const std::wstring& s);
};
class MojangsonPrimitiveParser : public MojangsonTypeParser {
public:
std::wstring b;
MojangsonPrimitiveParser(const std::wstring& name, const std::wstring& value) {
a = name;
b = value;
}
Tag* parse() override;
private:
static bool matchesDouble(const std::wstring& s);
static bool matchesFloat(const std::wstring& s);
static bool matchesByte(const std::wstring& s);
static bool matchesLong(const std::wstring& s);
static bool matchesShort(const std::wstring& s);
static bool matchesInt(const std::wstring& s);
static bool matchesDecimal(const std::wstring& s);
};
class MojangsonListParser : public MojangsonTypeParser {
public:
std::vector<MojangsonTypeParser*> b;
MojangsonListParser(const std::wstring& name) { a = name; }
Tag* parse() override;
~MojangsonListParser() {
for (auto* p : b) delete p;
}
};
class MojangsonCompoundParser : public MojangsonTypeParser {
public:
std::vector<MojangsonTypeParser*> b;
MojangsonCompoundParser(const std::wstring& name) { a = name; }
Tag* parse() override;
~MojangsonCompoundParser() {
for (auto* p : b) delete p;
}
};

View file

@ -0,0 +1,463 @@
#include "stdafx.h"
#include "PlayerSelector.h"
#include "EntityTypeMap.h"
#include "MojangsonParser.h"
#include "CompoundTag.h"
//#include "net.minecraft.world.entity.player.h"
#include"../Minecraft.Client/ServerPlayer.h"
#include "../Minecraft.Client/ServerPlayerGameMode.h"
#include <algorithm>
#include <cwctype>
#include <sstream>
#include <regex>
#include <random>
using std::wstring;
using std::map;
using std::vector;
using std::shared_ptr;
static wstring toLowerW(const wstring& s)
{
wstring r = s;
std::transform(r.begin(), r.end(), r.begin(), towlower);
return r;
}
static bool startsWith(const wstring& s, const wstring& prefix)
{
return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix;
}
static bool parseToken(const wstring& token, wstring& selectorType, wstring& argString)
{
if (token.size() < 2 || token[0] != L'@') return false;
wchar_t t = towlower(token[1]);
if (t != L'p' && t != L'a' && t != L'r' && t != L'e' && t != L's') return false;
selectorType = wstring(1, t);
if (token.size() == 2)
{
argString = L"";
return true;
}
if (token[2] == L'[' && token.back() == L']')
{
argString = token.substr(3, token.size() - 4);
return true;
}
return false;
}
map<wstring, wstring> PlayerSelector::getArgumentMap(const wstring& argString)
{
map<wstring, wstring> result;
if (argString.empty()) return result;
vector<wstring> tokens;
{
int depth = 0;
wstring cur;
for (wchar_t c : argString)
{
if (c == L'{') depth++;
if (c == L'}') depth--;
if (c == L',' && depth == 0) { tokens.push_back(cur); cur.clear(); }
else cur += c;
}
if (!cur.empty()) tokens.push_back(cur);
}
static const wchar_t* positional[] = { L"x", L"y", L"z", L"r" };
int positionalIdx = 0;
for (const wstring& tok : tokens)
{
size_t eq = tok.find(L'=');
if (eq == wstring::npos)
{
if (positionalIdx < 4 && !tok.empty())
result[positional[positionalIdx]] = tok;
positionalIdx++;
}
else
{
wstring key = tok.substr(0, eq);
wstring value = tok.substr(eq + 1);
while (!key.empty() && iswspace(key.front())) key.erase(key.begin());
while (!key.empty() && iswspace(key.back())) key.pop_back();
while (!value.empty() && iswspace(value.front())) value.erase(value.begin());
while (!value.empty() && iswspace(value.back())) value.pop_back();
result[key] = value;
positionalIdx = 4;
}
}
return result;
}
int PlayerSelector::parseIntWithDefault(const map<wstring, wstring>& args, const wstring& key, int def)
{
auto it = args.find(key);
if (it == args.end() || it->second.empty()) return def;
try { return std::stoi(it->second); } catch (...) { return def; }
}
double PlayerSelector::parseDoubleWithDefault(const map<wstring, wstring>& args, const wstring& key, double def)
{
auto it = args.find(key);
if (it == args.end() || it->second.empty()) return def;
try { return std::stod(it->second); } catch (...) { return def; }
}
wstring PlayerSelector::getArg(const map<wstring, wstring>& args, const wstring& key)
{
auto it = args.find(key);
return (it != args.end()) ? it->second : L"";
}
void PlayerSelector::getOrigin(const map<wstring, wstring>& args, shared_ptr<CommandSender> sender,
double& ox, double& oy, double& oz)
{
ox = oy = oz = 0.0;
if (sender)
{
auto e = dynamic_pointer_cast<Entity>(sender);
if (e) { ox = e->x; oy = e->y; oz = e->z; }
}
auto it = args.find(L"x"); if (it != args.end()) { try { ox = std::stod(it->second); } catch (...) {} }
it = args.find(L"y"); if (it != args.end()) { try { oy = std::stod(it->second); } catch (...) {} }
it = args.find(L"z"); if (it != args.end()) { try { oz = std::stod(it->second); } catch (...) {} }
}
bool PlayerSelector::hasWorldBindingArgs(const map<wstring, wstring>& args)
{
static const wchar_t* binding[] = { L"x", L"y", L"z", L"dx", L"dy", L"dz", L"rm", L"r" };
for (auto k : binding)
if (args.count(k)) return true;
return false;
}
int PlayerSelector::normaliseAngle(int a)
{
a = a % 360;
if (a >= 180) a -= 360;
if (a < -180) a += 360;
return a;
}
bool PlayerSelector::matchesType(shared_ptr<Entity> e, const wstring& selectorType, const map<wstring, wstring>& args)
{
wstring typeVal = getArg(args, L"type");
bool invert = false;
if (!typeVal.empty() && typeVal[0] == L'!')
{
invert = true;
typeVal = typeVal.substr(1);
}
if (typeVal.empty())
{
bool isPlayer = e->instanceof(eTYPE_PLAYER);
if (selectorType == L"e") return true; // all entities
return isPlayer; // only players for @a/@p/@r
}
bool matches;
if (typeVal == L"player")
matches = e->instanceof(eTYPE_PLAYER);
else
{
eINSTANCEOF t = EntityTypeMap::getTypeFromName(toLowerW(typeVal));
matches = (t != eTYPE_NOTSET) && e->instanceof(t);
}
return invert ? !matches : matches;
}
bool PlayerSelector::matchesLevel(shared_ptr<Entity> e, const map<wstring, wstring>& args)
{
int lm = parseIntWithDefault(args, L"lm", -1);
int l = parseIntWithDefault(args, L"l", -1);
if (lm == -1 && l == -1) return true;
auto player = dynamic_pointer_cast<Player>(e);
if (!player) return false;
int xp = player->experienceLevel;
if (lm >= 0 && xp < lm) return false;
if (l >= 0 && xp > l) return false;
return true;
}
bool PlayerSelector::matchesGameMode(shared_ptr<Entity> e, const map<wstring, wstring>& args)
{
int m = parseIntWithDefault(args, L"m", -1);
if (m == -1) return true;
auto player = dynamic_pointer_cast<ServerPlayer>(e);
if (!player) return false;
return player->gameMode->getGameModeForPlayer()->getId() == m;
}
bool PlayerSelector::matchesTeam(shared_ptr<Entity> e, const map<wstring, wstring>& args)
{
wstring teamVal = getArg(args, L"team");
if (teamVal.empty()) return true;
bool invert = false;
if (teamVal[0] == L'!') { invert = true; teamVal = teamVal.substr(1); }
// No scoreboard system
wstring entityTeam = L""; // placeholder: implement when scoreboard exists
bool matches = (entityTeam == teamVal);
return invert ? !matches : matches;
}
bool PlayerSelector::matchesName(shared_ptr<Entity> e, const map<wstring, wstring>& args)
{
wstring nameVal = getArg(args, L"name");
if (nameVal.empty()) return true;
bool invert = false;
if (nameVal[0] == L'!') { invert = true; nameVal = nameVal.substr(1); }
bool matches = (e->getName() == nameVal);
return invert ? !matches : matches;
}
bool PlayerSelector::matchesRange(shared_ptr<Entity> e, const map<wstring, wstring>& args,
double ox, double oy, double oz)
{
int rm = parseIntWithDefault(args, L"rm", -1);
int r = parseIntWithDefault(args, L"r", -1);
if (rm < 0 && r < 0) return true;
double dx = e->x - ox;
double dy = e->y - oy;
double dz = e->z - oz;
double distSq = dx*dx + dy*dy + dz*dz;
if (rm >= 0 && distSq < (double)(rm * rm)) return false;
if (r >= 0 && distSq > (double)(r * r)) return false;
return true;
}
bool PlayerSelector::matchesRotation(shared_ptr<Entity> e, const map<wstring, wstring>& args)
{
bool hasRy = args.count(L"ry") > 0;
bool hasRym = args.count(L"rym") > 0;
bool hasRx = args.count(L"rx") > 0;
bool hasRxm = args.count(L"rxm") > 0;
if (!hasRy && !hasRym && !hasRx && !hasRxm) return true;
if (hasRy || hasRym)
{
int yMin = normaliseAngle(parseIntWithDefault(args, L"rym", 0));
int yMax = normaliseAngle(parseIntWithDefault(args, L"ry", 359));
int yaw = normaliseAngle((int)std::floor(e->yRot));
bool ok = (yMin > yMax) ? (yaw >= yMin || yaw <= yMax) : (yaw >= yMin && yaw <= yMax);
if (!ok) return false;
}
if (hasRx || hasRxm)
{
int xMin = normaliseAngle(parseIntWithDefault(args, L"rxm", 0));
int xMax = normaliseAngle(parseIntWithDefault(args, L"rx", 359));
int pitch = normaliseAngle((int)std::floor(e->xRot));
bool ok = (xMin > xMax) ? (pitch >= xMin || pitch <= xMax) : (pitch >= xMin && pitch <= xMax);
if (!ok) return false;
}
return true;
}
bool PlayerSelector::matchesNbt(shared_ptr<Entity> e, const map<wstring, wstring>& args)
{
wstring nbtVal = getArg(args, L"nbt");
if (nbtVal.empty()) return true;
bool invert = false;
if (nbtVal[0] == L'!') { invert = true; nbtVal = nbtVal.substr(1); }
CompoundTag* filter = nullptr;
try { filter = MojangsonParser::parse(nbtVal); }
catch (...) { return !invert; }
CompoundTag entityNbt(L"");
if (e == nullptr) return !invert;
try {
e->saveWithoutId(&entityNbt);
} catch (...) {
delete filter;
return !invert;
}
auto* allTags = filter->getAllTags();
bool matches = true;
for (Tag* filterTag : *allTags)
{
Tag* entityTag = entityNbt.get(filterTag->getName());
if (entityTag == nullptr || !filterTag->equals(entityTag))
{
matches = false;
break;
}
}
delete allTags;
delete filter;
return invert ? !matches : matches;
}
bool PlayerSelector::matchesAABB(shared_ptr<Entity> e, const map<wstring, wstring>& args,
double ox, double oy, double oz)
{
if (!args.count(L"dx") && !args.count(L"dy") && !args.count(L"dz")) return true;
int dx = parseIntWithDefault(args, L"dx", 0);
int dy = parseIntWithDefault(args, L"dy", 0);
int dz = parseIntWithDefault(args, L"dz", 0);
double x0 = ox + (dx < 0 ? dx : 0);
double y0 = oy + (dy < 0 ? dy : 0);
double z0 = oz + (dz < 0 ? dz : 0);
double x1 = ox + (dx < 0 ? 0 : dx) + 1;
double y1 = oy + (dy < 0 ? 0 : dy) + 1;
double z1 = oz + (dz < 0 ? 0 : dz) + 1;
return e->x >= x0 && e->x < x1 &&
e->y >= y0 && e->y < y1 &&
e->z >= z0 && e->z < z1;
}
vector<shared_ptr<Entity>> PlayerSelector::applyCountAndSort(
vector<shared_ptr<Entity>>& candidates,
const map<wstring, wstring>& args,
shared_ptr<CommandSender> sender,
const wstring& selectorType,
double ox, double oy, double oz)
{
int defaultCount = (selectorType == L"a" || selectorType == L"e") ? 0 : 1;
int c = parseIntWithDefault(args, L"c", defaultCount);
if (selectorType == L"r")
{
std::shuffle(candidates.begin(), candidates.end(), std::mt19937{std::random_device{}()});
}
else if (selectorType == L"p" || selectorType == L"a" || selectorType == L"e")
{
std::sort(candidates.begin(), candidates.end(),
[ox, oy, oz](const shared_ptr<Entity>& a, const shared_ptr<Entity>& b)
{
double da = (a->x-ox)*(a->x-ox) + (a->y-oy)*(a->y-oy) + (a->z-oz)*(a->z-oz);
double db = (b->x-ox)*(b->x-ox) + (b->y-oy)*(b->y-oy) + (b->z-oz)*(b->z-oz);
return da < db;
});
}
if (c == 0) return candidates;
if (c < 0)
{
std::reverse(candidates.begin(), candidates.end());
c = -c;
}
int take = (std::min)(c, (int)candidates.size());
return vector<shared_ptr<Entity>>(candidates.begin(), candidates.begin() + take);
}
bool PlayerSelector::hasArguments(const wstring& token)
{
wstring st, as;
return parseToken(token, st, as);
}
bool PlayerSelector::matchesMultiplePlayers(const wstring& token)
{
wstring st, as;
if (!parseToken(token, st, as)) return false;
auto args = getArgumentMap(as);
int defC = (st == L"a" || st == L"e") ? 0 : 1;
return parseIntWithDefault(args, L"c", defC) != 1;
}
vector<shared_ptr<Entity>> PlayerSelector::matchEntities(
shared_ptr<CommandSender> sender,
const wstring& token,
eINSTANCEOF targetType)
{
wstring selectorType, argString;
if (!parseToken(token, selectorType, argString))
return {};
auto args = getArgumentMap(argString);
double ox, oy, oz;
getOrigin(args, sender, ox, oy, oz);
Level* level = nullptr;
{
auto senderEntity = dynamic_pointer_cast<Entity>(sender);
if (senderEntity) level = senderEntity->level;
}
if (!level) return {};
// @s
if (selectorType == L"s")
{
auto senderEntity = dynamic_pointer_cast<Entity>(sender);
if (!senderEntity || senderEntity->removed) return {};
if (targetType != eTYPE_ENTITY && !senderEntity->instanceof(targetType)) return {};
return { senderEntity };
}
vector<shared_ptr<Entity>> candidates;
// Gather from all entities in the level
for (auto& entity : level->entities)
{
if (entity == nullptr || entity->removed) continue;
if (targetType != eTYPE_ENTITY && !entity->instanceof(targetType)) continue;
if (!matchesType (entity, selectorType, args)) continue;
if (!matchesLevel (entity, args)) continue;
if (!matchesGameMode(entity, args)) continue;
if (!matchesTeam (entity, args)) continue;
if (!matchesName (entity, args)) continue;
if (!matchesRange (entity, args, ox, oy, oz)) continue;
if (!matchesRotation(entity, args)) continue;
if (!matchesNbt (entity, args)) continue;
if (!matchesAABB (entity, args, ox, oy, oz)) continue;
candidates.push_back(entity);
}
return applyCountAndSort(candidates, args, sender, selectorType, ox, oy, oz);
}
shared_ptr<Entity> PlayerSelector::matchOneEntity(shared_ptr<CommandSender> sender, const wstring& token)
{
auto list = matchEntities(sender, token, eTYPE_ENTITY);
return (list.size() == 1) ? list[0] : nullptr;
}
shared_ptr<Player> PlayerSelector::matchOnePlayer(shared_ptr<CommandSender> sender, const wstring& token)
{
auto list = matchEntities(sender, token, eTYPE_PLAYER);
if (list.size() != 1) return nullptr;
return dynamic_pointer_cast<Player>(list[0]);
}

View file

@ -1,247 +1,61 @@
/*
package net.minecraft.commands;
#pragma once
#include <vector>
#include <map>
#include <string>
#include <memory>
#include "net.minecraft.world.entity.h"
#include "net.minecraft.world.entity.player.h"
#include "net.minecraft.world.level.h"
#include "net.minecraft.commands.h"
import net.minecraft.Pos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelSettings;
using std::wstring;
using std::map;
using std::vector;
using std::shared_ptr;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//translation of net.minecraft.command.PlayerSelector from java to c++
class PlayerSelector
{
public:
// Returns true if the token is a selector pattern (@p, @a, @r, @e, @s)
static bool hasArguments(const wstring& token);
public class PlayerSelector {
private static final Pattern PATTERN_TARGETS = Pattern.compile("^@([parf])(?:\\[([\\w=,!-]*)\\])?$");
private static final Pattern PATTERN_SHORT_ARGUMENT = Pattern.compile("\\G([-!]?[\\w-]*)(?:$|,)");
private static final Pattern PATTERN_LONG_ARGUMENT = Pattern.compile("\\G(\\w+)=([-!]?[\\w-]*)(?:$|,)");
static bool matchesMultiplePlayers(const wstring& token);
private static final int TARGETS_GROUP_TYPE = 1;
private static final int TARGETS_GROUP_ARGS = 2; // Null if not specified
static shared_ptr<Player> matchOnePlayer(shared_ptr<CommandSender> sender, const wstring& token);
private static final String TARGET_NEAREST = "p";
private static final String TARGET_ALL = "a";
private static final String TARGET_RANDOM = "r";
static shared_ptr<Entity> matchOneEntity(shared_ptr<CommandSender> sender, const wstring& token);
private static final String ARGUMENT_RANGE_MAX = "r";
private static final String ARGUMENT_RANGE_MIN = "rm";
private static final String ARGUMENT_LEVEL_MAX = "l";
private static final String ARGUMENT_LEVEL_MIN = "lm";
private static final String ARGUMENT_COORDINATE_X = "x";
private static final String ARGUMENT_COORDINATE_Y = "y";
private static final String ARGUMENT_COORDINATE_Z = "z";
private static final String ARGUMENT_COUNT = "c";
private static final String ARGUMENT_MODE = "m";
private static final String ARGUMENT_SCORE_PREFIX = "score_";
private static final String ARGUMENT_TEAM_NAME = "team";
private static final String ARGUMENT_PLAYER_NAME = "name";
static vector<shared_ptr<Entity>> matchEntities(shared_ptr<CommandSender> sender, const wstring& token, eINSTANCEOF targetType = eTYPE_ENTITY);
public static ServerPlayer getPlayer(CommandSender source, String input) {
ServerPlayer[] result = getPlayers(source, input);
private:
if (result == null || result.length != 1) return null;
static map<wstring, wstring> getArgumentMap(const wstring& argString);
static int parseIntWithDefault(const map<wstring, wstring>& args, const wstring& key, int def);
static double parseDoubleWithDefault(const map<wstring, wstring>& args, const wstring& key, double def);
static wstring getArg(const map<wstring, wstring>& args, const wstring& key);
return result[0];
}
static void getOrigin(const map<wstring, wstring>& args, shared_ptr<CommandSender> sender,
double& ox, double& oy, double& oz);
public static String getPlayerNames(CommandSender source, String input) {
ServerPlayer[] result = getPlayers(source, input);
if (result == null || result.length == 0) return null;
String[] names = new String[result.length];
static bool hasWorldBindingArgs(const map<wstring, wstring>& args);
for (int i = 0; i < names.length; i++) {
names[i] = result[i].getDisplayName();
}
static int normaliseAngle(int angle);
return BaseCommand.joinStrings(names);
}
static vector<shared_ptr<Entity>> applyCountAndSort(
vector<shared_ptr<Entity>>& candidates,
const map<wstring, wstring>& args,
shared_ptr<CommandSender> sender,
const wstring& selectorType,
double ox, double oy, double oz);
public static ServerPlayer[] getPlayers(CommandSender source, String input) {
Matcher matcher = PATTERN_TARGETS.matcher(input);
if (matcher.matches()) {
Map<String, String> args = getArguments(matcher.group(TARGETS_GROUP_ARGS));
String type = matcher.group(TARGETS_GROUP_TYPE);
int rangeMin = getDefaultRangeMin(type);
int rangeMax = getDefaultRangeMax(type);
int levelMin = getDefaultLevelMin(type);
int levelMax = getDefaultLevelMax(type);
int count = getDefaultCount(type);
int mode = LevelSettings.GameType.NOT_SET.getId();
Pos pos = source.getCommandSenderWorldPosition();
Map<String, Integer> scores = getScores(args);
String name = null;
String team = null;
boolean requireLevel = false;
if (args.containsKey(ARGUMENT_RANGE_MIN)) {
rangeMin = Mth.getInt(args.get(ARGUMENT_RANGE_MIN), rangeMin);
requireLevel = true;
}
if (args.containsKey(ARGUMENT_RANGE_MAX)) {
rangeMax = Mth.getInt(args.get(ARGUMENT_RANGE_MAX), rangeMax);
requireLevel = true;
}
if (args.containsKey(ARGUMENT_LEVEL_MIN)) {
levelMin = Mth.getInt(args.get(ARGUMENT_LEVEL_MIN), levelMin);
}
if (args.containsKey(ARGUMENT_LEVEL_MAX)) {
levelMax = Mth.getInt(args.get(ARGUMENT_LEVEL_MAX), levelMax);
}
if (args.containsKey(ARGUMENT_COORDINATE_X)) {
pos.x = Mth.getInt(args.get(ARGUMENT_COORDINATE_X), pos.x);
requireLevel = true;
}
if (args.containsKey(ARGUMENT_COORDINATE_Y)) {
pos.y = Mth.getInt(args.get(ARGUMENT_COORDINATE_Y), pos.y);
requireLevel = true;
}
if (args.containsKey(ARGUMENT_COORDINATE_Z)) {
pos.z = Mth.getInt(args.get(ARGUMENT_COORDINATE_Z), pos.z);
requireLevel = true;
}
if (args.containsKey(ARGUMENT_MODE)) {
mode = Mth.getInt(args.get(ARGUMENT_MODE), mode);
}
if (args.containsKey(ARGUMENT_COUNT)) {
count = Mth.getInt(args.get(ARGUMENT_COUNT), count);
}
if (args.containsKey(ARGUMENT_TEAM_NAME)) {
team = args.get(ARGUMENT_TEAM_NAME);
}
if (args.containsKey(ARGUMENT_PLAYER_NAME)) {
name = args.get(ARGUMENT_PLAYER_NAME);
}
Level level = requireLevel ? source.getCommandSenderWorld() : null;
if (type.equals(TARGET_NEAREST) || type.equals(TARGET_ALL)) {
List<ServerPlayer> players = MinecraftServer.getInstance().getPlayers().getPlayers(pos, rangeMin, rangeMax, count, mode, levelMin, levelMax, scores, name, team, level);
return players == null || players.isEmpty() ? new ServerPlayer[0] : players.toArray(new ServerPlayer[0]);
} else if (type.equals(TARGET_RANDOM)) {
List<ServerPlayer> players = MinecraftServer.getInstance().getPlayers().getPlayers(pos, rangeMin, rangeMax, 0, mode, levelMin, levelMax, scores, name, team, level);
Collections.shuffle(players);
players = players.subList(0, Math.min(count, players.size()));
return players == null || players.isEmpty() ? new ServerPlayer[0] : players.toArray(new ServerPlayer[0]);
} else {
return null;
}
} else {
return null;
}
}
public static Map<String, Integer> getScores(Map<String, String> input) {
Map<String, Integer> result = new HashMap<String, Integer>();
for (String key : input.keySet()) {
if (key.startsWith(ARGUMENT_SCORE_PREFIX) && key.length() > ARGUMENT_SCORE_PREFIX.length()) {
String name = key.substring(ARGUMENT_SCORE_PREFIX.length());
result.put(name, Mth.getInt(input.get(key), 1));
}
}
return result;
}
public static boolean isList(String input) {
Matcher matcher = PATTERN_TARGETS.matcher(input);
if (matcher.matches()) {
Map<String, String> args = getArguments(matcher.group(TARGETS_GROUP_ARGS));
String type = matcher.group(TARGETS_GROUP_TYPE);
int count = getDefaultCount(type);
if (args.containsKey(ARGUMENT_COUNT)) count = Mth.getInt(args.get(ARGUMENT_COUNT), count);
return count != 1;
}
return false;
}
public static boolean isPattern(String input, String onlyType) {
Matcher matcher = PATTERN_TARGETS.matcher(input);
if (matcher.matches()) {
String type = matcher.group(TARGETS_GROUP_TYPE);
if (onlyType != null && !onlyType.equals(type)) return false;
return true;
}
return false;
}
public static boolean isPattern(String input) {
return isPattern(input, null);
}
private static final int getDefaultRangeMin(String type) {
return 0;
}
private static final int getDefaultRangeMax(String type) {
return 0;
}
private static final int getDefaultLevelMax(String type) {
return Integer.MAX_VALUE;
}
private static final int getDefaultLevelMin(String type) {
return 0;
}
private static final int getDefaultCount(String type) {
if (type.equals(TARGET_ALL)) {
return 0;
} else {
return 1;
}
}
private static Map<String, String> getArguments(String input) {
HashMap<String, String> result = new HashMap<String, String>();
if (input == null) return result;
Matcher matcher = PATTERN_SHORT_ARGUMENT.matcher(input);
int count = 0;
int last = -1;
while (matcher.find()) {
String name = null;
switch (count++) {
case 0:
name = ARGUMENT_COORDINATE_X;
break;
case 1:
name = ARGUMENT_COORDINATE_Y;
break;
case 2:
name = ARGUMENT_COORDINATE_Z;
break;
case 3:
name = ARGUMENT_RANGE_MAX;
break;
}
if (name != null && matcher.group(1).length() > 0) result.put(name, matcher.group(1));
last = matcher.end();
}
if (last < input.length()) {
matcher = PATTERN_LONG_ARGUMENT.matcher(last == -1 ? input : input.substring(last));
while (matcher.find()) {
result.put(matcher.group(1), matcher.group(2));
}
}
return result;
}
}
*/
static bool matchesType (shared_ptr<Entity> e, const wstring& selectorType, const map<wstring, wstring>& args);
static bool matchesLevel (shared_ptr<Entity> e, const map<wstring, wstring>& args);
static bool matchesGameMode (shared_ptr<Entity> e, const map<wstring, wstring>& args);
static bool matchesTeam (shared_ptr<Entity> e, const map<wstring, wstring>& args);
static bool matchesName (shared_ptr<Entity> e, const map<wstring, wstring>& args);
static bool matchesRange (shared_ptr<Entity> e, const map<wstring, wstring>& args, double ox, double oy, double oz);
static bool matchesRotation (shared_ptr<Entity> e, const map<wstring, wstring>& args);
static bool matchesNbt (shared_ptr<Entity> e, const map<wstring, wstring>& args);
static bool matchesAABB (shared_ptr<Entity> e, const map<wstring, wstring>& args, double ox, double oy, double oz);
};

View file

@ -173,6 +173,7 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_COMMANDS
"${CMAKE_CURRENT_SOURCE_DIR}/CommandSender.h"
"${CMAKE_CURRENT_SOURCE_DIR}/CommandsEnum.h"
"${CMAKE_CURRENT_SOURCE_DIR}/PlayerSelector.h"
"${CMAKE_CURRENT_SOURCE_DIR}/PlayerSelector.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/net.minecraft.commands.h"
)
source_group("net/minecraft/commands" FILES ${_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_COMMANDS})
@ -206,6 +207,8 @@ set(_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_COMMANDS_COMMON
"${CMAKE_CURRENT_SOURCE_DIR}/net.minecraft.commands.common.h"
"${CMAKE_CURRENT_SOURCE_DIR}/EntityTypeMap.h"
"${CMAKE_CURRENT_SOURCE_DIR}/EntityTypeMap.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MojangsonParser.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MojangsonParser.h"
)
source_group("net/minecraft/commands/common" FILES ${_MINECRAFT_WORLD_COMMON_NET_MINECRAFT_COMMANDS_COMMON})