Compare commits
7 Commits
main
...
hybridclie
Author | SHA1 | Date | |
---|---|---|---|
93dbf79583 | |||
431bebc361 | |||
4d27542ea9 | |||
cbdc83da7c | |||
5b568047c4 | |||
e790e44300 | |||
a37375b1bb |
@ -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
11
.gitmodules
vendored
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
12
build-clean.sh
Executable 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
12
build-client.sh
Executable 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
12
build-fastcompile.sh
Executable 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
12
build-release.sh
Executable 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
12
build.sh
Executable 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
BIN
client-resources/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
client-resources/logo.xcf
Normal file
BIN
client-resources/logo.xcf
Normal file
Binary file not shown.
BIN
client-resources/opensans.ttf
Normal file
BIN
client-resources/opensans.ttf
Normal file
Binary file not shown.
1
lib/libglez
Submodule
1
lib/libglez
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 221db8e9721510449e7f14a644410b8c7aead09c
|
1
lib/libhydride
Submodule
1
lib/libhydride
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 30659e1877f036b090bafc1a1e76170b88ab9fda
|
1
lib/libpdw
Submodule
1
lib/libpdw
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 9cbc0a088d5e65a498c4ddab2beeb8643a76a865
|
@ -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
134
src/Commands/CMDManager.cpp
Normal 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
94
src/Commands/CMDManager.h
Normal 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
89
src/Commands/CMDUtils.h
Normal 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!");
|
||||
}
|
10
src/Commands/CMakeLists.txt
Normal file
10
src/Commands/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
target_sources(
|
||||
${CMAKE_PROJECT_NAME} PRIVATE
|
||||
|
||||
CMDManager.cpp
|
||||
CoreCommands.cpp
|
||||
CMDManager.h
|
||||
CMDUtils.h
|
||||
CoreCommands.h
|
||||
)
|
||||
|
637
src/Commands/CoreCommands.cpp
Normal file
637
src/Commands/CoreCommands.cpp
Normal 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;
|
||||
}
|
45
src/Commands/CoreCommands.h
Normal file
45
src/Commands/CoreCommands.h
Normal 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);
|
||||
|
1620
src/Root.cpp
1620
src/Root.cpp
File diff suppressed because it is too large
Load Diff
316
src/Root.h
316
src/Root.h
@ -7,7 +7,7 @@
|
||||
* 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.
|
||||
@ -25,8 +25,9 @@
|
||||
#include "RankManager.h"
|
||||
#include "ChunkDef.h"
|
||||
|
||||
|
||||
|
||||
#ifdef NYQUBEL_CLIENT
|
||||
#include "nyqubel-client/client.hpp"
|
||||
#endif
|
||||
|
||||
// fwd:
|
||||
class cItem;
|
||||
@ -36,7 +37,8 @@ class cCraftingRecipes;
|
||||
class cRecipeMapper;
|
||||
class cFurnaceRecipe;
|
||||
class cWebAdmin;
|
||||
//class cPluginManager;
|
||||
// class cPluginManager;
|
||||
class cCMDManager;
|
||||
class cServer;
|
||||
class cWorld;
|
||||
class cPlayer;
|
||||
@ -48,222 +50,214 @@ class cUUID;
|
||||
class BlockTypePalette;
|
||||
class ProtocolPalettes;
|
||||
|
||||
using cPlayerListCallback = cFunctionRef<bool(cPlayer &)>;
|
||||
using cWorldListCallback = cFunctionRef<bool(cWorld &)>;
|
||||
using cPlayerListCallback = cFunctionRef<bool(cPlayer&)>;
|
||||
using cWorldListCallback = cFunctionRef<bool(cWorld&)>;
|
||||
|
||||
namespace Json
|
||||
{
|
||||
class Value;
|
||||
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
|
||||
static cRoot* Get() { return s_Root; }
|
||||
// tolua_end
|
||||
|
||||
/** which ini file to load settings from, default is settings.ini */
|
||||
AString m_SettingsFilename;
|
||||
/** which ini file to load settings from, default is settings.ini */
|
||||
AString m_SettingsFilename;
|
||||
|
||||
cRoot(void);
|
||||
~cRoot();
|
||||
cRoot(void);
|
||||
~cRoot();
|
||||
|
||||
/** Run the server. Returns true if we should restart, false to quit. */
|
||||
bool Run(cSettingsRepositoryInterface & a_OverridesRepo);
|
||||
/** Run the server. Returns true if we should restart, false to quit. */
|
||||
bool Run(cSettingsRepositoryInterface& a_OverridesRepo);
|
||||
|
||||
/** Interrupts the server and stops it, as if "/stop" typed in the console. */
|
||||
static void Stop();
|
||||
/** Interrupts the server and stops it, as if "/stop" typed in the console. */
|
||||
static void Stop();
|
||||
|
||||
/** Interrupts the server and restarts it, as if "/restart" was typed in the console. */
|
||||
static void Restart();
|
||||
/** Interrupts the server and restarts it, as if "/restart" was typed in the console. */
|
||||
static void Restart();
|
||||
|
||||
// tolua_begin
|
||||
cServer * GetServer(void) { return m_Server; }
|
||||
cWorld * GetDefaultWorld(void);
|
||||
// tolua_begin
|
||||
cServer* GetServer(void) { return m_Server; }
|
||||
cWorld* GetDefaultWorld(void);
|
||||
|
||||
/** Returns a pointer to the world specified. If no world of that name exists, returns a nullptr. */
|
||||
cWorld * GetWorld(const AString & a_WorldName);
|
||||
/** Returns a pointer to the world specified. If no world of that name exists, returns a nullptr. */
|
||||
cWorld* GetWorld(const AString& a_WorldName);
|
||||
|
||||
/** Returns the up time of the server in seconds */
|
||||
int GetServerUpTime(void)
|
||||
{
|
||||
return static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_StartTime).count());
|
||||
}
|
||||
// tolua_end
|
||||
/** Returns the up time of the server in seconds */
|
||||
int GetServerUpTime(void) {
|
||||
return static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_StartTime).count());
|
||||
}
|
||||
// tolua_end
|
||||
|
||||
/** Calls the callback for each world; returns true if the callback didn't abort (return true) */
|
||||
bool ForEachWorld(cWorldListCallback a_Callback); // >> Exported in ManualBindings <<
|
||||
/** Calls the callback for each world; returns true if the callback didn't abort (return true) */
|
||||
bool ForEachWorld(cWorldListCallback a_Callback); // >> Exported in ManualBindings <<
|
||||
|
||||
/** Writes chunkstats, for each world and totals, to the output callback */
|
||||
void LogChunkStats(cCommandOutputCallback & a_Output);
|
||||
/** Writes chunkstats, for each world and totals, to the output callback */
|
||||
void LogChunkStats(cCommandOutputCallback& a_Output);
|
||||
|
||||
cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; }
|
||||
cMonsterConfig* GetMonsterConfig(void) { return m_MonsterConfig; }
|
||||
|
||||
cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
|
||||
cRecipeMapper * GetRecipeMapper(void) { return m_RecipeMapper.get(); }
|
||||
cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature
|
||||
cBrewingRecipes * GetBrewingRecipes (void) { return m_BrewingRecipes.get(); } // Exported in ManualBindings.cpp
|
||||
cCraftingRecipes* GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
|
||||
cRecipeMapper* GetRecipeMapper(void) { return m_RecipeMapper.get(); }
|
||||
cFurnaceRecipe* GetFurnaceRecipe(void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature
|
||||
cBrewingRecipes* GetBrewingRecipes(void) { return m_BrewingRecipes.get(); } // Exported in ManualBindings.cpp
|
||||
|
||||
/** Returns the (read-write) storage for registered block types. */
|
||||
// BlockTypeRegistry & GetBlockTypeRegistry() { return m_BlockTypeRegistry; }
|
||||
/** Returns the (read-write) storage for registered block types. */
|
||||
// BlockTypeRegistry & GetBlockTypeRegistry() { return m_BlockTypeRegistry; }
|
||||
|
||||
/** Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel */
|
||||
static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export
|
||||
/** Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel */
|
||||
static int GetFurnaceFuelBurnTime(const cItem& a_Fuel); // tolua_export
|
||||
|
||||
/** Returns the completions for a player name across all worlds. Returns an
|
||||
empty vector if none are found. */
|
||||
AStringVector GetPlayerTabCompletionMultiWorld(const AString & a_Text);
|
||||
/** Returns the completions for a player name across all worlds. Returns an
|
||||
empty vector if none are found. */
|
||||
AStringVector GetPlayerTabCompletionMultiWorld(const AString& a_Text);
|
||||
|
||||
/** The current time where the startup of the server has been completed */
|
||||
std::chrono::steady_clock::time_point m_StartTime;
|
||||
/** The current time where the startup of the server has been completed */
|
||||
std::chrono::steady_clock::time_point m_StartTime;
|
||||
|
||||
cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export
|
||||
//cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
|
||||
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
|
||||
cMojangAPI & GetMojangAPI (void) { return *m_MojangAPI; }
|
||||
cRankManager * GetRankManager (void) { return m_RankManager.get(); }
|
||||
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(); }
|
||||
|
||||
/** Queues a console command for execution through the cServer class.
|
||||
The command will be executed in the tick thread
|
||||
The command's output will be written to the a_Output callback
|
||||
"stop" and "restart" commands have special handling.
|
||||
*/
|
||||
void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
|
||||
/** Queues a console command for execution through the cServer class.
|
||||
The command will be executed in the tick thread
|
||||
The command's output will be written to the a_Output callback
|
||||
"stop" and "restart" commands have special handling.
|
||||
*/
|
||||
void QueueExecuteConsoleCommand(const AString& a_Cmd, cCommandOutputCallback& a_Output);
|
||||
|
||||
/** Queues a console command for execution through the cServer class.
|
||||
The command will be executed in the tick thread
|
||||
The command's output will be sent to console
|
||||
"stop" and "restart" commands have special handling.
|
||||
*/
|
||||
void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export
|
||||
/** Queues a console command for execution through the cServer class.
|
||||
The command will be executed in the tick thread
|
||||
The command's output will be sent to console
|
||||
"stop" and "restart" commands have special handling.
|
||||
*/
|
||||
void QueueExecuteConsoleCommand(const AString& a_Cmd); // tolua_export
|
||||
|
||||
/** Kicks the user, no matter in what world they are. Used from cAuthenticator */
|
||||
void KickUser(int a_ClientID, const AString & a_Reason);
|
||||
/** Kicks the user, no matter in what world they are. Used from cAuthenticator */
|
||||
void KickUser(int a_ClientID, const AString& a_Reason);
|
||||
|
||||
/** Returns the number of chunks loaded */
|
||||
size_t GetTotalChunkCount(void); // tolua_export
|
||||
/** Returns the number of chunks loaded */
|
||||
size_t GetTotalChunkCount(void); // tolua_export
|
||||
|
||||
/** Saves all chunks in all worlds */
|
||||
void SaveAllChunks(void); // tolua_export
|
||||
/** Saves all chunks in all worlds */
|
||||
void SaveAllChunks(void); // tolua_export
|
||||
|
||||
/** Saves all chunks in all worlds synchronously (waits until dirty chunks have been sent to the ChunkStorage queue before returning) */
|
||||
void SaveAllChunksNow(void);
|
||||
/** Saves all chunks in all worlds synchronously (waits until dirty chunks have been sent to the ChunkStorage queue before returning) */
|
||||
void SaveAllChunksNow(void);
|
||||
|
||||
/** Sets whether saving chunks is enabled in all worlds (overrides however the worlds were already set) */
|
||||
void SetSavingEnabled(bool a_SavingEnabled); // tolua_export
|
||||
/** Sets whether saving chunks is enabled in all worlds (overrides however the worlds were already set) */
|
||||
void SetSavingEnabled(bool a_SavingEnabled); // tolua_export
|
||||
|
||||
/** Calls the callback for each player in all worlds */
|
||||
bool ForEachPlayer(cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
/** Calls the callback for each player in all worlds */
|
||||
bool ForEachPlayer(cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
|
||||
/** Finds a player from a partial or complete player name and calls the callback - case-insensitive */
|
||||
bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
/** Finds a player from a partial or complete player name and calls the callback - case-insensitive */
|
||||
bool FindAndDoWithPlayer(const AString& a_PlayerName, cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
|
||||
/** Finds the player over his uuid and calls the callback */
|
||||
bool DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
/** Finds the player over his uuid and calls the callback */
|
||||
bool DoWithPlayerByUUID(const cUUID& a_PlayerUUID, cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
|
||||
|
||||
/** Finds the player using it's complete username and calls the callback */
|
||||
bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback a_Callback);
|
||||
/** Finds the player using it's complete username and calls the callback */
|
||||
bool DoWithPlayer(const AString& a_PlayerName, cPlayerListCallback a_Callback);
|
||||
|
||||
/** Send playerlist of all worlds to player */
|
||||
void SendPlayerLists(cPlayer * a_DestPlayer);
|
||||
/** Send playerlist of all worlds to player */
|
||||
void SendPlayerLists(cPlayer* a_DestPlayer);
|
||||
|
||||
/** Broadcast playerlist addition through all worlds */
|
||||
void BroadcastPlayerListsAddPlayer(const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
|
||||
/** Broadcast playerlist addition through all worlds */
|
||||
void BroadcastPlayerListsAddPlayer(const cPlayer& a_Player, const cClientHandle* a_Exclude = nullptr);
|
||||
|
||||
/** Broadcast playerlist removal through all worlds */
|
||||
void BroadcastPlayerListsRemovePlayer(const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
|
||||
/** Broadcast playerlist removal through all worlds */
|
||||
void BroadcastPlayerListsRemovePlayer(const cPlayer& a_Player, const cClientHandle* a_Exclude = nullptr);
|
||||
|
||||
/** Broadcast playerlist header and footer through all worlds */
|
||||
void BroadcastPlayerListsHeaderFooter(const cCompositeChat & a_Header, const cCompositeChat & a_Footer); // tolua_export
|
||||
/** Broadcast playerlist header and footer through all worlds */
|
||||
void BroadcastPlayerListsHeaderFooter(const cCompositeChat& a_Header, const cCompositeChat& a_Footer); // tolua_export
|
||||
|
||||
// tolua_begin
|
||||
// tolua_begin
|
||||
|
||||
/** Sends a chat message to all connected clients (in all worlds) */
|
||||
void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom);
|
||||
void BroadcastChat (const cCompositeChat & a_Message);
|
||||
void BroadcastChatDeath (const AString & a_Message) { BroadcastChat(a_Message, mtDeath); }
|
||||
void BroadcastChatFailure(const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
|
||||
void BroadcastChatFatal (const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
|
||||
void BroadcastChatInfo (const AString & a_Message) { BroadcastChat(a_Message, mtInformation); }
|
||||
void BroadcastChatJoin (const AString & a_Message) { BroadcastChat(a_Message, mtJoin); }
|
||||
void BroadcastChatLeave (const AString & a_Message) { BroadcastChat(a_Message, mtLeave); }
|
||||
void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); }
|
||||
void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); }
|
||||
/** Sends a chat message to all connected clients (in all worlds) */
|
||||
void BroadcastChat(const AString& a_Message, eMessageType a_ChatPrefix = mtCustom);
|
||||
void BroadcastChat(const cCompositeChat& a_Message);
|
||||
void BroadcastChatDeath(const AString& a_Message) { BroadcastChat(a_Message, mtDeath); }
|
||||
void BroadcastChatFailure(const AString& a_Message) { BroadcastChat(a_Message, mtFailure); }
|
||||
void BroadcastChatFatal(const AString& a_Message) { BroadcastChat(a_Message, mtFailure); }
|
||||
void BroadcastChatInfo(const AString& a_Message) { BroadcastChat(a_Message, mtInformation); }
|
||||
void BroadcastChatJoin(const AString& a_Message) { BroadcastChat(a_Message, mtJoin); }
|
||||
void BroadcastChatLeave(const AString& a_Message) { BroadcastChat(a_Message, mtLeave); }
|
||||
void BroadcastChatSuccess(const AString& a_Message) { BroadcastChat(a_Message, mtSuccess); }
|
||||
void BroadcastChatWarning(const AString& a_Message) { BroadcastChat(a_Message, mtWarning); }
|
||||
|
||||
/** Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API */
|
||||
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
|
||||
/** Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API */
|
||||
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
|
||||
|
||||
/** Returns the amount of virtual RAM used, in KiB. Returns a negative number on error */
|
||||
static int GetVirtualRAMUsage(void);
|
||||
/** Returns the amount of virtual RAM used, in KiB. Returns a negative number on error */
|
||||
static int GetVirtualRAMUsage(void);
|
||||
|
||||
/** Returns the amount of virtual RAM used, in KiB. Returns a negative number on error */
|
||||
static int GetPhysicalRAMUsage(void);
|
||||
/** Returns the amount of virtual RAM used, in KiB. Returns a negative number on error */
|
||||
static int GetPhysicalRAMUsage(void);
|
||||
|
||||
// tolua_end
|
||||
// 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 {
|
||||
Run,
|
||||
Restart,
|
||||
Stop
|
||||
};
|
||||
|
||||
/** 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
|
||||
{
|
||||
Run,
|
||||
Restart,
|
||||
Stop
|
||||
};
|
||||
typedef std::map<AString, cWorld> WorldMap;
|
||||
|
||||
typedef std::map<AString, cWorld> WorldMap;
|
||||
/** Blocking reads and processes console input. */
|
||||
void HandleInput();
|
||||
|
||||
/** Blocking reads and processes console input. */
|
||||
void HandleInput();
|
||||
/** Performs run state transition, enforcing guarantees about state transitions. */
|
||||
static void TransitionNextState(NextState a_NextState);
|
||||
|
||||
/** Performs run state transition, enforcing guarantees about state transitions. */
|
||||
static void TransitionNextState(NextState a_NextState);
|
||||
cWorld* m_pDefaultWorld;
|
||||
WorldMap m_WorldsByName;
|
||||
|
||||
cWorld * m_pDefaultWorld;
|
||||
WorldMap m_WorldsByName;
|
||||
static cEvent s_StopEvent;
|
||||
|
||||
static cEvent s_StopEvent;
|
||||
cServer* m_Server;
|
||||
cMonsterConfig* m_MonsterConfig;
|
||||
|
||||
cServer * m_Server;
|
||||
cMonsterConfig * m_MonsterConfig;
|
||||
cCraftingRecipes* m_CraftingRecipes;
|
||||
std::unique_ptr<cRecipeMapper> m_RecipeMapper;
|
||||
cFurnaceRecipe* m_FurnaceRecipe;
|
||||
std::unique_ptr<cBrewingRecipes> m_BrewingRecipes;
|
||||
cWebAdmin* m_WebAdmin;
|
||||
// cPluginManager * m_PluginManager;
|
||||
cCMDManager* m_CMDManager;
|
||||
cAuthenticator m_Authenticator;
|
||||
cMojangAPI* m_MojangAPI;
|
||||
|
||||
cCraftingRecipes * m_CraftingRecipes;
|
||||
std::unique_ptr<cRecipeMapper> m_RecipeMapper;
|
||||
cFurnaceRecipe * m_FurnaceRecipe;
|
||||
std::unique_ptr<cBrewingRecipes> m_BrewingRecipes;
|
||||
cWebAdmin * m_WebAdmin;
|
||||
//cPluginManager * m_PluginManager;
|
||||
cAuthenticator m_Authenticator;
|
||||
cMojangAPI * m_MojangAPI;
|
||||
std::unique_ptr<cRankManager> m_RankManager;
|
||||
|
||||
std::unique_ptr<cRankManager> m_RankManager;
|
||||
cHTTPServer m_HTTPServer;
|
||||
|
||||
cHTTPServer m_HTTPServer;
|
||||
/** The storage for all registered block types. */
|
||||
// BlockTypeRegistry m_BlockTypeRegistry;
|
||||
|
||||
/** The storage for all registered block types. */
|
||||
// BlockTypeRegistry m_BlockTypeRegistry;
|
||||
void LoadGlobalSettings();
|
||||
|
||||
/** Loads the worlds from settings.ini, creates the worldmap */
|
||||
void LoadWorlds(cDeadlockDetect& a_dd, cSettingsRepositoryInterface& a_Settings, bool a_IsNewIniFile);
|
||||
|
||||
void LoadGlobalSettings();
|
||||
/** Starts each world's life */
|
||||
void StartWorlds(cDeadlockDetect& a_DeadlockDetect);
|
||||
|
||||
/** Loads the worlds from settings.ini, creates the worldmap */
|
||||
void LoadWorlds(cDeadlockDetect & a_dd, cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile);
|
||||
/** Stops each world's threads, so that it's safe to unload them */
|
||||
void StopWorlds(cDeadlockDetect& a_DeadlockDetect);
|
||||
|
||||
/** Starts each world's life */
|
||||
void StartWorlds(cDeadlockDetect & a_DeadlockDetect);
|
||||
static cRoot* s_Root;
|
||||
|
||||
/** Stops each world's threads, so that it's safe to unload them */
|
||||
void StopWorlds(cDeadlockDetect & a_DeadlockDetect);
|
||||
|
||||
static cRoot * s_Root;
|
||||
|
||||
/** Indicates the next action of cRoot, whether to run, stop or restart. */
|
||||
static std::atomic<NextState> s_NextState;
|
||||
}; // tolua_export
|
||||
/** Indicates the next action of cRoot, whether to run, stop or restart. */
|
||||
static std::atomic<NextState> s_NextState;
|
||||
}; // tolua_export
|
||||
|
363
src/Server.cpp
363
src/Server.cpp
@ -7,7 +7,7 @@
|
||||
* 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.
|
||||
@ -23,7 +23,7 @@
|
||||
#include "Mobs/Monster.h"
|
||||
#include "Root.h"
|
||||
#include "World.h"
|
||||
//#include "Bindings/PluginManager.h"
|
||||
// #include "Bindings/PluginManager.h"
|
||||
#include "ChatColor.h"
|
||||
#include "Entities/Player.h"
|
||||
#include "Inventory.h"
|
||||
@ -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)
|
||||
public:
|
||||
cServerListenCallbacks(cServer & a_Server, UInt16 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,8 +131,9 @@ cServer::cServer(void) :
|
||||
m_ShouldAuthenticate(false),
|
||||
m_UpTime(0)
|
||||
{
|
||||
// Initialize the LuaStateTracker singleton before the app goes multithreaded:
|
||||
//cLuaStateTracker::GetStats();
|
||||
// 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,8 +376,9 @@ 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):
|
||||
//cPluginManager::Get()->Tick(a_Dt);
|
||||
// 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:
|
||||
TickCommands();
|
||||
@ -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;
|
||||
@ -414,15 +448,18 @@ void cServer::TickClients(float a_Dt)
|
||||
|
||||
bool cServer::Start(void)
|
||||
{
|
||||
for (const auto & port: m_Ports)
|
||||
for (const auto & port : m_Ports)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return false; //cRoot::Get()->GetPluginManager()->CallHookChat(a_Player, a_Cmd);
|
||||
}
|
||||
);
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!a_Entity.IsPlayer())
|
||||
{
|
||||
a_Entity.Destroy();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
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");*/
|
||||
}
|
||||
|
||||
|
||||
@ -678,7 +758,7 @@ void cServer::BindBuiltInConsoleCommands(void)
|
||||
void cServer::Shutdown(void)
|
||||
{
|
||||
// Stop listening on all sockets:
|
||||
for (const auto & srv: m_ServerHandles)
|
||||
for (const auto & srv : m_ServerHandles)
|
||||
{
|
||||
srv->Close();
|
||||
}
|
||||
@ -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;
|
||||
});
|
||||
|
||||
|
14
src/nyqubel-client/CMakeLists.txt
Normal file
14
src/nyqubel-client/CMakeLists.txt
Normal 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()
|
953
src/nyqubel-client/client.cpp
Normal file
953
src/nyqubel-client/client.cpp
Normal 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;
|
||||
}
|
4
src/nyqubel-client/client.hpp
Normal file
4
src/nyqubel-client/client.hpp
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
int start_nyqubel_client();
|
88
src/nyqubel-client/util/geometry.cpp
Normal file
88
src/nyqubel-client/util/geometry.cpp
Normal 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
|
513
src/nyqubel-client/util/geometry.hpp
Normal file
513
src/nyqubel-client/util/geometry.hpp
Normal 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
22
start.sh
Executable 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
|
Loading…
x
Reference in New Issue
Block a user