Compare commits

...

7 Commits

Author SHA1 Message Date
93dbf79583
Reformatted Root.cpp and Initial Launching Client
BUILD_CLIENT cmake compile option was added in the previous commit.
This commit impliments the change to run start_nyqubel_client() and
allows the new "client" code to run.

Root.cpp was also reformatted due to this initial change.
2024-02-13 09:46:57 -05:00
431bebc361
Initial Client Implimented in GUI, still not launchable.
The code is implimented in this commit, but is not launchable as the
change needs to be made in root.
Everything should be launchable by running the one function within
src/nyqubel-client/client.hpp: start_nyqubel_client()
2024-02-13 09:44:41 -05:00
4d27542ea9
Impliment Command Manager within Server class and Server.cpp Reformatted
Command manager will now be called into by the Server Class when users
enter that into chat.
2024-02-13 09:31:26 -05:00
cbdc83da7c
New Easy Build scripts for linux.
I want to go fast, these allow you to simply run the script and it goes.
2024-02-13 09:28:49 -05:00
5b568047c4
Better Clang Format
This new format aligns better with whats in place for this currently. My
IDE wants to auto sort it, mind as well but it needs a clang-format that
matches this. Was having issues for me with the old one.
I used the libpdw one since it works already for me and appears to
replace it nicely.
2024-02-13 09:25:22 -05:00
e790e44300
Add a Command Manager to handle the commands in place of the old LuaPlugin Manager. RIP you will not be missed lua 2024-02-13 09:24:21 -05:00
a37375b1bb
Adding Libraries to assist with building a temporary UI and Client
Three specific libraries are being added for a purpose.
1. libhydride
it will setup a glx enviroment for us and we dont really have to do
anything more than let it create the enviroment. It creates a overlay in
x11 that is meant to be transparent over your display, and potentially
invisible to OBS depending on your needs. Its an additional feature we
dont need but its still making the process easy nonetheless.
2. libglez
this provides primitive drawing functions on top of opengl, it makes the
gl be ez. This is also simple "slide in place" with libhydride to allow
us to make pretty cubes and squares with words and the like.
3. libpdw
Using the GUI that I personally forked off of, it is useful and simple.
I have experience using it and not knowing alot of math, it will make
doing 2d work easier than doing things in 3d.

These three are all GPLv3 and should be compatible with the goals of
achieving a GPLv3 Client being made somewhat possible. The code that it
interrops with is new code and will be subject to the new licencing
recently added by me(rrowe) without any issue.
2024-02-13 09:15:49 -05:00
31 changed files with 3807 additions and 1245 deletions

View File

@ -1,39 +1,6 @@
# Not the exact style guide but enough for basic clang-tidy fix-its
Language: Cpp
BasedOnStyle: LLVM
AlignAfterOpenBracket: AlwaysBreak
BreakConstructorInitializers: AfterColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
PointerAlignment: Middle
SortIncludes: false
SpacesBeforeTrailingComments: 2
UseTab: Always
MaxEmptyLinesToKeep: 5
TabWidth: 4
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
IndentWidth: 4
IndentCaseLabels: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterExternBlock: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
# Always include globals first
IncludeCategories:
- Regex: 'Globals.h'
Priority: -1
BasedOnStyle: WebKit
BreakBeforeBraces: Attach
CompactNamespaces: true
FixNamespaceComments: true
NamespaceIndentation: None
SortIncludes: Never

11
.gitmodules vendored
View File

@ -65,3 +65,14 @@
[submodule "lib/libdeflate"]
path = lib/libdeflate
url = https://github.com/cuberite/libdeflate
[submodule "lib/libpdw"]
path = lib/libpdw
url = https://github.com/oneechanhax/libpdw
branch = async
[submodule "lib/libglez"]
path = lib/libglez
url = https://github.com/oneechanhax/libglez/
branch = rainbow
[submodule "lib/libhydride"]
path = lib/libhydride
url = https://github.com/oneechanhax/libhydride

View File

@ -31,6 +31,9 @@ function(build_dependencies)
# Enumerate all submodule libraries
# SQLiteCpp needs to be included before sqlite so the lsqlite target is available:
set(DEPENDENCIES expat fmt jsoncpp libdeflate libevent mbedtls SQLiteCpp)
if (BUILD_CLIENT)
set(DEPENDENCIES ${DEPENDENCIES} libglez libhydride libpdw)
endif()
foreach(DEPENDENCY ${DEPENDENCIES})
# Check that the libraries are present:
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/${DEPENDENCY}/CMakeLists.txt")
@ -42,6 +45,7 @@ function(build_dependencies)
# (mbedTLS also has test and example programs in their CMakeLists.txt, we don't want those):
add_subdirectory("lib/${DEPENDENCY}" EXCLUDE_FROM_ALL)
endforeach()
endfunction()
function(link_dependencies TARGET)
@ -80,4 +84,24 @@ function(link_dependencies TARGET)
if(${CMAKE_SYSTEM_NAME} MATCHES FreeBSD)
target_link_libraries(${TARGET} PRIVATE kvm)
endif()
if(BUILD_CLIENT)
set(OpenGL_GL_PREFERENCE "GLVND")
find_package(PNG REQUIRED) # We need to link all this again since we prebuilt them staticly
find_package(GLEW REQUIRED)
find_package(OpenGL REQUIRED)
find_package(X11 REQUIRED)
find_package(Freetype REQUIRED)
target_include_directories(
${TARGET} SYSTEM PRIVATE
lib/libpdw/include
)
target_link_libraries(${TARGET} PRIVATE
libpdw
hydride
glez
${PNG_LIBRARIES} GL GLU GLEW ${FREETYPE_LIBRARIES} ${X11_X11_LIB} ${X11_Xext_LIB} ${X11_Xfixes_LIB})
endif()
endfunction()

View File

@ -1,4 +1,4 @@
# This is the top-level CMakeLists.txt file for the Cuberite project
#
# Use CMake to generate the build files for your platform
@ -11,6 +11,7 @@ project(
LANGUAGES C CXX
)
option(BUILD_CLIENT "Sets up the build to create a client rather than a server. This is in tesing and will result in a hybrid server/client setup." OFF)
option(BUILD_TOOLS "Sets up additional executables to be built along with the server" OFF)
option(BUILD_UNSTABLE_TOOLS "Sets up yet more executables to be built, these can be broken and generally are obsolete" OFF)
option(NO_NATIVE_OPTIMIZATION "Disables CPU-specific optimisations for the current machine, allows use on other CPUs of the same platform" OFF)

12
build-clean.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -e
# Purpose of this is to in theroy be safer than rm -r...
if [ -f CMakeLists.txt ]; then
mkdir -p build
cd build
fi
if [ -f CMakeCache.txt ]; then
rm -r *
fi

12
build-client.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -e
if [ -f CMakeLists.txt ]; then
mkdir -p build
cd build
fi
if ! [ -f CMakeCache.txt ]; then
cmake -DCMAKE_BUILD_TYPE=Release -DUNITY_BUILDS=OFF -DWHOLE_PROGRAM_OPTIMISATION=OFF -DBUILD_CLIENT=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
fi
make -j$(nproc)

12
build-fastcompile.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -e
if [ -f CMakeLists.txt ]; then
mkdir -p build
cd build
fi
if ! [ -f CMakeCache.txt ]; then
cmake -DCMAKE_BUILD_TYPE=Release -DUNITY_BUILDS=OFF -DWHOLE_PROGRAM_OPTIMISATION=OFF ..
fi
make -j$(nproc)

12
build-release.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -e
if [ -f CMakeLists.txt ]; then
mkdir -p build
cd build
fi
if ! [ -f CMakeCache.txt ]; then
cmake -DCMAKE_BUILD_TYPE=Release ..
fi
make -j$(nproc)

12
build.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh -e
if [ -f CMakeLists.txt ]; then
mkdir -p build
cd build
fi
if ! [ -f CMakeCache.txt ]; then
cmake ..
fi
make -j$(nproc)

BIN
client-resources/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
client-resources/logo.xcf Normal file

Binary file not shown.

Binary file not shown.

1
lib/libglez Submodule

@ -0,0 +1 @@
Subproject commit 221db8e9721510449e7f14a644410b8c7aead09c

1
lib/libhydride Submodule

@ -0,0 +1 @@
Subproject commit 30659e1877f036b090bafc1a1e76170b88ab9fda

1
lib/libpdw Submodule

@ -0,0 +1 @@
Subproject commit 9cbc0a088d5e65a498c4ddab2beeb8643a76a865

View File

@ -148,10 +148,10 @@ target_sources(
)
set(FOLDERS
BlockEntities Blocks Entities
BlockEntities Blocks Commands Entities
Generating HTTP Items mbedTLS++ Mobs Noise
OSSupport Physics Protocol Registries Simulator
Simulator/IncrementalRedstoneSimulator UI WorldStorage
Simulator/IncrementalRedstoneSimulator UI WorldStorage nyqubel-client
)
# Add all child source directories:

134
src/Commands/CMDManager.cpp Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright 2011-2022 Cuberite Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CMDManager.h"
#include "StringUtils.h"
#include "Entities/Player.h"
#include "CoreCommands.h"
const cCMDManager::CommandMap cCMDManager::m_Commands = {
{"/clear", {"core.clear", "Clears the inventory of a player.", &HandleClearCommand}},
{"/effect", {"core.effect", "Adds an effect to a player.", &HandleEffectCommand}},
{"/kick", {"core.kick", "Kicks a player.", &HandleKickCommand}},
{"/list", {"core.list", "Shows a list of connected players.", &HandleListCommand}},
{"/rank", {"core.rank", "Shows or sets a player's rank.", &HandleRankCommand}},
{"/listgroups", {"core.listgroups", "Shows a list of the available groups.", &HandleListGroupsCommand}},
{"/listranks", {"core.listranks", "Shows a list of the available ranks.", &HandleListRanksCommand}},
{"/me", {"core.me", "Broadcasts what you are doing.", &HandleMeCommand}},
{"/numchunks", {"core.numchunks", "Shows number of chunks currently loaded.", &HandleNumChunksCommand}},
{"/op", {"core.rank", "Add a player to the administrator rank.", &HandleOpCommand}},
{"/players", {"core.players", "Shows a list of all connected players.", &HandlePlayersCommand}},
{"/portal", {"core.portal", "Moves your player to a different world.", &HandlePortalCommand}},
{"/save-all", {"core.save-all", "Saves all worlds.", &HandleSaveAllCommand}},
{"/say", {"core.say", "Sends a message in the chat to other players.", &HandleSayCommand}},
{"/seed", {"core.seed", "Shows the seed of the given world name or current world, if not given.", &HandleSeedCommand}},
{"/setspawn", {"core.setspawn", "Changes the world's spawn point.", &HandleSetSpawnCommand}},
{"/spawn", {"core.spawn", "Returns a player to the spawn point.", &HandleSpawnCommand}},
{"/spawnpoint", {"core.spawnpoint", "Sets the spawn point for a player.", &HandleSpawnPointCommand}},
//{"/stop", {"core.stop", "Stops the server.", &HandleStopCommand)}},
{"/summon", {"core.summon", "Summons an entity in the world.", &HandleSummonCommand}}
};
const cCMDManager::CommandMap cCMDManager::m_ConsoleCommands = cCMDManager::m_Commands;
cCMDManager::CommandResult cCMDManager::HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions)
{
AStringVector Split(StringSplit(a_Command, " "));
if (Split.empty())
{
return crUnknownCommand;
}
CommandMap::const_iterator cmd = m_Commands.find(Split[0]);
if (cmd == m_Commands.end())
{
// Command not found
// If it started with a slash, ask the plugins if they still want to handle it:
/*if (!a_Command.empty() && (a_Command[0] == '/'))
{
CommandResult Result = crUnknownCommand;
CallHookExecuteCommand(&a_Player, Split, a_Command, Result);
return Result;
}*/
return crUnknownCommand; // part of whole func comment out.
}
// Ask plugins first if a command is okay to execute the command:
/*(CommandResult Result = crBlocked;
if (CallHookExecuteCommand(&a_Player, Split, a_Command, Result))
{
if (Result == crBlocked)
{
LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
}
return Result;
}*/
if (
a_ShouldCheckPermissions &&
!cmd->second.m_Permission.empty() &&
!a_Player.HasPermission(cmd->second.m_Permission)
)
{
LOGINFO("Player %s tried to execute forbidden command: \"%s\"", a_Player.GetName().c_str(), Split[0].c_str());
return crNoPermission;
}
//ASSERT(cmd->second.m_Handler/* != nullptr*/ && *cmd->second.m_Handler);
if (!cmd->second.m_Handler(Split, &a_Player, a_Command, nullptr))
{
return crError;
}
return crExecuted;
}
bool cCMDManager::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command)
{
if (a_Split.empty())
{
return false;
}
CommandMap::const_iterator cmd = m_ConsoleCommands.find(a_Split[0]);
if (cmd == m_ConsoleCommands.end())
{
// Command not found
// Still notify the plugins (so that plugins such as Aliases can intercept unknown commands).
/*CommandResult res = crBlocked;
CallHookExecuteCommand(nullptr, a_Split, a_Command, res);*/
return false; // (res == crExecuted);
}
/*if (cmd->second.m_Plugin == nullptr)
{
// This is a built-in command
return false;
}*/
// Ask plugins first if a command is okay to execute the console command:
/*CommandResult res = crBlocked;
if (CallHookExecuteCommand(nullptr, a_Split, a_Command, res))
{
return (res == crExecuted);
}*/
return cmd->second.m_Handler(a_Split, nullptr, a_Command, &a_Output);
}

94
src/Commands/CMDManager.h Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2011-2022 Cuberite Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// this is apache2.0 since its taken from the old pluginmanager.
#pragma once
#include <functional>
#include <memory>
#include <list>
#include"StringUtils.h"
//#include "FunctionRef.h"
class cPlayer;
class cCommandOutputCallback;
class cCMDManager
{
public:
/*
*
* The following code is from Bindings/PluginManager, attempting to get command binding to work. I realized without uses, having the plugin system is kinda lame.
* New plan: Bake the commands in directly, aim for vanilla equality over adaptability!!! patches can be made to source if u want to mod it, its open source anyways ;-;
*
*/
enum CommandResult
{
crExecuted,
crUnknownCommand,
crError,
//crBlocked, // plugins dont hook anymore.
crNoPermission,
};
/** Executes the specified in-game command.
a_Split is the command string, split at the spaces.
a_Player is the player executing the command, nullptr in case of the console.
a_Command is the entire command string.
a_Output is the sink into which the additional text returned by the command handler should be sent; only used for console commands. */
/*using ExecuteCommand = cFunctionRef<bool(
const AStringVector & a_Split,
cPlayer * a_Player,
const AString & a_Command,
cCommandOutputCallback * a_Output //= nullptr
)>;*/
using ExecuteCommand = std::function<bool(
const AStringVector & a_Split,
cPlayer * a_Player,
const AString & a_Command,
cCommandOutputCallback * a_Output //= nullptr
)>;
using cCommandHandler = ExecuteCommand;
class cCommandReg
{
public:
/*cCommandReg(const AString& m_Permission, const AString& m_HelpString, cCommandHandlerPtr m_Handler)
: m_Permission(m_Permission), m_HelpString(m_HelpString), m_Handler(std::move(m_Handler)) {}*/
//cPlugin * m_Plugin;
AString m_Permission; // Not used for console commands
AString m_HelpString;
cCommandHandler m_Handler;
} ;
using CommandMap = std::map<AString, cCommandReg>;
static const CommandMap m_Commands; // constant due to it being compiled in :)
static const CommandMap m_ConsoleCommands;
/** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding class callback is called. Returns crExecuted if the command is executed. */
CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions);
/** Executes the command split into a_Split, as if it was given on the console.
Returns true if executed. Output is sent to the a_Output callback */
bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command);
};

89
src/Commands/CMDUtils.h Normal file
View File

@ -0,0 +1,89 @@
/*
* Copyright 2011-2022 Cuberite Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "ChatColor.h"
#include "CommandOutput.h"
#include "Root.h"
#include "Entities/Player.h"
/*
*
* Below here is the Plugin managers commands, ripped out and integrated directly into cuberite.
*
*/
void CommandSendMessage(cPlayer* a_Player, cCommandOutputCallback* a_Output, const AString& a_Message) {
if (a_Player) {
a_Player->SendMessageInfo(a_Message);
} else {
assert(a_Output);
a_Output->Out(a_Message);
}
}
void CommandSendMessageSuccess(cPlayer* a_Player, cCommandOutputCallback* a_Output, const AString& a_Message) {
if (a_Player) {
a_Player->SendMessageSuccess(a_Message);
} else {
assert(a_Output);
a_Output->Out(a_Message);
}
}
void CommandSendMessageFailure(cPlayer* a_Player, cCommandOutputCallback* a_Output, const AString& a_Message) {
if (a_Player) {
a_Player->SendMessageFailure(a_Message);
} else {
assert(a_Output);
a_Output->Out(a_Message);
}
}
cWorld* CommandGetWorld(const AString& world_name, cPlayer* a_Player) {
if (world_name.empty())
return (a_Player) ? a_Player->GetWorld() : cRoot::Get()->GetDefaultWorld();
return cRoot::Get()->GetWorld(world_name);
}
int RelativeCommandCoord(const AString& split, int coord) {
if (!split.empty()) {
if (split[0] == '~') {
int relative = 0;
try {
relative = std::stoi(split.substr(1));
} catch (...) { relative = 0; }
if (coord) {
if (relative) {
return coord + relative;
}
return coord;
}
return relative;
}
try { return std::stoi(split); } catch (...) {}
}
throw std::runtime_error("Split is empty!");
}

View File

@ -0,0 +1,10 @@
target_sources(
${CMAKE_PROJECT_NAME} PRIVATE
CMDManager.cpp
CoreCommands.cpp
CMDManager.h
CMDUtils.h
CoreCommands.h
)

View File

@ -0,0 +1,637 @@
/*
* Copyright 2011-2022 Cuberite Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ClientHandle.h"
#include "CMDUtils.h"
#include "Server.h"
bool HandleClearCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
cPlayerListCallback ClearInventory = [&](cPlayer& other_player) -> bool {
other_player.GetInventory().Clear();
if (a_Split.size() > 1) {
CommandSendMessageSuccess(a_Player, a_Output, "You cleared the inventory of player \"" + other_player.GetName() + "\"");
} else
other_player.SendMessageSuccess("You cleared your own inventory");
return false;
};
if (a_Split.size() < 2) {
if (!a_Player) {
a_Output->Out("Usage: " + a_Split[0] + " <player>");
} else
ClearInventory(*a_Player);
} else if (!a_Player || a_Player->HasPermission("core.admin.clear")) {
if (a_Split[1] == "" || !cRoot::Get()->FindAndDoWithPlayer(a_Split[1], ClearInventory))
CommandSendMessageFailure(a_Player, a_Output, "Player \"" + a_Split[1] + "\" not found");
}
return true;
}
bool HandleEffectCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() < 3) {
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <player> <effect> [seconds] [amplifier] OR " + a_Split[0] + " <player> clear");
return true;
}
bool all_players = a_Split[1] == "@a";
if (a_Split[2] != "clear") {
int effect_id = 0;
int seconds = 30;
int amplifier = 0;
bool invalid_effect = false;
try { // effect
effect_id = std::stoi(a_Split[2]);
} catch (...) {
invalid_effect = true;
}
if (invalid_effect || effect_id < 1 || effect_id > 23) {
CommandSendMessageFailure(a_Player, a_Output, "Invalid effect ID \"" + a_Split[2] + "\"");
return true;
}
if (a_Split.size() >= 4) { // duration
try {
seconds = std::stoi(a_Split[3]);
} catch (...) {
CommandSendMessageFailure(a_Player, a_Output, "Invalid duration \"" + a_Split[4] + "\"");
}
}
if (seconds < 0) {
CommandSendMessageFailure(a_Player, a_Output, "The duration in seconds must be at least 0");
return true;
} else if (seconds > 1000000) {
CommandSendMessageFailure(a_Player, a_Output, "The duration in seconds must be at most 1000000");
return true;
}
if (a_Split.size() >= 5) { //amplifier
try {
amplifier = std::stoi(a_Split[4]);
} catch (...) {
CommandSendMessageFailure(a_Player, a_Output, "Invalid amplification amount \"" + a_Split[4] + "\"");
return true;
}
}
if (amplifier < 0) {
CommandSendMessageFailure(a_Player, a_Output, "The amplification amount must be at least 0");
return true;
} else if (amplifier > 255) {
CommandSendMessageFailure(a_Player, a_Output, "The amplification amount must be at most 255");
return true;
}
auto ApplyEffect = [&](cPlayer& other_player) {
other_player.AddEntityEffect(static_cast<cEntityEffect::eType>(effect_id), seconds * 20, amplifier);
if (!all_players)
CommandSendMessageSuccess(a_Player, a_Output, "Successfully added effect to player \"" + other_player.GetName() + "\" for " + std::to_string(seconds) + " seconds");
return false;
};
if (all_players) {
cRoot::Get()->ForEachPlayer(ApplyEffect);
CommandSendMessageSuccess(a_Player, a_Output, "Successfully added effect to every player for " + std::to_string(seconds) + " seconds");
} else if (!cRoot::Get()->FindAndDoWithPlayer(a_Split[1], ApplyEffect))
CommandSendMessageFailure(a_Player, a_Output, "Player \"" + a_Split[1] + "\" not found");
} else {
auto RemoveEffects = [&](cPlayer& other_player) {
other_player.ClearEntityEffects();
if (!all_players)
CommandSendMessageSuccess(a_Player, a_Output, "Successfully removed effects from player \"" + other_player.GetName() + "\"");
return false;
};
if (all_players) {
cRoot::Get()->ForEachPlayer(RemoveEffects);
CommandSendMessageSuccess(a_Player, a_Output, "Successfully removed effects from every player");
} else if (!cRoot::Get()->FindAndDoWithPlayer(a_Split[1], RemoveEffects))
CommandSendMessageFailure(a_Player, a_Output, "Player \"" + a_Split[1] + "\" not found");
}
return true;
}
bool HandleKickCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() < 2) {
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <player> [reason ...]");
} else {
auto KickPlayer = [&](cPlayer& other_player) ->bool {
AString reason;
if (a_Split.size() >= 3)
reason = StringJoin(AStringVector(a_Split.begin() + 2, a_Split.end()), " ");
other_player.GetClientHandle()->Kick(!reason.empty() ? reason : "You have been kicked");
CommandSendMessageSuccess(a_Player, a_Output, "Successfully kicked player \"" + other_player.GetName() + "\"");
return true;
};
if (a_Split[2].empty() || !cRoot::Get()->FindAndDoWithPlayer(a_Split[1], KickPlayer))
CommandSendMessageFailure(a_Player, a_Output, "Player \"" + a_Split[1] + "\" not found");
}
return true;
}
bool HandleListCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
AStringVector player_table;
cRoot::Get()->ForEachPlayer([&](cPlayer& other_player) { player_table.push_back(other_player.GetName()); return true; });
std::sort(player_table.begin(), player_table.end());
CommandSendMessage(a_Player, a_Output, "Players (" + std::to_string(player_table.size()) + "): " + StringJoin(player_table, ", ") );
return true;
}
bool HandleRankCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() < 2) {
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <player> [rank]");
return true;
}
cUUID player_uuid = [](const AString& player_name) {
if (cRoot::Get()->GetServer()->ShouldAuthenticate()) {
// The server is in online-mode, get the UUID from Mojang servers and check for validity:
return cRoot::Get()->GetMojangAPI().GetUUIDFromPlayerName(player_name);
}
// The server is in offline mode, generate an offline-mode UUID, no validity check is possible:
return cClientHandle::GenerateOfflineUUID(player_name);
}(a_Split[1]);
if (player_uuid.IsNil() || player_uuid.ToShortString().size() != 32) {
CommandSendMessage(a_Player, a_Output, "There is no player with the name \"" + a_Split[1] + "\"");
return true;
}
// View the player's rank, if requested:
if (a_Split.size() < 3) {
const auto current_rank = cRoot::Get()->GetRankManager()->GetPlayerRankName(player_uuid);
if (current_rank.empty())
CommandSendMessage(a_Player, a_Output, "Player \"" + a_Split[1] + "\" has no rank assigned to them.");
else
CommandSendMessage(a_Player, a_Output, "The rank of player \"" + a_Split[1] + "\" is " + current_rank);
} else {
// Change the player's rank
if (!cRoot::Get()->GetRankManager()->RankExists(a_Split[2]))
CommandSendMessage(a_Player, a_Output, "The specified rank does not exist!");
else {
cRoot::Get()->GetRankManager()->SetPlayerRank(player_uuid, a_Split[1], a_Split[2]);
// Let the player know
cRoot::Get()->FindAndDoWithPlayer(a_Split[1], [&](cPlayer& other_player) {
if (a_Split[1] == other_player.GetName()) {
other_player.SendMessageInfo("You were assigned the rank " + a_Split[2] + " by " + ((a_Player) ? ("player \"" + a_Player->GetName() + "\"") : "the server console"));
other_player.LoadRank();
}
return true;
});
CommandSendMessageSuccess(a_Player, a_Output, "Player \"" + a_Split[1] + "\" is now in rank " + cRoot::Get()->GetRankManager()->GetPlayerRankName(player_uuid));
}
}
return true;
}
bool HandleListRanksCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
AStringVector ranks = cRoot::Get()->GetRankManager()->GetAllRanks();
CommandSendMessage(a_Player, a_Output, "Available ranks: " + StringJoin(ranks, (AString)cChatColor::Plain + ", ") + " (total: " + std::to_string(ranks.size()) + ")");
return true;
}
bool HandleListGroupsCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() > 2) {
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[1] + " [rank]");
return true;
}
if (a_Split.size() < 2) {
AStringVector groups = cRoot::Get()->GetRankManager()->GetAllGroups();
CommandSendMessage(a_Player, a_Output, "Available groups: " + StringJoin(groups, (AString)cChatColor::Plain + ", ") + " (total: " + std::to_string(groups.size()) + ")");
} else {
AStringVector groups = cRoot::Get()->GetRankManager()->GetRankGroups(a_Split[1]);
CommandSendMessage(a_Player, a_Output, "Groups in rank " + a_Split[1] + ": " + StringJoin(groups, (AString)cChatColor::Plain + ", ") + " (total: " + std::to_string(groups.size()) + ")");
}
return true;
}
bool HandleMeCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() < 2 || !a_Player)
CommandSendMessage(a_Player, a_Output, AString((a_Player) ? "" : "(only available to players) ") + "Usage: " + a_Split[0] + " <action ...>");
else
cRoot::Get()->BroadcastChat("* " + a_Player->GetName() + " " + StringJoin(AStringVector(a_Split.begin() + 1, a_Split.end()), " "));
return true;
}
bool HandleNumChunksCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
// List each world's chunk count into a table, sum the total chunk count:
std::size_t total = 0;
AStringVector response;
response.reserve(16);
cRoot::Get()->ForEachWorld([&](cWorld& world) {
std::size_t numchunks = world.GetNumChunks();
response.push_back(world.GetName() + ": " + std::to_string(numchunks) + " chunks");
total += numchunks;
return false;
});
std::sort(response.begin(), response.end());
response.insert(response.begin(), "Number of loaded chunks:");
response.push_back("Total: " + std::to_string(total) + " chunks");
// Return the complete report:
CommandSendMessage(a_Player, a_Output, StringJoin(response, "\n"));
return true;
}
bool HandleOpCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() < 2) {
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <player>");
return true;
}
AString admin_rank_name = [&]() {
const AStringVector ranks = cRoot::Get()->GetRankManager()->GetAllRanks();
for (const AString& r : ranks) {
const auto permissions = cRoot::Get()->GetRankManager()->GetRankPermissions(r);
for (const AString& p : permissions)
if (p == "*")
return r;
}
return AString();
}();
if (!admin_rank_name.empty())
return HandleRankCommand({"rank", a_Split[1], admin_rank_name}, a_Player, a_Command, a_Output);
CommandSendMessage(a_Player, a_Output, "No admin rank found, missing * permission");
return true;
}
bool HandlePlayersCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
AStringVector response;
response.reserve(16);
response.push_back("Players online:");
cRoot::Get()->ForEachWorld([&](cWorld& world) {
response.push_back("World " + world.GetName() + ":");
world.ForEachPlayer([&](cPlayer& other_player){
response.push_back(" " + other_player.GetName() + " @ " + other_player.GetIP());
return false;
});
return false;
});
CommandSendMessage(a_Player, a_Output, StringJoin(response, "\n"));
return true;
}
bool HandlePortalCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() > 2 || !a_Player)
CommandSendMessage(a_Player, a_Output, AString((a_Player) ? "" : "(only available to players) ") + "Usage: " + a_Split[0] + " [world]");
else if (a_Split.size() < 2)
CommandSendMessage(a_Player, a_Output, "You are in world \"" + a_Player->GetWorld()->GetName() + "\"");
else if (a_Player->GetWorld()->GetName() == a_Split[1])
CommandSendMessageFailure(a_Player, a_Output, "You are already in world \"" + a_Split[1] + "\"!");
else if (!cRoot::Get()->GetWorld(a_Split[1]))
CommandSendMessageFailure(a_Player, a_Output, "Could not find world \"" + a_Split[1] + "\"!");
else if (!a_Player->MoveToWorld(a_Split[1]))
CommandSendMessageFailure(a_Player, a_Output, "Could not move to world \"" + a_Split[1] + "\"!");
else
CommandSendMessageSuccess(a_Player, a_Output, "Successfully moved to world \"" + a_Split[1] + "\"! :D");
return true;
}
bool HandleSaveAllCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
cRoot::Get()->SaveAllChunks();
if (!a_Player)
CommandSendMessage(nullptr, a_Output, "Saving all worlds!");
else
cRoot::Get()->BroadcastChat((AString)cChatColor::Rose + "[WARNING] " + cChatColor::White + "Saving all worlds!");
return true;
}
bool HandleSayCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (a_Split.size() < 2)
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <message ...>");
else if (!a_Player)
cRoot::Get()->BroadcastChat(AString(cChatColor::Gold) + "[SERVER] " + cChatColor::Yellow + StringJoin(AStringVector(a_Split.begin() + 1, a_Split.end()), " "));
else
cRoot::Get()->BroadcastChat("[" + a_Player->GetName() + "] " + StringJoin(AStringVector(a_Split.begin() + 1, a_Split.end()), " "));
return true;
}
bool HandleSeedCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
cWorld* world = CommandGetWorld((a_Split.size() > 1) ? a_Split[1] : AString(), a_Player);
if (!world)
CommandSendMessage(a_Player, a_Output, "There is no world \"" + a_Split[1] + "\"");
else
CommandSendMessage(a_Player, a_Output, "Seed of world \"" + world->GetName() + "\": " + std::to_string(world->GetSeed()));
return true;
}
bool HandleSetSpawnCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
if (!a_Player) {
CommandSendMessage(nullptr, a_Output, "Command is for players only!");
return true;
}
int player_x = (int)a_Player->GetPosX(), player_y = (int)a_Player->GetPosY(), player_z = (int)a_Player->GetPosZ();
if (cWorld* world = a_Player->GetWorld(); world->SetSpawn(player_x, player_y, player_z)) {
CommandSendMessageSuccess(a_Player, a_Output, Printf("Successfully changed spawn position to [X:%i Y:%i Z:%i]", player_x, player_y, player_z));
} else
CommandSendMessageFailure(a_Player, a_Output, "Failed to change spawn position");
return true;
}
bool HandleSpawnCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
auto MoveToSpawn = [&](cPlayer& other_player) {
cWorld* world = other_player.GetWorld();
if (!world) {
CommandSendMessageFailure(a_Player, a_Output, "World for player \"" + a_Split[1] + "\" not found");
return false;
}
auto spawn_x = world->GetSpawnX(), spawn_y = world->GetSpawnY(), spawn_z = world->GetSpawnZ();
other_player.TeleportToCoords(spawn_x, spawn_y, spawn_z);
CommandSendMessageSuccess(a_Player, a_Output, (a_Split.size() < 2) ? "Successfully moved to world spawn"
: ("Successfully returned player \"" + other_player.GetName() + "\" to world spawn"));
return true;
};
if (a_Split.size() < 2) {
if (!a_Player) {
CommandSendMessage(nullptr, a_Output, "Usage: " + a_Split[0] + " <player>");
} else {
MoveToSpawn(*a_Player);
}
} else if (!a_Player || a_Player->HasPermission("core.spawn.others")) {
if (a_Split[1] == "" || !cRoot::Get()->FindAndDoWithPlayer(a_Split[1], MoveToSpawn)) {
CommandSendMessageFailure(a_Player, a_Output, "Player \"" + a_Split[1] + "\" not found");
}
}
return true;
}
bool HandleSpawnPointCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
int x, y, z;
auto world = cRoot::Get()->GetDefaultWorld();
if (a_Player) {
x = a_Player->GetPosX(), y = a_Player->GetPosY(), z = a_Player->GetPosZ();
world = a_Player->GetWorld();
}
AString response;
auto SetSpawnPoint = [&](cPlayer& other_player) {
if (a_Split.size() > 4) {
if (!a_Player)
x = other_player.GetPosX(), y = other_player.GetPosY(), z = other_player.GetPosZ();
try {
x = RelativeCommandCoord(a_Split[2], x);
} catch(...) {
response = "'" + a_Split[2] + "' is not a valid number";
}
try {
y = RelativeCommandCoord(a_Split[3], y);
} catch(...) {
response = "'" + a_Split[3] + "' is not a valid number";
}
try {
z = RelativeCommandCoord(a_Split[4], z);
} catch(...) {
response = "'" + a_Split[4] + "' is not a valid number";
}
}
if (a_Split.size() > 5) {
world = cRoot::Get()->GetWorld(a_Split[5]);
if (!world)
response = "Invalid world \"" + a_Split[5] + "\"";
}
if (response.empty()) {
assert(world); // unsure if needed...
other_player.SetBedPos(Vector3i(x, y, z), *world);
CommandSendMessageSuccess(a_Player, a_Output, "Set the spawn point of player \"" + other_player.GetName() + "\" to (" + std::to_string(std::floor(x)) + ", " + std::to_string(std::floor(y)) + ", " + std::to_string(std::floor(z)) + ") in world \"" + world->GetName() + "\"");
} else
CommandSendMessageFailure(a_Player, a_Output, response);
return true;
};
if ((a_Split.size() > 2 || !a_Player) && a_Split.size() < 5)
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <player> <x> <y> <z> [world]");
else if (a_Split.size() < 2)
SetSpawnPoint(*a_Player);
else if (a_Split[1] == "" || !cRoot::Get()->FindAndDoWithPlayer(a_Split[1], SetSpawnPoint))
CommandSendMessageFailure(a_Player, a_Output, "Player \"" + a_Split[1] + "\" not found");
return true;
}
bool HandleStopCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
cRoot::Get()->BroadcastChat((AString)cChatColor::Red + "[WARNING] " + cChatColor::White + "Server is terminating!");
cRoot::Get()->QueueExecuteConsoleCommand("stop");
return true;
}
bool HandleSummonCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output) {
static const std::map<AString, ENUM_ITEM_TYPE> minecarts = {
{"minecart", E_ITEM_MINECART},
{"chest_minecart", E_ITEM_CHEST_MINECART},
{"furnace_minecart", E_ITEM_FURNACE_MINECART},
{"hopper_minecart", E_ITEM_MINECART_WITH_HOPPER},
{"tnt_minecart", E_ITEM_MINECART_WITH_TNT},
// 1.10 and below
{"MinecartChest", E_ITEM_CHEST_MINECART},
{"MinecartFurnace", E_ITEM_FURNACE_MINECART},
{"MinecartHopper", E_ITEM_MINECART_WITH_HOPPER},
{"MinecartRideable", E_ITEM_MINECART},
{"MinecartTNT", E_ITEM_MINECART_WITH_TNT}
};
static const std::map<AString, eMonsterType> mobs = {
{"bat", mtBat},
{"blaze", mtBlaze},
{"cave_spider", mtCaveSpider},
{"chicken", mtChicken},
{"cow", mtCow},
{"creeper", mtCreeper},
{"ender_dragon", mtEnderDragon},
{"enderman", mtEnderman},
{"ghast", mtGhast},
{"giant", mtGiant},
{"guardian", mtGuardian},
{"horse", mtHorse},
{"iron_golem", mtIronGolem},
{"magma_cube", mtMagmaCube},
{"mooshroom", mtMooshroom},
{"ocelot", mtOcelot},
{"pig", mtPig},
{"rabbit", mtRabbit},
{"sheep", mtSheep},
{"silverfish", mtSilverfish},
{"skeleton", mtSkeleton},
{"slime", mtSlime},
{"snowman", mtSnowGolem},
{"spider", mtSpider},
{"squid", mtSquid},
{"villager", mtVillager},
{"witch", mtWitch},
{"wither", mtWither},
{"wither_skeleton", mtWitherSkeleton},
{"wolf", mtWolf},
{"zombie", mtZombie},
{"zombie_pigman", mtZombiePigman},
{"zombie_villager", mtZombieVillager},
// 1.10 and below
{"Bat", mtBat},
{"Blaze", mtBlaze},
{"CaveSpider", mtCaveSpider},
{"Chicken", mtChicken},
{"Cow", mtCow},
{"Creeper", mtCreeper},
{"EnderDragon", mtEnderDragon},
{"Enderman", mtEnderman},
{"Ghast", mtGhast},
{"Giant", mtGiant},
{"Guardian", mtGuardian},
{"Horse", mtHorse},
{"LavaSlime", mtMagmaCube},
{"MushroomCow", mtMooshroom},
{"Ozelot", mtOcelot},
{"Pig", mtPig},
{"Rabbit", mtRabbit},
{"Sheep", mtSheep},
{"Silverfish", mtSilverfish},
{"Skeleton", mtSkeleton},
{"Slime", mtSlime},
{"SnowMan", mtSnowGolem},
{"Spider", mtSpider},
{"Squid", mtSquid},
{"Villager", mtVillager},
{"VillagerGolem", mtIronGolem},
{"Witch", mtWitch},
{"Wither", mtWither},
{"WitherSkeleton", mtWitherSkeleton},
{"Wolf", mtWolf},
{"Zombie", mtZombie},
{"PigZombie", mtZombiePigman},
{"ZombieVillager", mtZombieVillager}
};
static const std::map<AString, cProjectileEntity::eKind> projectiles = {
{"arrow", cProjectileEntity::eKind::pkArrow},
{"egg", cProjectileEntity::eKind::pkEgg},
{"ender_pearl", cProjectileEntity::eKind::pkEnderPearl},
{"fireworks_rocket", cProjectileEntity::eKind::pkFirework},
{"fireball", cProjectileEntity::eKind::pkGhastFireball},
{"potion", cProjectileEntity::eKind::pkSplashPotion},
{"small_fireball", cProjectileEntity::eKind::pkFireCharge},
{"snowball", cProjectileEntity::eKind::pkSnowball},
{"wither_skull", cProjectileEntity::eKind::pkWitherSkull},
{"xp_bottle", cProjectileEntity::eKind::pkExpBottle},
// 1.10 and below
{"Arrow", cProjectileEntity::eKind::pkArrow},
{"Fireball", cProjectileEntity::eKind::pkGhastFireball},
{"FireworksRocketEntity", cProjectileEntity::eKind::pkFirework},
{"SmallFireball", cProjectileEntity::eKind::pkFireCharge},
{"Snowball", cProjectileEntity::eKind::pkSnowball},
{"ThrownEgg", cProjectileEntity::eKind::pkEgg},
{"ThrownEnderpearl", cProjectileEntity::eKind::pkEnderPearl},
{"ThrownExpBottle", cProjectileEntity::eKind::pkExpBottle},
{"ThrownPotion", cProjectileEntity::eKind::pkSplashPotion},
{"WitherSkull", cProjectileEntity::eKind::pkWitherSkull}
};
auto SpawnEntity = [&](const AString& entity_name, cWorld& world, double x, double y, double z) {
if (entity_name == "boat" || entity_name == "Boat")
world.SpawnBoat(Vector3d(x,y,z), cBoat::bmOak);
else if (entity_name == "falling_block" || entity_name == "FallingSand")
world.SpawnFallingBlock(Vector3i(x, y, z), E_BLOCK_SAND, 0);
else if (entity_name == "lightning_bolt" || entity_name == "LightningBolt")
world.CastThunderbolt(Vector3i(x,y,z));
else if (auto find = minecarts.find(entity_name); find != minecarts.end())
world.SpawnMinecart(Vector3d(x,y,z), find->second);
else if (auto find = mobs.find(entity_name); find != mobs.end())
world.SpawnMob(x, y, z, find->second);
else if (auto find = projectiles.find(entity_name); find != projectiles.end()) {
auto direction = a_Player->GetLookVector() * 20;
world.CreateProjectile(Vector3d(x,y,z), find->second, a_Player, &a_Player->GetEquippedItem(), &direction);
} else if (entity_name == "tnt" || entity_name == "PrimedTnt")
world.SpawnPrimedTNT(Vector3d(x,y,z));
else if (entity_name == "xp_orb" || entity_name == "XPOrb")
world.SpawnExperienceOrb(Vector3d(x,y,z), 1);
else if (entity_name == "ender_crystal" || entity_name == "EnderCrystal")
world.SpawnEnderCrystal(Vector3d(x,y,z), false);
else
return false;
return true;
};
if (a_Split.size() < 2 || (!a_Player && a_Split.size() < 5)) {
CommandSendMessage(a_Player, a_Output, "Usage: " + a_Split[0] + " <entityname> [x] [y] [z]");
return true;
}
double x, y, z;
cWorld* world = cRoot::Get()->GetDefaultWorld();
if (a_Player) {
x = a_Player->GetPosX(), y = a_Player->GetPosY(), z = a_Player->GetPosZ();
world = a_Player->GetWorld();
}
if (a_Split.size() > 4) {
try {
x = RelativeCommandCoord(a_Split[2], x);
} catch(...) {
CommandSendMessageFailure(a_Player, a_Output, "'" + a_Split[2] + "' is not a valid number");
return true;
}
try {
y = RelativeCommandCoord(a_Split[3], y);
} catch(...) {
CommandSendMessageFailure(a_Player, a_Output, "'" + a_Split[3] + "' is not a valid number");
return true;
}
try {
z = RelativeCommandCoord(a_Split[4], z);
} catch(...) {
CommandSendMessageFailure(a_Player, a_Output, "'" + a_Split[4] + "' is not a valid number");
return true;
}
}
assert(world); // idk, it seemed sus so this for saftey. works tho and clears with debug lol
if (SpawnEntity(a_Split[1], *world, x, y, z))
CommandSendMessageSuccess(a_Player, a_Output, "Successfully summoned entity at [X:" + std::to_string(std::floor(x)) + " Y:" + std::to_string(std::floor(y)) + " Z:" + std::to_string(std::floor(z)) + "]");
else
CommandSendMessageFailure(a_Player, a_Output, "Unknown entity '" + a_Split[1] + "'");
return true;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2011-2022 Cuberite Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "StringUtils.h"
class cPlayer;
class cCommandOutputCallback;
bool HandleClearCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleEffectCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleKickCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleListCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleRankCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleListRanksCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleListGroupsCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleMeCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleNumChunksCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleOpCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandlePlayersCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandlePortalCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSaveAllCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSayCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSeedCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSetSpawnCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSpawnCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSpawnPointCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleStopCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);
bool HandleSummonCommand(const AStringVector& a_Split, cPlayer* a_Player, const AString& a_Command, cCommandOutputCallback* a_Output);

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,9 @@
#include "RankManager.h"
#include "ChunkDef.h"
#ifdef NYQUBEL_CLIENT
#include "nyqubel-client/client.hpp"
#endif
// fwd:
class cItem;
@ -37,6 +38,7 @@ class cRecipeMapper;
class cFurnaceRecipe;
class cWebAdmin;
// class cPluginManager;
class cCMDManager;
class cServer;
class cWorld;
class cPlayer;
@ -51,19 +53,13 @@ class ProtocolPalettes;
using cPlayerListCallback = cFunctionRef<bool(cPlayer&)>;
using cWorldListCallback = cFunctionRef<bool(cWorld&)>;
namespace Json
{
namespace Json {
class Value;
}
/** The root of the object hierarchy */
// tolua_begin
class cRoot
{
class cRoot {
public:
static cRoot* Get() { return s_Root; }
// tolua_end
@ -91,8 +87,7 @@ public:
cWorld* GetWorld(const AString& a_WorldName);
/** Returns the up time of the server in seconds */
int GetServerUpTime(void)
{
int GetServerUpTime(void) {
return static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_StartTime).count());
}
// tolua_end
@ -125,6 +120,7 @@ public:
cWebAdmin* GetWebAdmin(void) { return m_WebAdmin; } // tolua_export
// cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cCMDManager* GetCMDManager(void) { return m_CMDManager; }
cAuthenticator& GetAuthenticator(void) { return m_Authenticator; }
cMojangAPI& GetMojangAPI(void) { return *m_MojangAPI; }
cRankManager* GetRankManager(void) { return m_RankManager.get(); }
@ -208,11 +204,9 @@ public:
// tolua_end
private:
/** States that the global cRoot can be in.
You can transition freely between Run and Restart, but the server unconditionally terminates in Stop. */
enum class NextState
{
enum class NextState {
Run,
Restart,
Stop
@ -240,6 +234,7 @@ private:
std::unique_ptr<cBrewingRecipes> m_BrewingRecipes;
cWebAdmin* m_WebAdmin;
// cPluginManager * m_PluginManager;
cCMDManager* m_CMDManager;
cAuthenticator m_Authenticator;
cMojangAPI* m_MojangAPI;
@ -250,7 +245,6 @@ private:
/** The storage for all registered block types. */
// BlockTypeRegistry m_BlockTypeRegistry;
void LoadGlobalSettings();
/** Loads the worlds from settings.ini, creates the worldmap */

View File

@ -39,20 +39,20 @@
#include <sstream>
#include <iostream>
#include "Commands/CMDManager.h"
////////////////////////////////////////////////////////////////////////////////
// cServerListenCallbacks:
class cServerListenCallbacks:
public cNetwork::cListenCallbacks
class cServerListenCallbacks : public cNetwork::cListenCallbacks
{
cServer & m_Server;
UInt16 m_Port;
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(
const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override
{
return m_Server.OnConnectionAccepted(a_RemoteIPAddress);
}
@ -61,13 +61,14 @@ class cServerListenCallbacks:
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
{
LOGWARNING("Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode, a_ErrorMsg.c_str());
LOGWARNING(
"Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode,
a_ErrorMsg.c_str());
}
public:
cServerListenCallbacks(cServer & a_Server, UInt16 a_Port) :
m_Server(a_Server),
m_Port(a_Port)
m_Server(a_Server), m_Port(a_Port)
{
}
};
@ -80,8 +81,7 @@ public:
// cServer::cTickThread:
cServer::cTickThread::cTickThread(cServer & a_Server) :
Super("Server Ticker"),
m_Server(a_Server)
Super("Server Ticker"), m_Server(a_Server)
{
}
@ -97,7 +97,9 @@ void cServer::cTickThread::Execute(void)
while (!m_ShouldTerminate)
{
auto NowTime = std::chrono::steady_clock::now();
auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(
NowTime - LastTime)
.count();
m_Server.Tick(static_cast<float>(msec));
auto TickTime = std::chrono::steady_clock::now() - NowTime;
@ -129,7 +131,8 @@ cServer::cServer(void) :
m_ShouldAuthenticate(false),
m_UpTime(0)
{
// Initialize the LuaStateTracker singleton before the app goes multithreaded:
// Initialize the LuaStateTracker singleton before the app goes
// multithreaded:
// cLuaStateTracker::GetStats();
}
@ -147,47 +150,52 @@ void cServer::ClientMovedToWorld(const cClientHandle * a_Client)
void cServer::PlayerCreated()
void cServer::PlayerCreated() { m_PlayerCount++; }
void cServer::PlayerDestroyed() { m_PlayerCount--; }
bool cServer::InitServer(
cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth)
{
m_PlayerCount++;
}
void cServer::PlayerDestroyed()
{
m_PlayerCount--;
}
bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth)
{
m_Description = a_Settings.GetValueSet("Server", "Description", "Cuberite - in C++!");
m_ShutdownMessage = a_Settings.GetValueSet("Server", "ShutdownMessage", "Server shutdown");
m_MaxPlayers = static_cast<size_t>(a_Settings.GetValueSetI("Server", "MaxPlayers", 100));
m_Description =
a_Settings.GetValueSet("Server", "Description", "Cuberite - in C++!");
m_ShutdownMessage =
a_Settings.GetValueSet("Server", "ShutdownMessage", "Server shutdown");
m_MaxPlayers = static_cast<size_t>(
a_Settings.GetValueSetI("Server", "MaxPlayers", 100));
m_bIsHardcore = a_Settings.GetValueSetB("Server", "HardcoreEnabled", false);
m_bAllowMultiLogin = a_Settings.GetValueSetB("Server", "AllowMultiLogin", false);
m_RequireResourcePack = a_Settings.GetValueSetB("Server", "RequireResourcePack", false);
m_bAllowMultiLogin =
a_Settings.GetValueSetB("Server", "AllowMultiLogin", false);
m_RequireResourcePack =
a_Settings.GetValueSetB("Server", "RequireResourcePack", false);
m_ResourcePackUrl = a_Settings.GetValueSet("Server", "ResourcePackUrl", "");
m_CustomRedirectUrl = a_Settings.GetValueSet("Server", "CustomRedirectUrl", "https://youtu.be/dQw4w9WgXcQ");
m_CustomRedirectUrl = a_Settings.GetValueSet(
"Server", "CustomRedirectUrl", "https://youtu.be/dQw4w9WgXcQ");
m_FaviconData = Base64Encode(cFile::ReadWholeFile(AString("favicon.png"))); // Will return empty string if file nonexistant; client doesn't mind
m_FaviconData = Base64Encode(cFile::ReadWholeFile(
AString("favicon.png"))); // Will return empty string if file
// nonexistant; client doesn't mind
if (m_bIsConnected)
{
LOGERROR("ERROR: Trying to initialize server while server is already running!");
LOGERROR("ERROR: Trying to initialize server while server is already "
"running!");
return false;
}
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
LOGD("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
m_Ports = ReadUpgradeIniPorts(a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565");
m_Ports = ReadUpgradeIniPorts(
a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565");
m_RCONServer.Initialize(a_Settings);
@ -208,33 +216,47 @@ bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_Shoul
}
// Check if both BungeeCord and online mode are on, if so, warn the admin:
m_ShouldAllowBungeeCord = a_Settings.GetValueSetB("Authentication", "AllowBungeeCord", false);
m_OnlyAllowBungeeCord = a_Settings.GetValueSetB("Authentication", "OnlyAllowBungeeCord", false);
m_ProxySharedSecret = a_Settings.GetValueSet("Authentication", "ProxySharedSecret", "");
m_ShouldAllowBungeeCord =
a_Settings.GetValueSetB("Authentication", "AllowBungeeCord", false);
m_OnlyAllowBungeeCord =
a_Settings.GetValueSetB("Authentication", "OnlyAllowBungeeCord", false);
m_ProxySharedSecret =
a_Settings.GetValueSet("Authentication", "ProxySharedSecret", "");
if (m_ShouldAllowBungeeCord && m_ShouldAuthenticate)
{
LOGWARNING("WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini.");
LOGWARNING("WARNING: BungeeCord is allowed and server set to online "
"mode. This is unsafe and will not work properly. Disable "
"either authentication or BungeeCord in settings.ini.");
}
if (m_ShouldAllowBungeeCord && m_ProxySharedSecret.empty())
{
LOGWARNING("WARNING: There is not a Proxy Forward Secret set up, and any proxy server can forward a player to this server unless closed from the internet.");
LOGWARNING("WARNING: There is not a Proxy Forward Secret set up, and "
"any proxy server can forward a player to this server "
"unless closed from the internet.");
}
m_ShouldAllowMultiWorldTabCompletion = a_Settings.GetValueSetB("Server", "AllowMultiWorldTabCompletion", true);
m_ShouldLimitPlayerBlockChanges = a_Settings.GetValueSetB("AntiCheat", "LimitPlayerBlockChanges", true);
m_ShouldAllowMultiWorldTabCompletion =
a_Settings.GetValueSetB("Server", "AllowMultiWorldTabCompletion", true);
m_ShouldLimitPlayerBlockChanges =
a_Settings.GetValueSetB("AntiCheat", "LimitPlayerBlockChanges", true);
const auto ClientViewDistance = a_Settings.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
const auto ClientViewDistance = a_Settings.GetValueSetI(
"Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
if (ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE)
{
m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE;
LOGINFO("Setting default view distance to the minimum of %d", m_ClientViewDistance);
LOGINFO(
"Setting default view distance to the minimum of %d",
m_ClientViewDistance);
}
else if (ClientViewDistance > cClientHandle::MAX_VIEW_DISTANCE)
{
m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE;
LOGINFO("Setting default view distance to the maximum of %d", m_ClientViewDistance);
LOGINFO(
"Setting default view distance to the maximum of %d",
m_ClientViewDistance);
}
else
{
@ -250,7 +272,9 @@ bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_Shoul
bool cServer::RegisterForgeMod(const AString & a_ModName, const AString & a_ModVersion, UInt32 a_ProtocolVersionNumber)
bool cServer::RegisterForgeMod(
const AString & a_ModName, const AString & a_ModVersion,
UInt32 a_ProtocolVersionNumber)
{
auto & Mods = RegisteredForgeMods(a_ProtocolVersionNumber);
@ -261,7 +285,8 @@ bool cServer::RegisterForgeMod(const AString & a_ModName, const AString & a_ModV
void cServer::UnregisterForgeMod(const AString & a_ModName, UInt32 a_ProtocolVersionNumber)
void cServer::UnregisterForgeMod(
const AString & a_ModName, UInt32 a_ProtocolVersionNumber)
{
auto & Mods = RegisteredForgeMods(a_ProtocolVersionNumber);
@ -331,10 +356,12 @@ void cServer::PrepareKeys(void)
cTCPLink::cCallbacksPtr cServer::OnConnectionAccepted(const AString & a_RemoteIPAddress)
cTCPLink::cCallbacksPtr
cServer::OnConnectionAccepted(const AString & a_RemoteIPAddress)
{
LOGD("Client \"%s\" connected!", a_RemoteIPAddress.c_str());
cClientHandlePtr NewHandle = std::make_shared<cClientHandle>(a_RemoteIPAddress, m_ClientViewDistance);
cClientHandlePtr NewHandle = std::make_shared<cClientHandle>(
a_RemoteIPAddress, m_ClientViewDistance);
cCSLock Lock(m_CSClients);
m_Clients.push_back(NewHandle);
return NewHandle;
@ -349,7 +376,8 @@ void cServer::Tick(float a_Dt)
// Update server uptime
m_UpTime++;
// Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102):
// Send the tick to the plugins, as well as let the plugin manager reload,
// if asked to (issue #102):
// cPluginManager::Get()->Tick(a_Dt);
// Process all the queued commands:
@ -372,10 +400,14 @@ void cServer::TickClients(float a_Dt)
{
cCSLock Lock(m_CSClients);
// Remove clients that have moved to a world (the world will be ticking them from now on)
for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
// Remove clients that have moved to a world (the world will be ticking
// them from now on)
for (auto itr = m_ClientsToRemove.begin(),
end = m_ClientsToRemove.end();
itr != end; ++itr)
{
for (auto itrC = m_Clients.begin(), endC = m_Clients.end(); itrC != endC; ++itrC)
for (auto itrC = m_Clients.begin(), endC = m_Clients.end();
itrC != endC; ++itrC)
{
if (itrC->get() == *itr)
{
@ -386,7 +418,8 @@ void cServer::TickClients(float a_Dt)
} // for itr - m_ClientsToRemove[]
m_ClientsToRemove.clear();
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
// Tick the remaining clients, take out those that have been destroyed
// into RemoveClients
for (auto itr = m_Clients.begin(); itr != m_Clients.end();)
{
auto & Client = *itr;
@ -394,7 +427,8 @@ void cServer::TickClients(float a_Dt)
Client->ServerTick(a_Dt);
if (Client->IsDestroyed())
{
// Delete the client later, when CS is not held, to avoid deadlock: https://forum.cuberite.org/thread-374.html
// Delete the client later, when CS is not held, to avoid
// deadlock: https://forum.cuberite.org/thread-374.html
RemoveClients.push_back(std::move(Client));
itr = m_Clients.erase(itr);
continue;
@ -419,10 +453,13 @@ bool cServer::Start(void)
UInt16 PortNum;
if (!StringToInteger(port, PortNum))
{
LOGWARNING("Invalid port specified for server: \"%s\". Ignoring.", port.c_str());
LOGWARNING(
"Invalid port specified for server: \"%s\". Ignoring.",
port.c_str());
continue;
}
auto Handle = cNetwork::Listen(PortNum, std::make_shared<cServerListenCallbacks>(*this, PortNum));
auto Handle = cNetwork::Listen(
PortNum, std::make_shared<cServerListenCallbacks>(*this, PortNum));
if (Handle->IsListening())
{
m_ServerHandles.push_back(Handle);
@ -444,12 +481,51 @@ bool cServer::Start(void)
bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd)
{
bool Res = cRoot::Get()->DoWithPlayerByUUID(
a_Client.GetUUID(),
[&](cPlayer & a_Player)
a_Client.GetUUID(), [&](cPlayer & a_Player) {
// Check if the message contains a command, execute it
switch (cRoot::Get()->GetCMDManager()->HandleCommand(
a_Player, a_Cmd, true))
{
return false; //cRoot::Get()->GetPluginManager()->CallHookChat(a_Player, a_Cmd);
case cCMDManager::CommandResult::crExecuted: {
// The command has executed successfully
return true;
}
);
/*case crBlocked:
{
// The command was blocked by a plugin using
HOOK_EXECUTE_COMMAND
// The plugin has most likely sent a message to the
player already return true;
}*/
case cCMDManager::CommandResult::crError: {
// An error in the plugin has prevented the command from
// executing. Report the error to the player:
a_Player.SendMessageFailure(Printf(
"Something went wrong while executing command \"%s\"",
a_Cmd.c_str()));
return true;
}
case cCMDManager::CommandResult::crNoPermission: {
// The player is not allowed to execute this command
a_Player.SendMessageFailure(Printf(
"Forbidden command; insufficient privileges: \"%s\"",
a_Cmd.c_str()));
return true;
}
case cCMDManager::CommandResult::crUnknownCommand: {
// This was not a known command, keep processing as a
// message
break;
}
}
return false; // cRoot::Get()->GetPluginManager()->CallHookChat(a_Player,
// a_Cmd); // replaced with above.
});
return Res;
}
@ -457,7 +533,8 @@ bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd)
void cServer::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
void cServer::QueueExecuteConsoleCommand(
const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands);
@ -468,7 +545,8 @@ void cServer::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCa
void cServer::ScheduleTask(cTickTime a_DelayTicks, std::function<void(cServer &)> a_Task)
void cServer::ScheduleTask(
cTickTime a_DelayTicks, std::function<void(cServer &)> a_Task)
{
const auto TargetTick = a_DelayTicks + m_UpTime;
// Insert the task into the list of scheduled tasks
@ -482,7 +560,8 @@ void cServer::ScheduleTask(cTickTime a_DelayTicks, std::function<void(cServer &)
void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
void cServer::ExecuteConsoleCommand(
const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
AStringVector split = StringSplit(a_Cmd, " ");
if (split.empty())
@ -490,9 +569,11 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
return;
}
// "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
// "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our
// caller, due to its access to controlling variables
// "help" and "reload" are to be handled by Cuberite, so that they work no matter what
// "help" and "reload" are to be handled by Cuberite, so that they work no
// matter what
if (split[0] == "help")
{
PrintHelp(split, a_Output);
@ -524,8 +605,10 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
if (split.size() > 1)
{
cPluginManager::Get()->RefreshPluginList(); // Refresh the plugin list, so that if the plugin was added just now, it is loadable
a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
cPluginManager::Get()->RefreshPluginList(); // Refresh the plugin
list, so that if the plugin was added just now, it is loadable
a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin
loaded" : "Error occurred loading plugin");
}
else
{
@ -550,26 +633,23 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
}*/
if (split[0] == "destroyentities")
{
cRoot::Get()->ForEachWorld([](cWorld & a_World)
{
a_World.ForEachEntity([](cEntity & a_Entity)
{
cRoot::Get()->ForEachWorld([](cWorld & a_World) {
a_World.ForEachEntity([](cEntity & a_Entity) {
if (!a_Entity.IsPlayer())
{
a_Entity.Destroy();
}
return false;
}
);
});
return false;
}
);
});
a_Output.Out("Destroyed all entities");
a_Output.Finished();
return;
}
// There is currently no way a plugin can do these (and probably won't ever be):
// There is currently no way a plugin can do these (and probably won't ever
// be):
else if (split[0].compare("chunkstats") == 0)
{
cRoot::Get()->LogChunkStats(a_Output);
@ -583,11 +663,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
a_Output.Finished();
return;
}*/
/*else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output, a_Cmd))
else if (cRoot::Get()->GetCMDManager()->HandleConsoleCommand(
split, a_Output, a_Cmd))
{
a_Output.Finished();
return;
}*/
}
a_Output.Out("Unknown command, type 'help' for all commands.");
a_Output.Finished();
@ -597,42 +678,36 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
void cServer::PrintHelp(
const AStringVector & a_Split, cCommandOutputCallback & a_Output)
{
UNUSED(a_Split);
typedef std::pair<AString, AString> AStringPair;
typedef std::vector<AStringPair> AStringPairs;
/*class cCallback :
public cPluginManager::cCommandEnumCallback
std::size_t m_MaxLen = 0;
AStringPairs command_pairs;
for (const auto & i : cRoot::Get()->GetCMDManager()->m_ConsoleCommands)
{
public:
cCallback(void) : m_MaxLen(0) {}
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
if (!i.second.m_HelpString.empty())
{
UNUSED(a_Plugin);
UNUSED(a_Permission);
if (!a_HelpString.empty())
command_pairs.push_back(
AStringPair(i.first, i.second.m_HelpString));
if (m_MaxLen < i.first.length())
{
m_Commands.push_back(AStringPair(a_Command, a_HelpString));
if (m_MaxLen < a_Command.length())
{
m_MaxLen = a_Command.length();
m_MaxLen = i.first.length();
}
}
return false;
}
AStringPairs m_Commands;
size_t m_MaxLen;
} Callback;
cPluginManager::Get()->ForEachConsoleCommand(Callback);
std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end());
for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr)
for (AStringPairs::const_iterator itr = command_pairs.begin(),
end = command_pairs.end();
itr != end; ++itr)
{
const AStringPair & cmd = *itr;
a_Output.Out(Printf("%-*s - %s\n", static_cast<int>(Callback.m_MaxLen), cmd.first.c_str(), cmd.second.c_str()));
a_Output.Out(Printf(
"%-*s - %s\n", static_cast<int>(m_MaxLen), cmd.first.c_str(),
cmd.second.c_str()));
} // for itr - Callback.m_Commands[]*/
}
@ -642,7 +717,8 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback &
void cServer::BindBuiltInConsoleCommands(void)
{
// Create an empty handler - the actual handling for the commands is performed before they are handed off to cPluginManager
// Create an empty handler - the actual handling for the commands is
// performed before they are handed off to cPluginManager
/*class cEmptyHandler:
public cPluginManager::cCommandHandler
{
@ -660,15 +736,19 @@ void cServer::BindBuiltInConsoleCommands(void)
// Register internal commands:
cPluginManager * PlgMgr = cPluginManager::Get();
PlgMgr->BindConsoleCommand("help", nullptr, handler, "Shows the available commands");
PlgMgr->BindConsoleCommand("reload", nullptr, handler, "Reloads all plugins");
PlgMgr->BindConsoleCommand("reloadweb", nullptr, handler, "Reloads the webadmin configuration");
PlgMgr->BindConsoleCommand("restart", nullptr, handler, "Restarts the server cleanly");
PlgMgr->BindConsoleCommand("stop", nullptr, handler, "Stops the server cleanly");
PlgMgr->BindConsoleCommand("chunkstats", nullptr, handler, "Displays detailed chunk memory statistics");
PlgMgr->BindConsoleCommand("load", nullptr, handler, "Adds and enables the specified plugin");
PlgMgr->BindConsoleCommand("unload", nullptr, handler, "Disables the specified plugin");
PlgMgr->BindConsoleCommand("destroyentities", nullptr, handler, "Destroys all entities in all worlds");*/
PlgMgr->BindConsoleCommand("help", nullptr, handler, "Shows the
available commands"); PlgMgr->BindConsoleCommand("reload", nullptr,
handler, "Reloads all plugins"); PlgMgr->BindConsoleCommand("reloadweb",
nullptr, handler, "Reloads the webadmin configuration");
PlgMgr->BindConsoleCommand("restart", nullptr, handler, "Restarts
the server cleanly"); PlgMgr->BindConsoleCommand("stop", nullptr,
handler, "Stops the server cleanly");
PlgMgr->BindConsoleCommand("chunkstats", nullptr, handler, "Displays
detailed chunk memory statistics"); PlgMgr->BindConsoleCommand("load",
nullptr, handler, "Adds and enables the specified plugin");
PlgMgr->BindConsoleCommand("unload", nullptr, handler, "Disables
the specified plugin"); PlgMgr->BindConsoleCommand("destroyentities",
nullptr, handler, "Destroys all entities in all worlds");*/
}
@ -687,7 +767,8 @@ void cServer::Shutdown(void)
// Notify the tick thread and wait for it to terminate:
m_TickThread.Stop();
// Save all chunks in all worlds, wait for chunks to be sent to the ChunkStorage queue for each world:
// Save all chunks in all worlds, wait for chunks to be sent to the
// ChunkStorage queue for each world:
cRoot::Get()->SaveAllChunksNow();
// Remove all clients:
@ -719,14 +800,20 @@ void cServer::KickUser(int a_ClientID, const AString & a_Reason)
void cServer::AuthenticateUser(int a_ClientID, AString && a_Username, const cUUID & a_UUID, Json::Value && a_Properties)
void cServer::AuthenticateUser(
int a_ClientID, AString && a_Username, const cUUID & a_UUID,
Json::Value && a_Properties)
{
cCSLock Lock(m_CSClients);
// Check max players condition within lock (expect server and authenticator thread to both call here)
// Check max players condition within lock (expect server and authenticator
// thread to both call here)
if (GetNumPlayers() >= GetMaxPlayers())
{
KickUser(a_ClientID, "The server is currently full :(" "\n" "Try again later?");
KickUser(
a_ClientID, "The server is currently full :("
"\n"
"Try again later?");
return;
}
@ -734,7 +821,8 @@ void cServer::AuthenticateUser(int a_ClientID, AString && a_Username, const cUUI
{
if (Client->GetUniqueID() == a_ClientID)
{
Client->Authenticate(std::move(a_Username), a_UUID, std::move(a_Properties));
Client->Authenticate(
std::move(a_Username), a_UUID, std::move(a_Properties));
return;
}
}
@ -779,8 +867,7 @@ void cServer::TickQueuedTasks(void)
// of list if time reached
auto MoveBeginIterator = std::partition(
m_Tasks.begin(), m_Tasks.end(),
[this](const decltype(m_Tasks)::value_type & a_Task)
{
[this](const decltype(m_Tasks)::value_type & a_Task) {
return a_Task.first >= m_UpTime;
});

View File

@ -0,0 +1,14 @@
if(BUILD_CLIENT)
target_sources(
${CMAKE_PROJECT_NAME} PRIVATE
client.cpp
client.hpp
util/geometry.cpp
util/geometry.hpp
)
target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC "-DNYQUBEL_CLIENT=1")
endif()

View File

@ -0,0 +1,953 @@
/*
* Libpdw: Primitives Done Well!
* Copyright (C) 2022 Rebekah Rowe
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <bitset>
#include <chrono>
#include <iostream>
#include <stdexcept>
#include <X11/Xutil.h>
#include <embed_resources.hpp>
#include <glez/detail/render.hpp>
#include <glez/color.hpp>
#include <glez/draw.hpp>
#include <glez/glez.hpp>
#include <hydride.h>
// xorg conflict
#undef RootWindow
#include "libpdw/gui/widgets/slider.hpp"
#include "libpdw/gui/widgets/titlebar.hpp"
#include "libpdw/gui/canvas.hpp"
#include "libpdw/gui/widgets/basebutton.hpp"
#include "libpdw/gui/widgets/basewindow.hpp"
#include "libpdw/gui/widgets/checkbox.hpp"
#include "libpdw/gui/widgets/dropdown.hpp"
#include "libpdw/gui/ncc/logo.hpp"
namespace x11 {
using RawKey = decltype(XK_2);
constexpr std::pair<std::string_view, RawKey> key_dict[] = { { "0", XK_0 }, { "1", XK_1 }, { "2", XK_2 },
{ "3", XK_3 }, { "4", XK_4 }, { "5", XK_5 },
{ "6", XK_6 }, { "7", XK_7 }, { "8", XK_8 },
{ "9", XK_9 }, { "a", XK_A }, { "b", XK_B },
{ "c", XK_C }, { "d", XK_D }, { "e", XK_E },
{ "f", XK_F }, { "g", XK_G }, { "h", XK_H },
{ "i", XK_I }, { "j", XK_J }, { "k", XK_K },
{ "l", XK_L }, { "m", XK_M }, { "n", XK_N },
{ "o", XK_O }, { "p", XK_P }, { "q", XK_Q },
{ "r", XK_R }, { "s", XK_S }, { "t", XK_T },
{ "u", XK_U }, { "v", XK_V }, { "w", XK_W },
{ "x", XK_X }, { "y", XK_Y }, { "z", XK_Z },
{ "escape", XK_Escape }, { "{", XK_bracketleft },
{ "}", XK_bracketright }, { ";", XK_semicolon },
{ "'", XK_apostrophe }, { ",", XK_apostrophe },
{ ".", XK_period }, { "/", XK_slash },
{ "\\", XK_backslash }, { "minus", XK_minus },
{ "=", XK_equal }, { "return", XK_Return },
{ "space", XK_space }, { "backspace", XK_BackSpace },
{ "tab", XK_Tab }, { "capslock", XK_Caps_Lock },
{ "insert", XK_Insert },
{ "delete", XK_Delete },
{ "home", XK_Home }, { "end", XK_End },
{ "pageup", XK_Page_Up }, { "pagedown", XK_Page_Down },
{ "shift_r", XK_Shift_L }, { "shift_r", XK_Shift_R },
{ "alt_l", XK_Alt_L }, { "alt_r", XK_Alt_R },
{ "control_l", XK_Control_L }, { "control_l", XK_Control_R },
{ "0_pad", XK_KP_0 }, { "1_pad", XK_KP_1 }, { "2_pad", XK_KP_2 },
{ "3_pad", XK_KP_3 }, { "4_pad", XK_KP_4 }, { "5_pad", XK_KP_5 },
{ "6_pad", XK_KP_6 }, { "7_pad", XK_KP_7 }, { "8_pad", XK_KP_8 },
{ "9_pad", XK_KP_9 },
{ "/_pad", XK_KP_Divide }, { "*_pad", XK_KP_Multiply },
{ "-_pad", XK_KP_Subtract }, { "+_pad", XK_KP_Add },
{ "enter_pad", XK_KP_Enter }, { "._pad", XK_KP_Decimal },
{ "up", XK_Up }, { "left", XK_Left },
{ "down", XK_Down }, { "right", XK_Right },
{ "f1", XK_F1 }, { "f2", XK_F2 }, { "f3", XK_F3 },
{ "f4", XK_F4 }, { "f5", XK_F5 }, { "f6", XK_F6 },
{ "f7", XK_F7 }, { "f8", XK_F8 }, { "f9", XK_F9 },
{ "f10", XK_F10 }, { "f11", XK_F11 }, { "f12", XK_F12 },
//{ "mouse_1", XK_Pointer_DfltBtnPrev },
//{ "mouse_2", XK_Pointer_DfltBtnNext }
{ "mouse_wheel_up", XK_Pointer_DfltBtnPrev },
{ "mouse_wheel_down", XK_Pointer_DfltBtnNext } };
constexpr auto key_dict_size = []() {
std::size_t ret = 0;
for (auto i : key_dict)
ret++;
return ret;
}();
std::pair<std::pair<int, int>, std::bitset<3>> QueryPointer(decltype(hydride_library.display) dis, decltype(hydride_library.window) win) {
Window root_return, child_return;
std::pair<int, int> mouse, root;
unsigned int mask_return;
if (!XQueryPointer(hydride_library.display, hydride_library.window, &root_return, &child_return, &root.first, &root.second, &mouse.first, &mouse.second, &mask_return))
throw std::runtime_error("Unable to query pointer from X11");
std::bitset<3> ret_buttons;
ret_buttons[0] = (mask_return & (Button1Mask));
ret_buttons[1] = (mask_return & (Button2Mask));
ret_buttons[2] = (mask_return & (Button3Mask));
return { mouse, ret_buttons };
}
using Keymap = std::array<char, 32>;
bool PickFromKeymap(decltype(hydride_library.display) dis, Keymap map, decltype(XK_2) k) {
int current_key = XKeysymToKeycode(dis, k);
return (map[current_key / 8] & (1 << (current_key % 8)));
}
Keymap QueryKeymap(decltype(hydride_library.display) dis) {
Keymap keys;
XQueryKeymap(dis, keys.data());
return keys;
}
bool QueryKey(decltype(hydride_library.display) dis, decltype(XK_2) k) {
auto map = QueryKeymap(dis);
return PickFromKeymap(dis, map, k);
}
template <int size>
class ChangeDetector { // generate async from sync
std::pair<int, int> previous_mouse { -1, -1 };
std::pair<int, int> previous_bounds { -1, -1 };
std::bitset<size> previous_states;
public:
ChangeDetector() {
this->previous_states.reset();
}
bool UpdateKey(std::size_t k, bool state) {
if (state == previous_states[k]) // Just to prevent issues, idk if we need this at runtime ;-;
return false;
previous_states.set(k, state);
return true;
}
decltype(previous_mouse) UpdateMouse(const decltype(previous_mouse)& state) {
if (state == previous_mouse) // Just to prevent issues, idk if we need this at runtime ;-;
return { 0, 0 };
decltype(previous_mouse) delta = { state.first - previous_mouse.first, state.second - previous_mouse.second };
previous_mouse = state;
return delta;
}
decltype(previous_bounds) UpdateBounds(const decltype(previous_bounds)& state) {
if (state == previous_bounds) // Just to prevent issues, idk if we need this at runtime ;-;
return { 0, 0 };
decltype(previous_bounds) delta = { state.first - previous_bounds.first, state.second - previous_bounds.second };
previous_bounds = state;
return delta;
}
};
class X11Poller {
ChangeDetector<key_dict_size> async_gen;
decltype(hydride_library.display) display;
decltype(hydride_library.window) window;
public:
X11Poller(decltype(hydride_library.display) display, decltype(hydride_library.window) window)
: display(display)
, window(window) {
UpdateKeys();
UpdateMouse();
}
std::vector<std::pair<std::string_view, bool>> UpdateKeys() {
auto km = QueryKeymap(display);
std::vector<std::pair<std::string_view, bool>> ret;
for (std::size_t i = 0; i < key_dict_size; i++) {
bool key_state = PickFromKeymap(display, km, key_dict[i].second);
if (async_gen.UpdateKey(i, key_state))
ret.push_back({ key_dict[i].first, key_state });
}
return ret;
}
std::tuple<std::pair<int, int>, std::pair<int, int>, std::bitset<3>> UpdateMouse() {
auto pointer_info = QueryPointer(this->display, this->window);
auto delta = async_gen.UpdateMouse(pointer_info.first);
return { pointer_info.first, delta, pointer_info.second };
}
};
} // namespace x11
#include "util/geometry.hpp"
namespace qubel {
enum VoxelTypes {
kAir,
kStone,
kDirt,
kSand,
kGrass
};
enum CardinalDirections {
kNorth,
kSouth,
kEast,
kWest // ,
/*kUp,
kDown*/
};
class Chunk {
public:
// "typedefs" or templates go here lol
static constexpr uint _dim_length = 2; // its in 2d!
static constexpr uint _size_in_power = 16; // 8x8 size
static constexpr std::size_t _chunk_array_size = _size_in_power * _size_in_power;
using BlockType = uint8_t;
// Using a single digit to locate the exact voxel's byte in the chunk, for very fast use.
using ChunkDataType = std::array<uint8_t, _chunk_array_size>;
using ChunkVecType = geo::internal::BVec2<uint8_t>;
ChunkDataType raw_chunk_data = {
kStone,
kStone,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kStone,
kStone,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kGrass,
kGrass,
kGrass,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kGrass,
kDirt,
kDirt,
kDirt,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kGrass,
kGrass,
kDirt,
kDirt,
kDirt,
kDirt,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kSand,
kSand,
kDirt,
kDirt,
kDirt,
kStone,
kStone,
kStone,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kSand,
kSand,
kDirt,
kDirt,
kStone,
kStone,
kStone,
kStone,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kAir,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kAir,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kSand,
kAir,
kAir,
kAir,
kSand,
kAir,
kSand,
kAir,
kAir,
kSand,
kAir,
kSand,
kAir,
kAir,
kSand,
kAir,
kSand,
kAir,
kAir,
kAir,
kAir,
kSand,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kSand,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kAir,
kStone,
};
class LocalVoxel {
public:
std::size_t pos; // yes a single integer, x y and z can all exist due to the way chunking keeps it localized and x + (z * 64) means the values co-exist.
// LocalVoxel(geo::internal::BVec2<uint8_t> raw_chunk_pos)
//: pos(raw_chunk_pos) { }
static LocalVoxel GetVoxelFromPosInChunk(geo::internal::BVec2<uint8_t> in_pos) { // basicly your 2d/3d position to a index, make sure ur in the chunk! lol
assert(in_pos.x < _size_in_power && in_pos.y < _size_in_power);
LocalVoxel index;
index.pos = 0;
return index.AddPos(in_pos);
}
LocalVoxel& AddPos(geo::internal::BVec2<uint8_t> in_pos) {
this->pos += in_pos.x;
this->pos += in_pos.y * _size_in_power;
// if constexpr (_dim_length >= 3) // We gotta move these to a seperate class all-together... do this once the templates are split.
// index.pos += in_pos.z * (_size_in_power * _size_in_power);
return *this;
}
ChunkVecType ExtractVec() {
ChunkVecType ret_pos;
ret_pos.x = this->pos % _size_in_power;
ret_pos.y = (this->pos / _size_in_power) % _size_in_power;
// ret_pos.z = ((this->pos / _size_in_power) / _size_in_power) % _size_in_power;
return ret_pos;
}
// tmp, TODO replace/add with chunk pointer or chunk x and y... both??? oof if so
// need good way to keep the handle associated.
// Local HANDLES AND "standard voxel"/global voxel in other words one for chunk and one inheriting with the extra details? ah genious!
};
static LocalVoxel GetVoxelFromPosInChunk(geo::internal::BVec2<uint8_t> in_pos) {
return LocalVoxel::GetVoxelFromPosInChunk(in_pos);
}
BlockType GetVoxelType(LocalVoxel v) {
return raw_chunk_data[v.pos];
}
class AxisIterator {
public:
bool end_flagged = false;
ChunkVecType cur;
using VecMaskType = glm::bvec2;
const VecMaskType mask, reverse_mask; // stick it in reverse spongebob!
constexpr AxisIterator(const ChunkVecType& src, glm::bvec2 mask, glm::bvec2 reverse_mask = { false, false })
: cur(src)
, mask(mask)
, reverse_mask(reverse_mask) { }
AxisIterator& operator++(int) {
// new_iter.cur.AddPos(this->GetPosToAdd()); we cant do this cause we have to test it ;-;
this->cur += this->GetPosToAdd();
if (!IsPosValidWithinChunk(this->cur))
this->end_flagged = true;
return *this;
}
bool operator==(const AxisIterator& other) const {
if (other.end_flagged)
return this->end_flagged;
return other.cur == this->cur && other.mask == this->mask && other.reverse_mask == this->reverse_mask;
}
bool operator!=(const AxisIterator& other) const {
return !(*this == other);
}
static constexpr AxisIterator end() {
AxisIterator ret({}, {});
ret.end_flagged = true;
return ret;
}
ChunkVecType GetPosToAdd() const {
ChunkVecType pos_to_add = { 0, 0 };
if (mask.x)
pos_to_add.x = 1;
if (mask.y)
pos_to_add.y = 1;
if (reverse_mask.x && pos_to_add.x)
pos_to_add.x = -pos_to_add.x;
if (reverse_mask.y && pos_to_add.y)
pos_to_add.y = -pos_to_add.y;
return pos_to_add;
}
static constexpr auto CreateCardinalMask(const CardinalDirections dir) {
VecMaskType mask = { false, false }, reverse_mask = { false, false };
switch (dir) {
case CardinalDirections::kNorth:
reverse_mask.y = true;
case CardinalDirections::kSouth:
mask.y = true;
break;
case CardinalDirections::kWest:
reverse_mask.x = true;
case CardinalDirections::kEast:
mask.x = true;
break;
default:
throw std::logic_error("Trying to access a cardinal Direction that does not work!");
}
return std::make_pair(mask, reverse_mask);
}
};
enum MesherComplexity {
kPlain,
kNieve,
kGreedy, // Just expand same type faces
kIntrinsicGreedy, // Using bitwise operations :O
kGlobalLattice // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
};
static constexpr float box_size = 25.0f;
// glez::vertex not verbose enough for us sadly...
struct ChunkMeshedQuad {
geo::Box<geo::Vec2> quad;
glez::rgba clr;
};
static constexpr glez::rgba chunk_textures[] = {
glez::rgba(0, 0, 0, 0),
glez::rgba(40, 40, 40),
glez::rgba(120, 40, 40),
glez::color::yellow,
glez::color::green
};
std::vector<ChunkMeshedQuad> MeshChunk(MesherComplexity cmplxity) {
std::vector<ChunkMeshedQuad> finished_quads;
std::cout << "Start Meshing: ";
auto start_time = std::chrono::high_resolution_clock::now();
switch (cmplxity) {
case MesherComplexity::kNieve: // I dont think this is possible in 2d for the time being...
case MesherComplexity::kPlain: {
std::cout << "Plain" << std::endl;
for (uint dim_x = 0; dim_x < _size_in_power; dim_x++) {
for (uint dim_y = 0; dim_y < _size_in_power; dim_y++) {
LocalVoxel cube = GetVoxelFromPosInChunk({ dim_x, dim_y });
auto type = this->GetVoxelType(cube);
if (type == VoxelTypes::kAir)
continue;
ChunkMeshedQuad new_face;
auto start = geo::Vec2(dim_x * box_size, dim_y * box_size);
// auto end = /*start +*/ geo::Vec2(box_size, box_size);
auto size = /*start +*/ geo::Vec2(box_size, box_size);
new_face.quad = geo::Box<geo::Vec2>(start, size);
new_face.clr = chunk_textures[type];
finished_quads.push_back(new_face);
std::cout << " *Added quad! : {" << start.x << ", " << start.y << "} with size {" << size.x << ", " << size.y << "}" << std::endl;
}
}
std::cout << "Plain ";
break;
}
/*case MesherComplexity::kNieve: {
} */
case MesherComplexity::kGreedy: {
std::cout << "Greedy" << std::endl;
std::bitset<_chunk_array_size> chunk_finished_checksum;
auto FindOpenSpace = [&]() -> std::pair<std::size_t, std::optional<decltype(chunk_finished_checksum)::reference>> {
for (std::size_t x = 0; x < _size_in_power; x++) {
for (std::size_t y = 0; y < _size_in_power; y++) {
auto vox = this->GetVoxelFromPosInChunk({ x, y });
if (!chunk_finished_checksum[vox.pos])
return { vox.pos, chunk_finished_checksum[vox.pos] };
}
}
return { std::size_t(-1), {} };
};
while (!chunk_finished_checksum.all()) {
auto unmeshed_face = FindOpenSpace();
if (!unmeshed_face.second.has_value())
throw std::logic_error("Greedy ChunkMesher checksum thinks there is a unmeshed chunk but unable to find it!");
LocalVoxel cube;
cube.pos = unmeshed_face.first;
auto type = this->GetVoxelType(cube);
if (type == VoxelTypes::kAir) {
chunk_finished_checksum[cube.pos] = true;
continue;
}
{
auto ext_cube = cube.ExtractVec();
std::cout << "Greedy found Seed block \"" << (int)type << "\": {" << (int)ext_cube.x << ", " << (int)ext_cube.y << "}" << std::endl;
}
ChunkVecType block_quad_size = { 1, 1 };
auto src_block_loc = cube.ExtractVec();
// Need a better "search line down" func... instead of one block we need to check multiple and return how many are the same.
constexpr auto south_dir_mask = AxisIterator::CreateCardinalMask(CardinalDirections::kSouth);
auto SearchLineSouthForSameTypes = [&](ChunkVecType src_line, std::uint8_t max_size = _size_in_power) -> std::uint8_t {
std::size_t alike_counter = 0;
const auto end = AxisIterator::end();
for (auto axis_crawl = AxisIterator(src_line, south_dir_mask.first, south_dir_mask.second); axis_crawl != end; axis_crawl++) {
LocalVoxel block_to_check = GetVoxelFromPosInChunk(axis_crawl.cur);
auto below_block_type = this->GetVoxelType(block_to_check);
if (below_block_type != type)
break;
if (chunk_finished_checksum[block_to_check.pos])
break;
alike_counter++;
if (alike_counter >= max_size)
break;
}
return alike_counter;
};
auto same_blocks_south_count = SearchLineSouthForSameTypes(src_block_loc);
assert(same_blocks_south_count);
block_quad_size.y = same_blocks_south_count;
std::cout << " Found blocks going south: " << (int)block_quad_size.y << std::endl;
auto SearchLineEastForSameTypes = [&]() -> std::uint8_t {
std::size_t alike_counter = 1;
const auto end = AxisIterator::end();
constexpr auto east_dir_mask = AxisIterator::CreateCardinalMask(CardinalDirections::kEast);
auto side_axis_crawl = AxisIterator(src_block_loc, east_dir_mask.first, east_dir_mask.second);
side_axis_crawl++; // starting one ahead
// if (side_axis_crawl != end) {
std::cout << " Searching More East!!" << std::endl;
for (; side_axis_crawl != end; side_axis_crawl++) {
LocalVoxel side_block_to_check = GetVoxelFromPosInChunk(side_axis_crawl.cur);
auto below_block_type = this->GetVoxelType(side_block_to_check);
if (below_block_type != type)
break;
// here we go again, dip down to this max length
auto additional_same_blocks_south_count = SearchLineSouthForSameTypes(side_axis_crawl.cur, same_blocks_south_count);
assert(additional_same_blocks_south_count <= same_blocks_south_count);
if (additional_same_blocks_south_count < same_blocks_south_count)
break;
alike_counter++;
}
//}
return alike_counter;
};
if (src_block_loc.x + 1 < _size_in_power) { // only if we have room to check!
auto same_blocks_east_count = SearchLineEastForSameTypes();
block_quad_size.x = same_blocks_east_count;
std::cout << " Found blocks going east: " << (int)block_quad_size.x << std::endl;
}
ChunkMeshedQuad new_face;
auto start = geo::Vec2(src_block_loc.x * box_size, src_block_loc.y * box_size);
auto size = geo::Vec2(block_quad_size.x * box_size, block_quad_size.y * box_size);
new_face.quad = geo::Box<geo::Vec2>(start, size);
new_face.clr = chunk_textures[type];
std::cout << " *Added quad! : {" << start.x << ", " << start.y << "} with size {" << size.x << ", " << size.y << "}" << std::endl;
finished_quads.push_back(new_face);
auto MarkAreaFinished = [&](ChunkVecType origin, ChunkVecType size) {
assert(size.x && size.y);
for (std::size_t x = 0; x < size.x; x++) {
for (std::size_t y = 0; y < size.y; y++) {
auto block_to_clear = origin + ChunkVecType(x, y);
std::cout << " ------Marking Block Finished: {" << (int)block_to_clear.x << ", " << (int)block_to_clear.y << "}" << std::endl;
chunk_finished_checksum[this->GetVoxelFromPosInChunk(block_to_clear).pos] = true;
}
}
};
MarkAreaFinished(src_block_loc, block_quad_size);
// mark it off of the chunk_finished_checksum by setting the chunk_finished_checksum[i] to 1
}
std::cout << "Greedy ";
break;
}
default:
// Do nothing, no fail just no-op any mesher un-implimented.
break;
};
auto end_time = std::chrono::high_resolution_clock::now(); // Calculate duration and convert to milliseconds
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
std::cout << "Meshing Finished!!! QuadCount: " << (int)finished_quads.size() << ", Within: " << duration << " microseconds" << std::endl;
return finished_quads;
}
static bool IsPosValidWithinChunk(ChunkVecType voxel_loc) {
if (voxel_loc.x >= _size_in_power)
return false;
if (voxel_loc.y >= _size_in_power)
return false;
return true;
}
};
} // namespace qubel
class QubelMeshingTestWindow : public CBaseWindow {
public:
CCheckbox* activate;
CCheckbox* wireframe;
CDropdown* dropdown;
QubelMeshingTestWindow(IWidget* parent)
: CBaseWindow(parent, "qubelmesh_test_settings") {
this->always_visible = false;
this->hover = false;
this->SetMaxSize(1270, 1000);
this->SetPositionMode(PositionMode::FLOATING);
this->Add<CTitleBar>("~SimpleQubelMeshingTester~");
this->Add<CTextLabel>("activate_label", "Activate Viewing:");
activate = this->Add<CCheckbox>("activate", false);
this->Add<CTextLabel>("spread_label", "VoxelSpread:");
this->Add<CSlider>("spread")->SetStep(0.1f);
this->Add<CTextLabel>("wireframe_label", "Wireframe Mesh:");
wireframe = this->Add<CCheckbox>("wireframe", true);
this->Add<CTextLabel>("wanted_mesher_label", "Meshing Algorithm:");
dropdown = this->Add<CDropdown>("wanted_mesher");
dropdown->AddValue("NoFaceOcclusion, Plain");
dropdown->AddValue("Nieve");
dropdown->AddValue("Greedy"); // Just expand same type faces
dropdown->AddValue("Intrinsic Greedy"); // Using bitwise operations :O
dropdown->AddValue("Global Lattice"); // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
dropdown->SetSize(150, 16);
dropdown->SetValue(2);
this->Add<CTextLabel>("binary_meshing_label", "Binary Meshing(ignore types):");
this->Add<CCheckbox>("binary_meshing", false);
}
};
using namespace qubel;
class QubelMeshingTestRenderingWindow : public CBaseWindow {
public:
const CTitleBar* titlebar;
const QubelMeshingTestWindow* settings;
Chunk world_slice;
class ChunkRenderer : public CBaseWidget {
public:
Chunk* world_slice;
QubelMeshingTestWindow* settings;
ChunkRenderer(IWidget* parent, QubelMeshingTestWindow* settings, Chunk* world_slice)
: CBaseWidget("qubelmesh_test_renderer_sceneoutput", parent)
, settings(settings)
, world_slice(world_slice) {
assert(settings && world_slice);
settings->dropdown->SetCallback([&](CDropdown*, int value) {
current_render_quads.clear();
});
}
std::vector<Chunk::ChunkMeshedQuad> current_render_quads;
virtual void Draw(ICanvas* the_drawing_machine) override {
this->CBaseWidget::Draw(the_drawing_machine);
const auto ConvertGLMVecToSTDPairVec = [](const geo::Vec2& vec_in) -> std::pair<int, int> {
return std::pair<int, int>(vec_in.x, vec_in.y);
};
const auto ConvertGLMQuadToSTDPairQuad = [&](const geo::Box<geo::Vec2>& box_in) -> Canvas::TranslationMatrix {
return Canvas::TranslationMatrix(ConvertGLMVecToSTDPairVec(box_in.origin), ConvertGLMVecToSTDPairVec(box_in.GetSize()));
};
for (const Chunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads)
the_drawing_machine->Rect(ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad), kinda_a_vert.clr);
if (this->settings->wireframe->Value()) {
for (const Chunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads) {
the_drawing_machine->Rect({ ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad) }, glez::color::black, Canvas::RectType::Outline);
the_drawing_machine->Line({ ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad) }, glez::color::black);
}
}
}
virtual void Update() override {
this->CBaseWidget::Update();
if (current_render_quads.empty()) {
current_render_quads = world_slice->MeshChunk(Chunk::MesherComplexity(settings->dropdown->Value()));
std::pair<int, int> found_size;
for (const Chunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads) {
auto max_coord = kinda_a_vert.quad.GetMax();
if (max_coord.x > found_size.first)
found_size.first = max_coord.x;
if (max_coord.y > found_size.second)
found_size.second = max_coord.y;
}
std::cout << "Created mesh with pixel size: " << found_size.first << ", " << found_size.second << std::endl;
this->SetSize(found_size.first, found_size.second);
}
}
};
ChunkRenderer* render_scene;
public:
QubelMeshingTestRenderingWindow(IWidget* parent, QubelMeshingTestWindow* settings)
: CBaseWindow(parent, "qubelmesh_test_renderer_frame")
, settings(settings) {
assert(settings);
this->always_visible = false;
this->hover = false;
this->SetPositionMode(PositionMode::FLOATING);
this->SetMaxSize(1270, 1000);
this->zindex = -999;
titlebar = this->Add<CTitleBar>("QubelWorld Mesh Test uwu~");
render_scene = this->Add<ChunkRenderer>(settings, &world_slice);
}
~QubelMeshingTestRenderingWindow() { }
virtual bool AlwaysVisible() const override {
return this->settings->activate->Value();
}
virtual bool IsVisible() const override {
return this->settings->activate->Value() && this->CBaseWindow::IsVisible();
}
public:
// std::chrono::time_point<std::chrono::system_clock> last_update;
};
int start_nyqubel_client() {
hydride_init();
glez::init(hydride_library.width, hydride_library.height);
Canvas* canvas;
x11::X11Poller x11_poller(hydride_library.display, hydride_library.window);
{
input::RefreshInput();
hydride_draw_begin();
glez::begin();
canvas = new Canvas();
canvas->Setup();
glez::end();
hydride_draw_end();
}
canvas->Add<ncc::Logo>()->SetOffset(500, 525);
auto mesh_test_settings_window = canvas->Add<QubelMeshingTestWindow>();
auto mesh_test_rendering_window = canvas->Add<QubelMeshingTestRenderingWindow>(mesh_test_settings_window);
mesh_test_settings_window->SetOffset(2000, 400);
mesh_test_rendering_window->SetOffset(3000, 800);
bool client_exiting = false;
mesh_test_settings_window->Add<CBaseButton>("exit_button", "Press to Quit-rite", [&](CBaseButton*) {
client_exiting = true;
std::cout << "User Requested Client quit." << std::endl;
});
// Need a 856/480 size window.
hydride_show();
while (1) {
input::RefreshInput();
// Must be called in that order.
hydride_draw_begin();
glez::begin();
{
x11_poller.UpdateKeys();
x11_poller.UpdateMouse();
canvas->Update();
}
glez::end();
hydride_draw_end();
if (client_exiting)
break;
}
std::cout << "Client is Exiting!!!" << std::endl;
return 0;
}

View File

@ -0,0 +1,4 @@
#pragma once
int start_nyqubel_client();

View File

@ -0,0 +1,88 @@
/*
* Nekohook: Free, fast and modular cheat software
* Copyright (C) 2018 Rebekah Rowe
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cstdlib> // rand()
#include "geometry.hpp"
namespace qubel::geo::internal {
Angle2& Angle2::Clamp() {
while (this->x > 89.0f) this->x -= 180.0f;
while (this->x < -89.0f) this->x += 180.0f;
while (this->y > 180.0f) this->y -= 360.0f;
while (this->y < -180.0f) this->y += 360.0f;
return *this;
}
// Returns angles to a point in space
AngleBase<2> AngleBase<2>::PointTo(const Vec3& src_point, const Vec3& dest_point) {
Vec3 aim_point = dest_point - src_point;
Angle2 out;
out.y = atan2(aim_point.y, aim_point.x) * 180.0f / F_PI;
out.x = atan2(0, sqrt(aim_point.x * aim_point.x + aim_point.y * aim_point.y)) * 180.0f / F_PI;
return out.Clamp();
}
// A function to get the difference from angles, Please make sure inputs are
// clamped
AngleBase<2> AngleBase<2>::GetDelta(const AngleBase<2>& dest_angles) const {
// Our output difference
Angle2 diff;
// Yaw
if (this->y != dest_angles.y) {
// Check if yaw is on opposing poles
if (this->y < -90 && dest_angles.y > 90)
diff.y = this->y + 360 - dest_angles.y;
else if (this->y > 90 && dest_angles.y < -90)
diff.y = dest_angles.y + 360 - this->y;
else
diff.y = std::abs(this->y - dest_angles.y);
}
// Pitch
if (this->x != dest_angles.x) diff.x = std::abs(this->x - dest_angles.x);
return diff;
}
// Use input angles and our eye position to get fov to a destination point
float AngleBase<2>::GetFov(const Vec3& src, const Vec3& dest) const {
return this->GetFov(PointTo(src, dest));
}
float AngleBase<2>::GetFov(const AngleBase<2>& pointed_angle) const {
Angle2 delta = this->GetDelta(pointed_angle);
return std::max<float>(delta.x, delta.y);
}
Vec3 DirectionalMove(const Vec3& src, const AngleBase<2>& dir, float amt) {
// Math I dont understand
float sp = sinf(dir.x * F_PI / 180.f);
float cp = cosf(dir.x * F_PI / 180.f);
float sy = sinf(dir.y * F_PI / 180.f);
float cy = cosf(dir.y * F_PI / 180.f);
return Vec3(cp * cy, cp * sy, -sp) * amt + src;
}
} // namespace neko::math

View File

@ -0,0 +1,513 @@
/*
* Nekohook: Free, fast and modular cheat software
* Copyright (C) 2018 Rebekah Rowe
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
#include <algorithm>
#include <glm/detail/qualifier.hpp>
#include <glm/fwd.hpp>
#include <stdexcept>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
#include <glm/geometric.hpp>
namespace qubel::geo {
namespace internal {
#if true // NEKOHOOK_GFX == opengl
template <int _dim_length, typename T>
using BVec = glm::vec<_dim_length, T>;
template <typename T>
using BVec2 = glm::vec<2, T>;
template <typename T>
using BVec3 = glm::vec<3, T>;
using Vec2 = glm::vec2;
using Vec3 = glm::vec3;
using DVec3 = glm::dvec3;
using IVec2 = glm::ivec2;
using UVec2 = glm::uvec2;
constexpr float F_PI = glm::pi<float>();
template <class... T>
auto Distance(T... in) { return glm::distance(in...); }
template <typename T>
auto GetMaskOfVec2(const T& src, glm::bvec2 mask) {
T ret = { 0, 0 };
if (mask.x)
ret.x = src.x;
if (mask.y)
ret.y = src.y;
return ret;
}
#endif
template <typename T, int _length>
class BoxBase;
template <typename T>
class BoxBase<T, 2> {
static_assert(T::length() == 2);
protected:
BoxBase() { }
BoxBase(T _first, T _second)
: first(_first)
, second(_second) { }
public:
// TODO, rotation around origin?
// Data
union {
T first, origin;
};
union {
T second, size;
};
T GetMax() const { return this->origin + this->size; }
T GetSize() const { return this->size; }
T GetPoint(int idx) const {
switch (idx) {
case 0:
return this->origin;
case 1:
return { this->origin.x + this->size.x, this->origin.y };
case 2:
return { this->origin.x, this->origin.y + this->size.y };
case 3:
return this->origin + this->size;
default:
throw std::logic_error("Unknown point idx: " + std::to_string(idx));
}
}
std::array<T, 4> GetPoints() const {
T max = this->GetMax();
return {
this->origin,
{ max.x, this->origin.y },
{ this->origin.x, max.y },
max
};
}
bool Contains(T in) const {
if (in.x > this->origin.x && in.y > this->origin.y) {
T max = this->GetMax();
return (in.x < max.x && in.x < max.y);
}
return false;
}
bool SegmentIntersects(T src, T dest) const {
if ((dest.x > this->min.x && src.x > this->min.x) && (dest.y > this->min.y && src.y > this->min.y)) {
T max = this->GetMax();
return (dest.x < max.x && src.x < max.x) || (dest.y < max.y && src.y < max.y);
}
return false;
}
enum class Direction {
kUp,
kDown,
kLeft,
kRight,
kPosX = kRight,
kPosY = kDown,
kNegX = kLeft,
kNegY = kUp
};
void Expand(Direction dir, int amt) { // TODO: Bitmongering(nopey)
switch (dir) {
case Direction::kUp:
this->origin.y -= amt;
case Direction::kDown:
this->size.y += amt;
break;
case Direction::kLeft:
this->origin.x -= amt;
case Direction::kRight:
this->size.x += amt;
break;
default:
assert(false);
}
}
void ExpandTo(T v) {
if (this->origin.x > v.x)
this->Expand(Direction::kNegX, this->origin.x - v.x);
if (this->origin.y > v.y)
this->Expand(Direction::kNegY, this->origin.y - v.y);
T t_max = this->GetMax();
if (t_max.x < v.x)
this->Expand(Direction::kPosX, v.x - this->origin.x);
if (t_max.y < v.y)
this->Expand(Direction::kPosY, v.y - this->origin.y);
}
// TODO: Center around
};
template <typename T>
class BoxBase<T, 3> {
static_assert(T::length() == 3);
protected:
BoxBase() { }
BoxBase(T _first, T _second)
: first(_first)
, second(_second) { }
public:
// Data
union {
T first, min;
};
union {
T second, max;
};
auto operator[](const int idx) const {
return this->data.at(idx);
}
// Utility functions
T GetSize() const { return this->max - this->min; }
T& GetMax() const { return this->max; }
T GetPoint(int idx) const {
switch (idx) {
case 0:
return this->min;
case 1:
return { this->max.x, this->min.y, this->min.z };
case 2:
return { this->max.x, this->max.y, this->min.z };
case 3:
return { this->min.x, this->max.y, this->max.z };
case 4:
return { this->min.x, this->min.y, this->max.z };
case 5:
return { this->max.x, this->min.y, this->max.z };
case 6:
return { this->min.x, this->max.y, this->min.z };
case 7:
return this->max;
default:
throw std::logic_error("GetPoint() is only available for 2 and 3 axis vectors");
}
}
std::array<T, 8> GetPoints() const {
return {
this->min,
{ this->max.x, this->min.y, this->min.z },
{ this->max.x, this->max.y, this->min.z },
{ this->min.x, this->max.y, this->max.z },
{ this->min.x, this->min.y, this->max.z },
{ this->max.x, this->min.y, this->max.z },
{ this->min.x, this->max.y, this->min.z },
this->max
};
}
bool Contains(T i) const {
if (i.x > this->min.x || i.y > this->min.y || i.z > this->min.z)
if (i.x < this->max.x || i.y < this->max.y || i.z < this->max.z)
return false;
return true;
}
// Credits to cathook
bool LineIntersects(T src, T dst) const {
if (dst.x < this->min.x && src.x < this->min.x)
return false;
if (dst.y < this->min.y && src.y < this->min.y)
return false;
if (dst.z < this->min.z && src.z < this->min.z)
return false;
if (dst.x > this->max.x && src.x > this->max.x)
return false;
if (dst.y > this->max.y && src.y > this->max.y)
return false;
if (dst.z > this->max.z && src.z > this->max.z)
return false;
return true;
}
enum Direction {
kUp,
kDown,
kLeft,
kRight,
kForward,
kBackward,
kPosX = kUp,
kPosY = kLeft,
kPosZ = kForward,
kNegX = kDown,
kNegY = kRight,
kNegZ = kBackward
};
void Expand(Direction dir, int amt) {
switch (dir) {
case Direction::kUp:
this->max.y += amt;
break;
case Direction::kDown:
this->min.y -= amt;
break;
case Direction::kLeft:
this->min.x += amt;
break;
case Direction::kRight:
this->min.x -= amt;
break;
case Direction::kForward:
this->min.z += amt;
break;
case Direction::kBackward:
this->max.z -= amt;
break;
default:
assert(false);
}
}
};
template <typename T>
class Box : public BoxBase<T, T::length()> {
using Parent = BoxBase<T, T::length()>;
public:
using Direction = typename Parent::Direction;
Box() { }
Box(T _first, T _second)
: Parent(_first, _second) { }
Box(std::pair<T, T> v)
: Box(v.first, v.second) { }
template <typename TT>
Box(TT v)
: Box(v.first, v.second) {
static_assert(!std::is_same_v<std::pair<T, T>, TT>);
}
template <typename TT>
Box<T>& operator=(TT v) { *this = Box<T>(v.first, v.second); }
// Unary arithmetic operators
bool operator==(T v) const {
return this->first == v.first && this->second == v.second;
}
bool operator!=(T v) const {
return !(*this == v);
}
Box<T> operator+(T v) const {
return { this->min + v, this->max + v };
}
Box<T> operator-(T v) const {
return { this->min - v, this->max - v };
}
Box<T> operator*(typename T::value_type v) const {
T center = this->GetCenter();
T size = (this->GetSize() * v) / typename T::value_type(2);
return { center - size, center + size };
}
Box<T> operator/(typename T::value_type v) const {
T center = this->GetCenter();
T delta = (this->GetSize() / v) / typename T::value_type(2);
return { center - delta, center + delta };
}
Box<T>& operator/=(typename T::value_type v) {
return *this = *this / v;
}
// Utility functions
T GetCenter() const { return (this->first + this->second) / typename T::value_type(2); }
void Expand(std::initializer_list<Direction> dirs, int amt) {
auto end = dirs.end();
for (auto i = dirs.begin(); i != end; i++)
this->Expand(*i, amt);
}
void Shrink(Direction dir, int amt) { this->Expand(dir, -amt); }
};
template <int T_length>
class AngleBase;
template <>
class AngleBase<1> {
float data;
protected:
template <typename... T>
AngleBase(T... args)
: data(args...) { }
public:
operator float() const {
return this->data;
}
operator float&() {
return this->data;
}
};
template <>
class AngleBase<2> : public Vec2 {
using Vec2::Vec2;
using Parent = Vec2;
protected:
template <typename... T>
AngleBase(T... args)
: Vec2(args...) {
static_assert(!(std::is_same_v<Vec2, T> || ...));
}
public:
using Vec2::operator=;
template <class T>
AngleBase operator-(T in) { return *this - Vec2(in); }
template <class T>
AngleBase operator+(T in) { return *this + Vec2(in); }
AngleBase<2>& Clamp();
AngleBase<2> Clamp() const;
AngleBase<2> GetDelta(const AngleBase<2>& other_angles) const;
float GetFov(const Vec3& view_src, const Vec3& dest_point) const;
float GetFov(const AngleBase<2>& pointed_angle) const;
static AngleBase<2> PointTo(const Vec3& src_point, const Vec3& dest_point);
};
template <int T_Length>
using Angle = AngleBase<T_Length>;
using Angle1 = Angle<1>;
using Angle2 = Angle<2>;
template <typename T_Origin, typename T_Angle>
class Ray {
private:
const T_Origin origin;
const T_Angle angle;
public:
Ray(T_Origin origin, T_Angle angle)
: origin(origin)
, angle(angle) { }
std::pair<T_Origin, T_Origin> Cast(float dist);
};
template <typename T>
class Segment : public std::pair<T, T> {
public:
using Parent = std::pair<T, T>;
using Parent::Parent;
using Parent::operator=;
template <typename TT>
Segment(Ray<T, TT> r, float dist = 8192.0f)
: Parent(r.Cast(dist)) { }
Segment(const std::pair<T, T>& p)
: Parent(p) { }
Segment(std::pair<T, T>&& p)
: Parent(std::move(p)) { }
Segment(std::pair<T, T> p)
: Parent(std::move(p)) { }
};
} // namespace internal
using Vec3 = internal::Vec3;
using Vec2 = internal::Vec2;
// idk if these should be in here, but its too useful not to...
using DVec3 = internal::DVec3;
using IVec2 = internal::IVec2;
using UVec2 = internal::UVec2;
///
template <class... T>
auto Distance(T... in) { return internal::Distance(in...); }
template <class T>
auto GetMaskOfVec2(const T& src, glm::bvec2 mask) { return internal::GetMaskOfVec2(src, mask); }
template <class T>
using Box = internal::Box<T>;
template <int T>
using Angle = internal::AngleBase<T>;
template <typename T_Origin, typename T_Angle>
using Ray = internal::Ray<T_Origin, T_Angle>;
template <typename T>
using Segment = internal::Segment<T>;
using Segment2 = internal::Segment<Vec2>;
using Segment3 = internal::Segment<Vec3>;
template <class T_Vec, class T_Float>
using Sphereoid = std::pair<T_Vec, T_Float>;
/*
template<class T>
concept Vec = requires(T&& t) {
{ t.x } -> std::floating_point;
{ t.y } -> std::floating_point;
};
template<class T>
concept Vec3 = Vec<T> && requires(T&& t) {
{ t.z } -> std::floating_point;
};
template<class T>
concept Vec2 = Vec<T> && !Vec3<T>;
template<class T>
concept Box = requires(T&& t) {
{ t.origin } -> Vec;
{ t.size } -> Vec;
};
template<class T>
concept Angle = requires(T&& t) {
{ t.p } -> std::floating_point;
{ t.y } -> std::floating_point;
};
template<class T>
concept Ray = requires(T&& t) {
{ std::forward<T>(t).origin } -> Vec;
{ std::forward<T>(t).angle } -> Angle;
//{ std::forward<T>(t).Cast(float) } -> Vec;
};
template<Vec O, std::floating_point D>
using Sphereoid = std::pair<O, D>;
*/
} // namespace qubel::geo

22
start.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/sh
if [ -f CMakeLists.txt ]; then
mkdir -p build
cd build
fi
if ! [ -f CMakeCache.txt ]; then
cmake ..
fi
if ! [ -f Server/Cuberite ]; then
make -j$(nproc)
fi
if ! [ -d res ]; then
ln -s ../client-resources/ res
fi
cd Server
LD_PRELOAD=/usr/local/lib/libhydride.so ./Cuberite
#LD_PRELOAD=/usr/local/lib/libhydride.so gdb ./Cuberite