diff options
Diffstat (limited to 'src')
247 files changed, 7691 insertions, 3932 deletions
diff --git a/src/Bindings/.gitignore b/src/Bindings/.gitignore index 0d00dd578..711ae9c3a 100644 --- a/src/Bindings/.gitignore +++ b/src/Bindings/.gitignore @@ -1,2 +1,4 @@ lua51.dll -LuaState_Call.inc +LuaState_Declaration.inc +LuaState_Implementation.cpp +LuaState_Typedefs.inc diff --git a/src/Bindings/AllToLua.bat b/src/Bindings/AllToLua.bat index f085af9e9..44675a886 100644 --- a/src/Bindings/AllToLua.bat +++ b/src/Bindings/AllToLua.bat @@ -12,7 +12,7 @@ :: Regenerate the files: echo Regenerating LUA bindings . . . -"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg +"tolua++.exe" -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg diff --git a/src/Bindings/AllToLua.sh b/src/Bindings/AllToLua.sh index 887c2490c..625ae4300 100644 --- a/src/Bindings/AllToLua.sh +++ b/src/Bindings/AllToLua.sh @@ -1,2 +1,2 @@ #!/bin/bash -/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg +/usr/bin/tolua++ -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg diff --git a/src/Bindings/AllToLua_lua.bat b/src/Bindings/AllToLua_lua.bat index 81c738f32..2d52c022d 100644 --- a/src/Bindings/AllToLua_lua.bat +++ b/src/Bindings/AllToLua_lua.bat @@ -13,7 +13,7 @@ :: Regenerate the files: echo Regenerating LUA bindings . . . -lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg +lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg diff --git a/src/Bindings/BindingsProcessor.lua b/src/Bindings/BindingsProcessor.lua new file mode 100644 index 000000000..f86be6c6d --- /dev/null +++ b/src/Bindings/BindingsProcessor.lua @@ -0,0 +1,161 @@ + +-- BindingsProcessor.lua + +-- Implements additional processing that is done while generating the Lua bindings + + + + + +local access = {public = 0, protected = 1, private = 2} + + + + + +--- Defines classes that have a custom manual Push() implementation and should not generate the automatic one +-- Map of classname -> true +local g_HasCustomPushImplementation = +{ + cEntity = true +} + + + + + +function parser_hook(s) + local container = classContainer.curr -- get the current container + + -- process access-specifying labels (public, private, etc) + do + local b, e, label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' + if b then + + -- found a label, get the new access value from the global 'access' table + if access[label] then + container.curr_member_access = access[label] + end -- else ? + + return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] + end + end +end + + + + + +--- Outputs the helper files supplementing the cLuaState class +-- Writes: +-- LuaState_Declaration.inc +-- LuaState_Implementation.cpp +-- LuaState_Typedefs.inc +local function OutputLuaStateHelpers(a_Package) + -- Collect all class types from ToLua: + local types = {} + for idx, item in ipairs(a_Package) do + local mt = getmetatable(item) or {} + if (mt.classtype == "class") then + table.insert(types, {name = item.name, lname = item.lname}) + end + end + table.sort(types, + function(a_Item1, a_Item2) + return (a_Item1.name:lower() < a_Item2.name:lower()) + end + ) + + -- Output the typedefs: + do + local f = assert(io.open("LuaState_Typedefs.inc", "w")) + f:write("\n// LuaState_Typedefs.inc\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n") + f:write("// Provides a forward declaration and a typedef for a pointer to each class exported to the Lua API.\n") + f:write("\n\n\n\n\n") + for _, item in ipairs(types) do + if not(item.name:match(".*<.*")) then -- Skip templates altogether + -- Classes start with a "c", everything else is a struct: + if (item.name:sub(1, 1) == "c") then + f:write("class " .. item.name .. ";\n") + else + f:write("struct " .. item.name .. ";\n") + end + end + end + f:write("\n\n\n\n\n") + for _, item in ipairs(types) do + f:write("typedef " .. item.name .. " * Ptr" .. item.lname .. ";\n") + end + f:write("\n\n\n\n\n") + f:close() + end + + -- Output the Push() and GetStackValue() function declarations: + do + local f = assert(io.open("LuaState_Declaration.inc", "w")) + f:write("\n// LuaState_Declaration.inc\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n") + f:write("// Implements a Push() and GetStackValue() function for each class exported to the Lua API.\n") + f:write("// This file expects to be included form inside the cLuaState class definition\n") + f:write("\n\n\n\n\n") + for _, item in ipairs(types) do + f:write("void Push(" .. item.name .. " * a_Value);\n") + end + for _, item in ipairs(types) do + f:write("void GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal);\n") + end + f:write("\n\n\n\n\n") + f:close() + end + + -- Output the Push() and GetStackValue() function implementations: + do + local f = assert(io.open("LuaState_Implementation.cpp", "w")) + f:write("\n// LuaState_Implementation.cpp\n\n// This file is generated along with the Lua bindings by ToLua. Do not edit manually, do not commit to repo.\n") + f:write("// Implements a Push() and GetStackValue() function for each class exported to the Lua API.\n") + f:write("// This file expects to be compiled as a separate translation unit\n") + f:write("\n\n\n\n\n") + f:write("#include \"Globals.h\"\n#include \"LuaState.h\"\n#include \"tolua++/include/tolua++.h\"\n") + f:write("\n\n\n\n\n") + for _, item in ipairs(types) do + if not(g_HasCustomPushImplementation[item.name]) then + f:write("void cLuaState::Push(" .. item.name .. " * a_Value)\n{\n\tASSERT(IsValid());\n") + f:write("\ttolua_pushusertype(m_LuaState, a_Value, \"" .. item.name .. "\");\n"); + f:write("\tm_NumCurrentFunctionArgs += 1;\n") + f:write("}\n\n\n\n\n\n") + end + end + for _, item in ipairs(types) do + f:write("void cLuaState::GetStackValue(int a_StackPos, Ptr" .. item.lname .. " & a_ReturnedVal)\n{\n\tASSERT(IsValid());\n") + f:write("\tif (lua_isnil(m_LuaState, a_StackPos))\n\t{\n") + f:write("\t a_ReturnedVal = nullptr;\n") + f:write("\t return;\n\t}\n") + f:write("\ttolua_Error err;\n") + f:write("\tif (tolua_isusertype(m_LuaState, a_StackPos, \"" .. item.name .. "\", false, &err))\n") + f:write("\t{\n") + f:write("\t a_ReturnedVal = *(reinterpret_cast<" .. item.name .. " **>(lua_touserdata(m_LuaState, a_StackPos)));\n") + f:write("\t}\n") + f:write("}\n\n\n\n\n\n") + end + f:close() + end +end + + + + + +function pre_output_hook(a_Package) + OutputLuaStateHelpers(a_Package) +end + + + + + +function post_output_hook() + print("Bindings have been generated.") +end + + + + diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 4cc73b350..133c2224d 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -11,12 +11,14 @@ SET (SRCS LuaNameLookup.cpp LuaServerHandle.cpp LuaState.cpp + LuaState_Implementation.cpp LuaTCPLink.cpp LuaUDPEndpoint.cpp LuaWindow.cpp ManualBindings.cpp ManualBindings_Network.cpp ManualBindings_RankManager.cpp + ManualBindings_World.cpp Plugin.cpp PluginLua.cpp PluginManager.cpp @@ -31,6 +33,8 @@ SET (HDRS LuaNameLookup.h LuaServerHandle.h LuaState.h + LuaState_Declaration.inc + LuaState_Typedefs.inc LuaTCPLink.h LuaUDPEndpoint.h LuaWindow.h @@ -46,12 +50,15 @@ SET (HDRS set (BINDING_OUTPUTS ${CMAKE_CURRENT_SOURCE_DIR}/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings.h + ${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Declaration.inc + ${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Implementation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/LuaState_Typedefs.inc ) set(BINDING_DEPENDENCIES tolua - ../Bindings/virtual_method_hooks.lua ../Bindings/AllToLua.pkg + ../Bindings/BindingsProcessor.lua ../Bindings/LuaFunctions.h ../Bindings/LuaWindow.h ../Bindings/Plugin.h @@ -126,7 +133,7 @@ if (NOT MSVC) OUTPUT ${BINDING_OUTPUTS} # Regenerate bindings: - COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg + COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} # add any new generation dependencies here @@ -134,13 +141,12 @@ if (NOT MSVC) ) endif () -set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES GENERATED TRUE) -set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.h PROPERTIES GENERATED TRUE) +set_source_files_properties(${BINDING_OUTPUTS} PROPERTIES GENERATED TRUE) set_source_files_properties(${CMAKE_SOURCE_DIR}/src/Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS -Wno-error) if(NOT MSVC) add_library(Bindings ${SRCS} ${HDRS}) - target_link_libraries(Bindings lua sqlite tolualib polarssl) + target_link_libraries(Bindings lua sqlite tolualib mbedtls) endif() diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 25c77a652..08c7e19d7 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -19,13 +19,13 @@ extern "C" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" -// fwd: SQLite/lsqlite3.c +// fwd: "SQLite/lsqlite3.c" extern "C" { int luaopen_lsqlite3(lua_State * L); } -// fwd: LuaExpat/lxplib.c: +// fwd: "LuaExpat/lxplib.c": extern "C" { int luaopen_lxp(lua_State * L); @@ -107,7 +107,7 @@ void cLuaState::Create(void) void cLuaState::RegisterAPILibs(void) { tolua_AllToLua_open(m_LuaState); - ManualBindings::Bind(m_LuaState); + cManualBindings::Bind(m_LuaState); DeprecatedBindings::Bind(m_LuaState); luaopen_lsqlite3(m_LuaState); luaopen_lxp(m_LuaState); @@ -530,42 +530,6 @@ void cLuaState::Push(bool a_Value) -void cLuaState::Push(cBlockEntity * a_BlockEntity) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_BlockEntity, (a_BlockEntity == nullptr) ? "cBlockEntity" : a_BlockEntity->GetClass()); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cChunkDesc * a_ChunkDesc) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cClientHandle * a_Client) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Client, "cClientHandle"); - m_NumCurrentFunctionArgs += 1; -} - - - - - void cLuaState::Push(cEntity * a_Entity) { ASSERT(IsValid()); @@ -632,42 +596,6 @@ void cLuaState::Push(cEntity * a_Entity) -void cLuaState::Push(cHopperEntity * a_Hopper) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cItem * a_Item) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Item, "cItem"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cItems * a_Items) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Items, "cItems"); - m_NumCurrentFunctionArgs += 1; -} - - - - - void cLuaState::Push(cLuaServerHandle * a_ServerHandle) { ASSERT(IsValid()); @@ -704,114 +632,6 @@ void cLuaState::Push(cLuaUDPEndpoint * a_UDPEndpoint) -void cLuaState::Push(cMonster * a_Monster) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Monster, "cMonster"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cPickup * a_Pickup) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Pickup, "cPickup"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cPlayer * a_Player) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cPluginLua * a_Plugin) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cProjectileEntity * a_ProjectileEntity) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_ProjectileEntity, "cProjectileEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cTNTEntity * a_TNTEntity) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cWebAdmin * a_WebAdmin) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cWindow * a_Window) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Window, "cWindow"); - m_NumCurrentFunctionArgs += 1; -} - - - - - -void cLuaState::Push(cWorld * a_World) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_World, "cWorld"); - m_NumCurrentFunctionArgs += 1; -} - - - - - void cLuaState::Push(double a_Value) { ASSERT(IsValid()); @@ -836,11 +656,18 @@ void cLuaState::Push(int a_Value) -void cLuaState::Push(TakeDamageInfo * a_TDI) +void cLuaState::Push(void * a_Ptr) { + UNUSED(a_Ptr); ASSERT(IsValid()); - tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo"); + // Investigate the cause of this - what is the callstack? + // One code path leading here is the OnHookExploding / OnHookExploded with exotic parameters. Need to decide what to do with them + LOGWARNING("Lua engine: attempting to push a plain pointer, pushing nil instead."); + LOGWARNING("This indicates an unimplemented part of MCS bindings"); + LogStackTrace(); + + lua_pushnil(m_LuaState); m_NumCurrentFunctionArgs += 1; } @@ -848,11 +675,11 @@ void cLuaState::Push(TakeDamageInfo * a_TDI) -void cLuaState::Push(Vector3d * a_Vector) +void cLuaState::Push(std::chrono::milliseconds a_Value) { ASSERT(IsValid()); - tolua_pushusertype(m_LuaState, a_Vector, "Vector3<double>"); + tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count())); m_NumCurrentFunctionArgs += 1; } @@ -860,51 +687,40 @@ void cLuaState::Push(Vector3d * a_Vector) -void cLuaState::Push(Vector3i * a_Vector) +/* +void cLuaState::PushUserType(void * a_Object, const char * a_Type) { ASSERT(IsValid()); - tolua_pushusertype(m_LuaState, a_Vector, "Vector3<int>"); + tolua_pushusertype(m_LuaState, a_Object, a_Type); m_NumCurrentFunctionArgs += 1; } +*/ -void cLuaState::Push(void * a_Ptr) -{ - UNUSED(a_Ptr); - ASSERT(IsValid()); - - // Investigate the cause of this - what is the callstack? - // One code path leading here is the OnHookExploding / OnHookExploded with exotic parameters. Need to decide what to do with them - LOGWARNING("Lua engine: attempting to push a plain pointer, pushing nil instead."); - LOGWARNING("This indicates an unimplemented part of MCS bindings"); - LogStackTrace(); - - lua_pushnil(m_LuaState); - m_NumCurrentFunctionArgs += 1; -} - -void cLuaState::Push(std::chrono::milliseconds a_Value) +void cLuaState::GetStackValue(int a_StackPos, AString & a_Value) { - ASSERT(IsValid()); - - tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count())); - m_NumCurrentFunctionArgs += 1; + size_t len = 0; + const char * data = lua_tolstring(m_LuaState, a_StackPos, &len); + if (data != nullptr) + { + a_Value.assign(data, len); + } } -void cLuaState::PushUserType(void * a_Object, const char * a_Type) +void cLuaState::GetStackValue(int a_StackPos, BLOCKTYPE & a_ReturnedVal) { - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Object, a_Type); - m_NumCurrentFunctionArgs += 1; + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = static_cast<BLOCKTYPE>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)); + } } @@ -920,13 +736,11 @@ void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, AString & a_Value) +void cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result) { - size_t len = 0; - const char * data = lua_tolstring(m_LuaState, a_StackPos, &len); - if (data != nullptr) + if (lua_isnumber(m_LuaState, a_StackPos)) { - a_Value.assign(data, len); + a_Result = static_cast<cPluginManager::CommandResult>(static_cast<int>((tolua_tonumber(m_LuaState, a_StackPos, a_Result)))); } } @@ -934,12 +748,9 @@ void cLuaState::GetStackValue(int a_StackPos, AString & a_Value) -void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) { - if (lua_isnumber(m_LuaState, a_StackPos)) - { - a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); - } + a_Ref.RefStack(*this, a_StackPos); } @@ -958,11 +769,11 @@ void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, float & a_ReturnedVal) { if (lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = (eWeather)Clamp((int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal), (int)wSunny, (int)wThunderstorm); + a_ReturnedVal = static_cast<float>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)); } } @@ -970,35 +781,27 @@ void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) { - if (lua_isnil(m_LuaState, a_StackPos)) + if (!lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = nullptr; return; } - tolua_Error err; - if (tolua_isusertype(m_LuaState, a_StackPos, "cBoundingBox", false, &err)) - { - a_ReturnedVal = *((cBoundingBox **)lua_touserdata(m_LuaState, a_StackPos)); - } + a_ReturnedVal = static_cast<eWeather>(Clamp( + static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)), + static_cast<int>(wSunny), static_cast<int>(wThunderstorm)) + ); } -void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal) { - if (lua_isnil(m_LuaState, a_StackPos)) - { - a_ReturnedVal = nullptr; - return; - } - tolua_Error err; - if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err)) + if (lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = *((cWorld **)lua_touserdata(m_LuaState, a_StackPos)); + a_ReturnedVal = static_cast<int>(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)); } } @@ -1006,15 +809,6 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) -{ - a_Ref.RefStack(*this, a_StackPos); -} - - - - - bool cLuaState::CallFunction(int a_NumResults) { ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first @@ -1133,6 +927,9 @@ bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam) VERIFY(lua_getstack(m_LuaState, 0, &entry)); VERIFY(lua_getinfo (m_LuaState, "n", &entry)); AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?"); + + BreakIntoDebugger(m_LuaState); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); return false; } // for i - Param @@ -1280,7 +1077,7 @@ bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam) bool cLuaState::CheckParamEnd(int a_Param) { tolua_Error tolua_err; - if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err)) + if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err) == 1) { return true; } @@ -1297,6 +1094,30 @@ bool cLuaState::CheckParamEnd(int a_Param) +bool cLuaState::IsParamUserType(int a_Param, AString a_UserType) +{ + ASSERT(IsValid()); + + tolua_Error tolua_err; + return (tolua_isusertype(m_LuaState, a_Param, a_UserType.c_str(), 0, &tolua_err) == 1); +} + + + + + +bool cLuaState::IsParamNumber(int a_Param) +{ + ASSERT(IsValid()); + + tolua_Error tolua_err; + return (tolua_isnumber(m_LuaState, a_Param, 0, &tolua_err) == 1); +} + + + + + bool cLuaState::ReportErrors(int a_Status) { return ReportErrors(m_LuaState, a_Status); @@ -1376,7 +1197,7 @@ int cLuaState::CallFunctionWithForeignParams( if (!PushFunction(a_FunctionName.c_str())) { LOGWARNING("Function '%s' not found", a_FunctionName.c_str()); - lua_pop(m_LuaState, 2); + lua_settop(m_LuaState, OldTop); return -1; } @@ -1384,7 +1205,7 @@ int cLuaState::CallFunctionWithForeignParams( if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0) { // Something went wrong, fix the stack and exit - lua_pop(m_LuaState, 2); + lua_settop(m_LuaState, OldTop); m_NumCurrentFunctionArgs = -1; m_CurrentFunctionName.clear(); return -1; @@ -1395,13 +1216,8 @@ int cLuaState::CallFunctionWithForeignParams( if (ReportErrors(s)) { LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str()); - // Fix the stack. - // We don't know how many values have been pushed, so just get rid of any that weren't there initially - int CurTop = lua_gettop(m_LuaState); - if (CurTop > OldTop) - { - lua_pop(m_LuaState, CurTop - OldTop); - } + // Reset the stack: + lua_settop(m_LuaState, OldTop); // Reset the internal checking mechanisms: m_NumCurrentFunctionArgs = -1; @@ -1553,6 +1369,7 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) { LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); LogStackTrace(a_LuaState, 1); + BreakIntoDebugger(a_LuaState); return 1; // We left the error message on the stack as the return value } @@ -1560,6 +1377,28 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) +int cLuaState::BreakIntoDebugger(lua_State * a_LuaState) +{ + // Call the BreakIntoDebugger function, if available: + lua_getglobal(a_LuaState, "BreakIntoDebugger"); + if (!lua_isfunction(a_LuaState, -1)) + { + LOGD("LUA: BreakIntoDebugger() not found / not a function"); + lua_pop(a_LuaState, 1); + return 1; + } + lua_insert(a_LuaState, -2); // Copy the string that has been passed to us + LOGD("Calling BreakIntoDebugger()..."); + lua_call(a_LuaState, 1, 0); + LOGD("Returned from BreakIntoDebugger()."); + + return 0; +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 159483634..5b4ec3ae4 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -32,40 +32,14 @@ extern "C" #include "../Vector3.h" #include "../Defines.h" +#include "PluginManager.h" +#include "LuaState_Typedefs.inc" - - - - -class cWorld; -class cPlayer; -class cEntity; -class cProjectileEntity; -class cMonster; -class cItem; -class cItems; -class cClientHandle; -class cPickup; -class cChunkDesc; -class cCraftingGrid; -class cCraftingRecipe; -struct TakeDamageInfo; -class cWindow; -class cPluginLua; -struct HTTPRequest; -class cWebAdmin; -struct HTTPTemplateRequest; -class cTNTEntity; -class cHopperEntity; -class cBlockEntity; -class cBoundingBox; -class cLuaTCPLink; +// fwd: class cLuaServerHandle; +class cLuaTCPLink; class cLuaUDPEndpoint; -typedef cBoundingBox * pBoundingBox; -typedef cWorld * pWorld; - @@ -202,59 +176,30 @@ public: void Push(const Vector3i & a_Vector); void Push(const Vector3i * a_Vector); - // Push a value onto the stack (keep alpha-sorted): + // Push a simple value onto the stack (keep alpha-sorted): void Push(bool a_Value); - void Push(cBlockEntity * a_BlockEntity); - void Push(cChunkDesc * a_ChunkDesc); - void Push(cClientHandle * a_ClientHandle); - void Push(cEntity * a_Entity); - void Push(cHopperEntity * a_Hopper); - void Push(cItem * a_Item); - void Push(cItems * a_Items); - void Push(cLuaServerHandle * a_ServerHandle); - void Push(cLuaTCPLink * a_TCPLink); - void Push(cLuaUDPEndpoint * a_UDPEndpoint); - void Push(cMonster * a_Monster); - void Push(cPickup * a_Pickup); - void Push(cPlayer * a_Player); - void Push(cPluginLua * a_Plugin); - void Push(cProjectileEntity * a_ProjectileEntity); - void Push(cTNTEntity * a_TNTEntity); - void Push(cWebAdmin * a_WebAdmin); - void Push(cWindow * a_Window); - void Push(cWorld * a_World); void Push(double a_Value); void Push(int a_Value); - void Push(TakeDamageInfo * a_TDI); - void Push(Vector3d * a_Vector); - void Push(Vector3i * a_Vector); void Push(void * a_Ptr); void Push(std::chrono::milliseconds a_time); + void Push(cLuaServerHandle * a_ServerHandle); + void Push(cLuaTCPLink * a_TCPLink); + void Push(cLuaUDPEndpoint * a_UDPEndpoint); - /** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */ - void GetStackValue(int a_StackPos, bool & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid string. If not, a_Value is unchanged */ + // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged. + // Enum values are clamped to their allowed range. void GetStackValue(int a_StackPos, AString & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */ - void GetStackValue(int a_StackPos, int & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */ + void GetStackValue(int a_StackPos, BLOCKTYPE & a_Value); + void GetStackValue(int a_StackPos, bool & a_Value); + void GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result); + void GetStackValue(int a_StackPos, cRef & a_Ref); void GetStackValue(int a_StackPos, double & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather. - If not, a_Value is unchanged. */ void GetStackValue(int a_StackPos, eWeather & a_Value); + void GetStackValue(int a_StackPos, float & a_ReturnedVal); + void GetStackValue(int a_StackPos, int & a_Value); - /** Retrieve value at a_StackPos, if it is a valid cBoundingBox class. If not, a_Value is unchanged */ - void GetStackValue(int a_StackPos, pBoundingBox & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */ - void GetStackValue(int a_StackPos, pWorld & a_Value); - - /** Store the value at a_StackPos as a reference. */ - void GetStackValue(int a_StackPos, cRef & a_Ref); + // Include the auto-generated Push and GetStackValue() functions: + #include "LuaState_Declaration.inc" /** Call the specified Lua function. Returns true if call succeeded, false if there was an error. @@ -303,6 +248,10 @@ public: /** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */ bool CheckParamEnd(int a_Param); + bool IsParamUserType(int a_Param, AString a_UserType); + + bool IsParamNumber(int a_Param); + /** If the status is nonzero, prints the text on the top of Lua stack and returns true */ bool ReportErrors(int status); @@ -429,7 +378,7 @@ protected: bool PushFunction(const cTableRef & a_TableRef); /** Pushes a usertype of the specified class type onto the stack */ - void PushUserType(void * a_Object, const char * a_Type); + // void PushUserType(void * a_Object, const char * a_Type); /** Calls the function that has been pushed onto the stack by PushFunction(), @@ -440,6 +389,9 @@ protected: /** Used as the error reporting function for function calls */ static int ReportFnCallErrors(lua_State * a_LuaState); + + /** Tries to break into the MobDebug debugger, if it is installed. */ + static int BreakIntoDebugger(lua_State * a_LuaState); } ; diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index d4014059b..5f7832ef5 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -159,7 +159,7 @@ void cLuaWindow::Destroy(void) m_Plugin->Unreference(m_LuaRef); } - // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! + // Lua will take care of this object, it will garbage-collect it, so we must not delete it! m_IsDestroyed = false; } @@ -167,10 +167,10 @@ void cLuaWindow::Destroy(void) -void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) { cSlotAreas Areas; - for (auto Area : m_SlotAreas) + for (auto && Area : m_SlotAreas) { if (Area != a_ClickedArea) { diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 6e579b364..ff904d74a 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -5,6 +5,7 @@ #undef TOLUA_TEMPLATE_BIND #include <sstream> #include <iomanip> +#include <array> #include "tolua++/include/tolua++.h" #include "polarssl/md5.h" #include "polarssl/sha1.h" @@ -32,13 +33,14 @@ #include "../WorldStorage/SchematicFileSerializer.h" #include "../CompositeChat.h" #include "../StringCompression.h" +#include "../CommandOutput.h" // Better error reporting for Lua -static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError) +int cManualBindings::tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError) { // Retrieve current function name lua_Debug entry; @@ -58,7 +60,7 @@ static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pTo -static int lua_do_error(lua_State* L, const char * a_pFormat, ...) +int cManualBindings::lua_do_error(lua_State * L, const char * a_pFormat, ...) { // Retrieve current function name lua_Debug entry; @@ -67,7 +69,7 @@ static int lua_do_error(lua_State* L, const char * a_pFormat, ...) // Insert function name into error msg AString msg(a_pFormat); - ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); + ReplaceString(msg, "#funcname#", (entry.name != nullptr) ? entry.name : "?"); // Copied from luaL_error and modified va_list argp; @@ -90,12 +92,12 @@ static int tolua_Clamp(lua_State * tolua_S) int NumArgs = lua_gettop(LuaState); if (NumArgs != 3) { - return lua_do_error(LuaState, "Error in function call '#funcname#': Requires 3 arguments, got %i", NumArgs); + return cManualBindings::lua_do_error(LuaState, "Error in function call '#funcname#': Requires 3 arguments, got %i", NumArgs); } if (!lua_isnumber(LuaState, 1) || !lua_isnumber(LuaState, 2) || !lua_isnumber(LuaState, 3)) { - return lua_do_error(LuaState, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); + return cManualBindings::lua_do_error(LuaState, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); } lua_Number Number = tolua_tonumber(LuaState, 1, 0); @@ -253,12 +255,13 @@ static int tolua_InflateString(lua_State * tolua_S) static int tolua_StringSplit(lua_State * tolua_S) { + // Get the params: cLuaState LuaState(tolua_S); - std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); - std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + AString str, delim; + LuaState.GetStackValues(1, str, delim); - AStringVector Split = StringSplit(str, delim); - LuaState.Push(Split); + // Execute and push the result: + LuaState.Push(StringSplit(str, delim)); return 1; } @@ -286,12 +289,20 @@ static int tolua_StringSplitWithQuotes(lua_State * tolua_S) static int tolua_StringSplitAndTrim(lua_State * tolua_S) { - cLuaState LuaState(tolua_S); - std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); - std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamString(1, 2) || + !L.CheckParamEnd(3) + ) + { + return 0; + } - AStringVector Split = StringSplitAndTrim(str, delim); - LuaState.Push(Split); + // Process: + AString str, delim; + L.GetStackValues(1, str, delim); + L.Push(StringSplitAndTrim(str, delim)); return 1; } @@ -450,7 +461,7 @@ static int tolua_Base64Decode(lua_State * tolua_S) -cPluginLua * GetLuaPlugin(lua_State * L) +cPluginLua * cManualBindings::GetLuaPlugin(lua_State * L) { // Get the plugin identification out of LuaState: lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); @@ -472,6 +483,7 @@ cPluginLua * GetLuaPlugin(lua_State * L) static int tolua_cFile_GetFolderContents(lua_State * tolua_S) { + // Check params: cLuaState LuaState(tolua_S); if ( !LuaState.CheckParamUserTable(1, "cFile") || @@ -482,10 +494,12 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S) return 0; } - AString Folder = (AString)tolua_tocppstring(LuaState, 2, 0); + // Get params: + AString Folder; + LuaState.GetStackValues(2, Folder); - AStringVector Contents = cFile::GetFolderContents(Folder); - LuaState.Push(Contents); + // Execute and push result: + LuaState.Push(cFile::GetFolderContents(Folder)); return 1; } @@ -493,447 +507,25 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S) -template < - class Ty1, - class Ty2, - bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &) -> -static int tolua_DoWith(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 2) && (NumArgs != 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr); - - const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); - if ((ItemName == nullptr) || (ItemName[0] == 0)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs); - } - if (!lua_isfunction( tolua_S, 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 3) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); - } - - class cLuaCallback : public cItemCallback<Ty2> - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - - bool bRetVal = (self->*Func1)(ItemName, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -template < - class Ty1, - class Ty2, - bool (Ty1::*Func1)(UInt32, cItemCallback<Ty2> &) -> -static int tolua_DoWithID(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 2) && (NumArgs != 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, nullptr); - - int ItemID = (int)tolua_tonumber(tolua_S, 2, 0); - if (!lua_isfunction(tolua_S, 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 3) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); - } - - class cLuaCallback : public cItemCallback<Ty2> - { - public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef, int a_TableRef) : - LuaState(a_LuaState), - FuncRef(a_FuncRef), - TableRef(a_TableRef) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call - tolua_pushusertype(LuaState, a_Item, a_Item->GetClass()); // Push the item - if (TableRef != LUA_REFNIL) - { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - - bool bRetVal = (self->*Func1)(ItemID, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -template < - class Ty1, - class Ty2, - bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &) -> -static int tolua_DoWithXYZ(lua_State* tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 4) && (NumArgs != 5)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr); - if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); - } - - int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0)); - int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0)); - int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0)); - if (!lua_isfunction( tolua_S, 5)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4"); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 5) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #5"); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #4"); - } - - class cLuaCallback : public cItemCallback<Ty2> - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(ItemX, ItemY, ItemZ, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -template < - class Ty1, - class Ty2, - bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &) -> -static int tolua_ForEachInChunk(lua_State * tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 3) && (NumArgs != 4)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr); - if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2"); - } - - int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0)); - int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0)); - - if (!lua_isfunction( tolua_S, 4)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3"); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 4) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #4"); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #3"); - } - - class cLuaCallback : public cItemCallback<Ty2> - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(ChunkX, ChunkZ, Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -template < - class Ty1, - class Ty2, - bool (Ty1::*Func1)(const cBoundingBox &, cItemCallback<Ty2> &) -> -static int tolua_ForEachInBox(lua_State * tolua_S) +static int tolua_cFile_ReadWholeFile(lua_State * tolua_S) { // Check params: - cLuaState L(tolua_S); + cLuaState LuaState(tolua_S); if ( - !L.CheckParamUserType(1, "cWorld") || - !L.CheckParamUserType(2, "cBoundingBox") || - !L.CheckParamFunction(3) || - !L.CheckParamEnd(4) + !LuaState.CheckParamUserTable(1, "cFile") || + !LuaState.CheckParamString (2) || + !LuaState.CheckParamEnd (3) ) { return 0; } - - // Get the params: - Ty1 * Self = nullptr; - cBoundingBox * Box = nullptr; - L.GetStackValues(1, Self, Box); - if ((Self == nullptr) || (Box == nullptr)) - { - LOGWARNING("Invalid world (%p) or boundingbox (%p)", Self, Box); - L.LogStackTrace(); - return 0; - } - // Create a reference for the function: - cLuaState::cRef FnRef(L, 3); + // Get params: + AString FileName; + LuaState.GetStackValues(2, FileName); - // Callback wrapper for the Lua function: - class cLuaCallback : public cItemCallback<Ty2> - { - public: - cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) : - m_LuaState(a_LuaState), - m_FnRef(a_FuncRef) - {} - - private: - // cItemCallback<Ty2> overrides: - virtual bool Item(Ty2 * a_Item) override - { - bool res = false; - if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res)) - { - LOGWARNING("Failed to call Lua callback"); - m_LuaState.LogStackTrace(); - return true; // Abort enumeration - } - - return res; - } - cLuaState & m_LuaState; - cLuaState::cRef & m_FnRef; - } Callback(L, FnRef); - - bool bRetVal = (Self->*Func1)(*Box, Callback); - - FnRef.UnRef(); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); + // Execute and push result: + LuaState.Push(cFile::ReadWholeFile(FileName)); return 1; } @@ -941,535 +533,30 @@ static int tolua_ForEachInBox(lua_State * tolua_S) -template < - class Ty1, - class Ty2, - bool (Ty1::*Func1)(cItemCallback<Ty2> &) -> -static int tolua_ForEach(lua_State * tolua_S) -{ - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 1) && (NumArgs != 2)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs); - } - - Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, nullptr); - if (self == nullptr) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); - } - - if (!lua_isfunction( tolua_S, 2)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); - } - - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 2) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2"); - } - } - - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); - } - - class cLuaCallback : public cItemCallback<Ty2> - { - public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} - - private: - virtual bool Item(Ty2 * a_Item) override - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean( LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ - } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); - - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); - return 1; -} - - - - - -static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight) - // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight] - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 5, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - #ifndef TOLUA_RELEASE - if (self == nullptr) - { - tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", nullptr); - } - #endif - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight; - bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushnumber(tolua_S, BlockType); - tolua_pushnumber(tolua_S, BlockMeta); - tolua_pushnumber(tolua_S, BlockSkyLight); - tolua_pushnumber(tolua_S, BlockBlockLight); - return 5; - } - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err); - return 0; - #endif -} - - - - - -static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta) - // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta] - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 5, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - #ifndef TOLUA_RELEASE - if (self == nullptr) - { - tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", nullptr); - } - #endif - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushnumber(tolua_S, BlockType); - tolua_pushnumber(tolua_S, BlockMeta); - return 3; - } - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err); - return 0; - #endif -} - - - - - -static int tolua_cWorld_GetSignLines(lua_State * tolua_S) -{ - // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4) - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 10, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - #ifndef TOLUA_RELEASE - if (self == nullptr) - { - tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", nullptr); - } - #endif - { - AString Line1, Line2, Line3, Line4; - bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushstring(tolua_S, Line1.c_str()); - tolua_pushstring(tolua_S, Line2.c_str()); - tolua_pushstring(tolua_S, Line3.c_str()); - tolua_pushstring(tolua_S, Line4.c_str()); - return 5; - } - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err); - return 0; - #endif -} - - - - - -static int tolua_cWorld_SetSignLines(lua_State * tolua_S) +static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) { - // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4) - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 5, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 6, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 7, 0, &tolua_err) || - !tolua_iscppstring(tolua_S, 8, 0, &tolua_err) || - !tolua_isusertype (tolua_S, 9, "cPlayer", 1, &tolua_err) || - !tolua_isnoobj (tolua_S, 10, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); - const AString Line1 = tolua_tocppstring(tolua_S, 5, 0); - const AString Line2 = tolua_tocppstring(tolua_S, 6, 0); - const AString Line3 = tolua_tocppstring(tolua_S, 7, 0); - const AString Line4 = tolua_tocppstring(tolua_S, 8, 0); - cPlayer * Player = (cPlayer *)tolua_tousertype (tolua_S, 9, nullptr); - #ifndef TOLUA_RELEASE - if (self == nullptr) - { - tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines'", nullptr); - } - #endif - { - bool res = self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player); - tolua_pushboolean(tolua_S, res ? 1 : 0); - } - } - return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'SetSignLines'.", &tolua_err); - return 0; - #endif -} - - - + // API function no longer available: + LOGWARNING("cPluginManager:GetAllPlugins() is no longer available, use cPluginManager:ForEachPlugin() instead"); + cLuaState::LogStackTrace(tolua_S); -static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) -{ - // Exported manually, because tolua would require the out-only param a_Height to be used when calling - // Takes a_World, a_BlockX, a_BlockZ - // Returns Height, IsValid - #ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || - !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || - !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || - !tolua_isnoobj (tolua_S, 4, &tolua_err) - ) - goto tolua_lerror; - else - #endif - { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, nullptr); - int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); - int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0); - #ifndef TOLUA_RELEASE - if (self == nullptr) - { - tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", nullptr); - } - #endif - { - int Height = 0; - bool res = self->TryGetHeight(BlockX, BlockZ, Height); - tolua_pushboolean(tolua_S, res ? 1 : 0); - if (res) - { - tolua_pushnumber(tolua_S, Height); - return 2; - } - } - } + // Return an empty table: + lua_newtable(tolua_S); return 1; - - #ifndef TOLUA_RELEASE -tolua_lerror: - tolua_error(tolua_S, "#ferror in function 'TryGetHeight'.", &tolua_err); - return 0; - #endif } -class cLuaWorldTask : - public cWorld::cTask, - public cPluginLua::cResettable -{ -public: - cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - cPluginLua::cResettable(a_Plugin), - m_FnRef(a_FnRef) - { - } - -protected: - int m_FnRef; - - // cWorld::cTask overrides: - virtual void Run(cWorld & a_World) override - { - cCSLock Lock(m_CSPlugin); - if (m_Plugin != nullptr) - { - m_Plugin->Call(m_FnRef, &a_World); - } - } -} ; - - - - - -static int tolua_cWorld_QueueTask(lua_State * tolua_S) -{ - // Binding for cWorld::QueueTask - // Params: function - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // An error message has been already printed in GetLuaPlugin() - return 0; - } - - // Retrieve the args: - cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr); - if (self == nullptr) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); - } - if (!lua_isfunction(tolua_S, 2)) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); - } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); - } - - auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(task); - self->QueueTask(task); - return 0; -} - - - - - -class cLuaScheduledWorldTask : - public cWorld::cTask, - public cPluginLua::cResettable -{ -public: - cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - cPluginLua::cResettable(a_Plugin), - m_FnRef(a_FnRef) - { - } - -protected: - int m_FnRef; - - // cWorld::cTask overrides: - virtual void Run(cWorld & a_World) override - { - cCSLock Lock(m_CSPlugin); - if (m_Plugin != nullptr) - { - m_Plugin->Call(m_FnRef, &a_World); - } - } -}; - - - - - -static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) +static int tolua_cPluginManager_GetCurrentPlugin(lua_State * S) { - // Binding for cWorld::ScheduleTask - // Params: function, Ticks - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = GetLuaPlugin(tolua_S); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(S); if (Plugin == nullptr) { - // An error message has been already printed in GetLuaPlugin() - return 0; - } - - // Retrieve the args: - cLuaState L(tolua_S); - if ( - !L.CheckParamUserType(1, "cWorld") || - !L.CheckParamNumber (2) || - !L.CheckParamFunction(3) - ) - { + // An error message has already been printed in GetLuaPlugin() return 0; } - cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr); - if (World == nullptr) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); - } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) - { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); - } - - int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0); - - auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(task); - World->ScheduleTask(DelayTicks, task); - return 0; -} - - - - - -static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) -{ - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr); - - const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); - - lua_newtable(tolua_S); - int index = 1; - cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); - while (iter != AllPlugins.end()) - { - const cPlugin* Plugin = iter->second; - tolua_pushstring(tolua_S, iter->first.c_str()); - if (Plugin != nullptr) - { - tolua_pushusertype(tolua_S, (void *)Plugin, "const cPlugin"); - } - else - { - tolua_pushboolean(tolua_S, 0); - } - lua_rawset(tolua_S, -3); - ++iter; - ++index; - } + tolua_pushusertype(S, Plugin, "cPluginLua"); return 1; } @@ -1477,16 +564,12 @@ static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) -static int tolua_cPluginManager_GetCurrentPlugin(lua_State * S) +static int tolua_cPluginManager_GetPlugin(lua_State * tolua_S) { - cPluginLua * Plugin = GetLuaPlugin(S); - if (Plugin == nullptr) - { - // An error message has already been printed in GetLuaPlugin() - return 0; - } - tolua_pushusertype(S, Plugin, "cPluginLua"); - return 1; + // API function no longer available: + LOGWARNING("cPluginManager:GetPlugin() is no longer available. Use cPluginManager:DoWithPlugin() or cPluginManager:CallPlugin() instead."); + cLuaState::LogStackTrace(tolua_S); + return 0; } @@ -1510,7 +593,7 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, // The arg types have already been checked // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = GetLuaPlugin(S); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(S); if (Plugin == nullptr) { // An error message has been already printed in GetLuaPlugin() @@ -1557,7 +640,7 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, S.LogStackTrace(); return 0; } - if (Plugin != GetLuaPlugin(S)) + if (Plugin != cManualBindings::GetLuaPlugin(S)) { // The plugin parameter passed to us is not our stored plugin. Disallow this! LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added."); @@ -1664,74 +747,55 @@ static int tolua_cPluginManager_AddHook(lua_State * tolua_S) static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) { - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if (NumArgs != 1) - { - LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs); - return 0; - } - - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr); - if (self == nullptr) - { - LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); - return 0; - } + /* + Function signature: + cPluginManager:Get():ForEachCommand(a_CallbackFn) -> bool + */ - if (!lua_isfunction(tolua_S, 2)) + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cPluginManager") || + !L.CheckParamFunction(2) || + !L.CheckParamEnd(3) + ) { - LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1"); return 0; } - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) + // Get the params: + cLuaState::cRef FnRef; + L.GetStackValues(2, FnRef); + if (!FnRef.IsValid()) { LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1"); return 0; } + // Callback for enumerating all commands: class cLuaCallback : public cPluginManager::cCommandEnumCallback { public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - {} + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } private: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override { UNUSED(a_Plugin); - - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushcppstring(LuaState, a_Command); - tolua_pushcppstring(LuaState, a_Permission); - tolua_pushcppstring(LuaState, a_HelpString); - - int s = lua_pcall(LuaState, 3, 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean( LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ + bool ret = false; + m_LuaState.Call(m_FnRef, a_Command, a_Permission, a_HelpString, cLuaState::Return, ret); + return ret; } - lua_State * LuaState; - int FuncRef; - } Callback(tolua_S, FuncRef); - - bool bRetVal = self->ForEachCommand(Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); + // Execute and push the returned value: + L.Push(cPluginManager::Get()->ForEachCommand(Callback)); return 1; } @@ -1741,74 +805,56 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) { - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if (NumArgs != 1) - { - LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs); - return 0; - } - - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr); - if (self == nullptr) - { - LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); - return 0; - } + /* + Function signature: + cPluginManager:Get():ForEachConsoleCommand(a_CallbackFn) -> bool + */ - if (!lua_isfunction(tolua_S, 2)) + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cPluginManager") || + !L.CheckParamFunction(2) || + !L.CheckParamEnd(3) + ) { - LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1"); return 0; } - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) + // Get the params: + cLuaState::cRef FnRef; + L.GetStackValues(2, FnRef); + if (!FnRef.IsValid()) { LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1"); return 0; } + // Callback for enumerating all commands: class cLuaCallback : public cPluginManager::cCommandEnumCallback { public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - {} + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } private: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override { UNUSED(a_Plugin); UNUSED(a_Permission); - - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushcppstring(LuaState, a_Command); - tolua_pushcppstring(LuaState, a_HelpString); - - int s = lua_pcall(LuaState, 2, 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean( LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ + bool ret = false; + m_LuaState.Call(m_FnRef, a_Command, a_HelpString, cLuaState::Return, ret); + return ret; } - lua_State * LuaState; - int FuncRef; - } Callback(tolua_S, FuncRef); - - bool bRetVal = self->ForEachConsoleCommand(Callback); - - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); + // Execute and push the returned value: + L.Push(cPluginManager::Get()->ForEachConsoleCommand(Callback)); return 1; } @@ -1822,7 +868,7 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) cPluginManager:BindCommand(Command, Permission, Function, HelpString) cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param */ - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { return 0; @@ -1892,7 +938,7 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) */ // Get the plugin identification out of LuaState: - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { return 0; @@ -1980,7 +1026,7 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) } // If requesting calling the current plugin, refuse: - cPluginLua * ThisPlugin = GetLuaPlugin(L); + cPluginLua * ThisPlugin = cManualBindings::GetLuaPlugin(L); if (ThisPlugin == nullptr) { return 0; @@ -2011,7 +1057,11 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) virtual bool Item(cPlugin * a_Plugin) override { - m_NumReturns = ((cPluginLua *)a_Plugin)->CallFunctionFromForeignState( + if (!a_Plugin->IsLoaded()) + { + return false; + } + m_NumReturns = static_cast<cPluginLua *>(a_Plugin)->CallFunctionFromForeignState( m_FunctionName, m_SrcLuaState, 4, lua_gettop(m_SrcLuaState) ); return true; @@ -2019,9 +1069,11 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) } Callback(FunctionName, L); if (!cPluginManager::Get()->DoWithPlugin(PluginName, Callback)) { - // TODO 2014_01_20 _X: This might be too much logging, plugins cannot know if other plugins are loaded (async) - LOGWARNING("cPluginManager::CallPlugin: No such plugin name (\"%s\")", PluginName.c_str()); - L.LogStackTrace(); + return 0; + } + if (Callback.m_NumReturns < 0) + { + // The call has failed, there are zero return values. Do NOT return negative number (Lua considers that a "yield") return 0; } return Callback.m_NumReturns; @@ -2031,48 +1083,48 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) -static int tolua_cWorld_ChunkStay(lua_State * tolua_S) +static int tolua_cPluginManager_ExecuteConsoleCommand(lua_State * tolua_S) { - /* Function signature: - World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable) - ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... } + /* + Function signature: + cPluginManager:ExecuteConsoleCommand(EntireCommandStr) -> OutputString */ - + + // Check params: cLuaState L(tolua_S); if ( - !L.CheckParamUserType (1, "cWorld") || - !L.CheckParamTable (2) || - !L.CheckParamFunctionOrNil(3, 4) + !L.CheckParamUserTable(1, "cPluginManager") || + !L.CheckParamString(2) || + !L.CheckParamEnd(3) ) { return 0; } - - cPluginLua * Plugin = GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - return 0; - } - - // Read the params: - cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr); - if (World == nullptr) - { - LOGWARNING("World:ChunkStay(): invalid world parameter"); - L.LogStackTrace(); - return 0; - } - cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin); + // Get the params: + AString Command; + L.GetStackValues(2, Command); + auto Split = StringSplit(Command, " "); + + // Store the command output in a string: + cStringAccumCommandOutputCallback CommandOutput; + L.Push(cPluginManager::Get()->ExecuteConsoleCommand(Split, CommandOutput, Command)); + L.Push(CommandOutput.GetAccum()); + return 2; +} - if (!ChunkStay->AddChunks(2)) - { - delete ChunkStay; - ChunkStay = nullptr; - return 0; - } - ChunkStay->Enable(*World->GetChunkMap(), 3, 4); + + + +static int tolua_cPluginManager_FindPlugins(lua_State * tolua_S) +{ + // API function no longer exists: + LOGWARNING("cPluginManager:FindPlugins() is obsolete, use cPluginManager:RefreshPluginList() instead!"); + cLuaState::LogStackTrace(tolua_S); + + // Still, do the actual work performed by the API function when it existed: + cPluginManager::Get()->RefreshPluginList(); return 0; } @@ -2080,75 +1132,40 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) -static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) +static int tolua_cPlayer_GetPermissions(lua_State * tolua_S) { - /* Function signature: - World:PrepareChunk(ChunkX, ChunkZ, Callback) - */ - - // Check the param types: + // Function signature: cPlayer:GetPermissions() -> {permissions-array} + + // Check the params: cLuaState L(tolua_S); if ( - !L.CheckParamUserType (1, "cWorld") || - !L.CheckParamNumber (2, 3) || - !L.CheckParamFunctionOrNil(4) + !L.CheckParamUserType(1, "cPlayer") || + !L.CheckParamEnd (2) ) { return 0; } - - // Read the params: - cWorld * world = nullptr; - int chunkX = 0, chunkZ = 0; - L.GetStackValues(1, world, chunkX, chunkZ); - if (world == nullptr) + + // Get the params: + cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, nullptr); + if (self == nullptr) { - LOGWARNING("World:PrepareChunk(): invalid world parameter"); - L.LogStackTrace(); + LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); return 0; } - - // Wrap the Lua callback inside a C++ callback class: - class cCallback: - public cChunkCoordCallback - { - public: - cCallback(lua_State * a_LuaState): - m_LuaState(a_LuaState), - m_Callback(m_LuaState, 4) - { - } - - // cChunkCoordCallback override: - virtual void Call(int a_CBChunkX, int a_CBChunkZ) override - { - if (m_Callback.IsValid()) - { - m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ); - } - - // This is the last reference of this object, we must delete it so that it doesn't leak: - delete this; - } - - protected: - cLuaState m_LuaState; - cLuaState::cRef m_Callback; - }; - cCallback * callback = new cCallback(tolua_S); - - // Call the chunk preparation: - world->PrepareChunk(chunkX, chunkZ, callback); - return 0; + + // Push the permissions: + L.Push(self->GetPermissions()); + return 1; } -static int tolua_cPlayer_GetPermissions(lua_State * tolua_S) +static int tolua_cPlayer_GetRestrictions(lua_State * tolua_S) { - // Function signature: cPlayer:GetPermissions() -> {permissions-array} + // Function signature: cPlayer:GetRestrictions() -> {restrictions-array} // Check the params: cLuaState L(tolua_S); @@ -2169,7 +1186,7 @@ static int tolua_cPlayer_GetPermissions(lua_State * tolua_S) } // Push the permissions: - L.Push(self->GetPermissions()); + L.Push(self->GetRestrictions()); return 1; } @@ -2182,7 +1199,7 @@ static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) // Function signature: cPlayer:OpenWindow(Window) // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = GetLuaPlugin(tolua_S); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); if (Plugin == nullptr) { return 0; @@ -2263,7 +1280,7 @@ static int tolua_SetObjectCallback(lua_State * tolua_S) // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = GetLuaPlugin(tolua_S); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); if (Plugin == nullptr) { // Warning message has already been printed by GetLuaPlugin(), bail out silently @@ -2315,7 +1332,7 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) } else { - return tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); + return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); } if (Reference != LUA_REFNIL) @@ -2337,40 +1354,40 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) -static int tolua_cPluginLua_AddTab(lua_State* tolua_S) +static int tolua_cPlugin_GetDirectory(lua_State * tolua_S) { - cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, nullptr); - LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")", - self->GetName().c_str(), self->GetDirectory().c_str() - ); - return tolua_cPluginLua_AddWebTab( tolua_S); + cLuaState L(tolua_S); + + // Log the obsoletion warning: + LOGWARNING("cPlugin:GetDirectory() is obsolete, use cPlugin:GetFolderName() instead."); + L.LogStackTrace(); + + // Retrieve the params: + cPlugin * Plugin = static_cast<cPluginLua *>(tolua_tousertype(tolua_S, 1, nullptr)); + + // Get the folder name: + L.Push(Plugin->GetFolderName()); + return 1; } -static int tolua_cPlugin_Call(lua_State * tolua_S) +static int tolua_cPlugin_GetLocalDirectory(lua_State * tolua_S) { cLuaState L(tolua_S); // Log the obsoletion warning: - LOGWARNING("cPlugin:Call() is obsolete and unsafe, use cPluginManager:CallPlugin() instead."); + LOGWARNING("cPlugin:GetLocalDirectory() is obsolete, use cPlugin:GetLocalFolder() instead."); L.LogStackTrace(); - // Retrieve the params: plugin and the function name to call - cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, nullptr); - AString FunctionName = tolua_tostring(tolua_S, 2, ""); + // Retrieve the params: + cPlugin * Plugin = static_cast<cPluginLua *>(tolua_tousertype(tolua_S, 1, nullptr)); - // Call the function: - int NumReturns = TargetPlugin->CallFunctionFromForeignState(FunctionName, L, 3, lua_gettop(L)); - if (NumReturns < 0) - { - LOGWARNING("cPlugin::Call() failed to call destination function"); - L.LogStackTrace(); - return 0; - } - return NumReturns; + // Get the folder: + L.Push(Plugin->GetLocalFolder()); + return 1; } @@ -2488,8 +1505,8 @@ static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, for (std::map<std::string, std::string>::iterator it = a_StringStringMap.begin(); it != a_StringStringMap.end(); ++it) { - const char* key = it->first.c_str(); - const char* value = it->second.c_str(); + const char * key = it->first.c_str(); + const char * value = it->second.c_str(); lua_pushstring(tolua_S, key); lua_pushstring(tolua_S, value); lua_settable(tolua_S, top); @@ -2504,7 +1521,7 @@ static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) { - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, nullptr); + HTTPRequest * self = reinterpret_cast<HTTPRequest *>(tolua_tousertype(tolua_S, 1, nullptr)); return tolua_push_StringStringMap(tolua_S, self->Params); } @@ -2514,7 +1531,7 @@ static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) { - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, nullptr); + HTTPRequest * self = reinterpret_cast<HTTPRequest *>(tolua_tousertype(tolua_S, 1, nullptr)); return tolua_push_StringStringMap(tolua_S, self->PostParams); } @@ -2524,8 +1541,8 @@ static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S) { - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, nullptr); - std::map< std::string, HTTPFormData >& FormData = self->FormData; + HTTPRequest * self = reinterpret_cast<HTTPRequest *>(tolua_tousertype(tolua_S, 1, nullptr)); + std::map<std::string, HTTPFormData> & FormData = self->FormData; lua_newtable(tolua_S); int top = lua_gettop(tolua_S); @@ -2628,22 +1645,16 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) { - cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S, 1, nullptr); - - const cWebPlugin::TabNameList & TabNames = self->GetTabNames(); - + // Returns a map of (SafeTitle -> Title) for the plugin's web tabs. + auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr)); + auto TabNames = self->GetTabNames(); lua_newtable(tolua_S); int index = 1; - cWebPlugin::TabNameList::const_iterator iter = TabNames.begin(); - while (iter != TabNames.end()) - { - const AString & FancyName = iter->first; - const AString & WebName = iter->second; - tolua_pushstring( tolua_S, WebName.c_str()); // Because the WebName is supposed to be unique, use it as key - tolua_pushstring( tolua_S, FancyName.c_str()); - // + for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr) + { + tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key + tolua_pushstring(tolua_S, itr->first.c_str()); lua_rawset(tolua_S, -3); - ++iter; ++index; } return 1; @@ -3214,6 +2225,44 @@ static int tolua_cBlockArea_GetOrigin(lua_State * tolua_S) +static int tolua_cBlockArea_GetNonAirCropRelCoords(lua_State * tolua_S) +{ + // function cBlockArea::GetNonAirCropRelCoords() + // Exported manually because tolua would generate extra input params for the outputs + + cLuaState L(tolua_S); + if (!L.CheckParamUserType(1, "cBlockArea")) + { + return 0; + } + + cBlockArea * self = nullptr; + BLOCKTYPE IgnoreBlockType = E_BLOCK_AIR; + L.GetStackValues(1, self, IgnoreBlockType); + if (self == nullptr) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetNonAirCropRelCoords'", nullptr); + return 0; + } + + // Calculate the crop coords: + int MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ; + self->GetNonAirCropRelCoords(MinRelX, MinRelY, MinRelZ, MaxRelX, MaxRelY, MaxRelZ, IgnoreBlockType); + + // Push the six crop coords: + L.Push(MinRelX); + L.Push(MinRelY); + L.Push(MinRelZ); + L.Push(MaxRelX); + L.Push(MaxRelY); + L.Push(MaxRelZ); + return 6; +} + + + + + static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S) { // function cBlockArea::GetRelBlockTypeMeta() @@ -3680,17 +2729,17 @@ static int tolua_cCompositeChat_UnderlineUrls(lua_State * tolua_S) -void ManualBindings::Bind(lua_State * tolua_S) +void cManualBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, nullptr); // Create the new classes: tolua_usertype(tolua_S, "cCryptoHash"); - tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr); + tolua_usertype(tolua_S, "cLineBlockTracer"); tolua_usertype(tolua_S, "cStringCompression"); + tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr); + tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr); tolua_cclass(tolua_S, "cStringCompression", "cStringCompression", "", nullptr); - tolua_usertype(tolua_S, "cLineBlockTracer"); - tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr); // Globals: tolua_function(tolua_S, "Clamp", tolua_Clamp); @@ -3705,15 +2754,12 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead - - tolua_beginmodule(tolua_S, "cFile"); - tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); - tolua_endmodule(tolua_S); - + tolua_beginmodule(tolua_S, "cBlockArea"); tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta); tolua_function(tolua_S, "GetCoordRange", tolua_cBlockArea_GetCoordRange); tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin); + tolua_function(tolua_S, "GetNonAirCropRelCoords", tolua_cBlockArea_GetNonAirCropRelCoords); tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta); tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize); tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile); @@ -3721,7 +2767,13 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); tolua_function(tolua_S, "SaveToSchematicString", tolua_cBlockArea_SaveToSchematicString); tolua_endmodule(tolua_S); - + + tolua_beginmodule(tolua_S, "cClientHandle"); + tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE); + tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE); + tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cCompositeChat"); tolua_function(tolua_S, "AddRunCommandPart", tolua_cCompositeChat_AddRunCommandPart); tolua_function(tolua_S, "AddSuggestCommandPart", tolua_cCompositeChat_AddSuggestCommandPart); @@ -3731,105 +2783,105 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "SetMessageType", tolua_cCompositeChat_SetMessageType); tolua_function(tolua_S, "UnderlineUrls", tolua_cCompositeChat_UnderlineUrls); tolua_endmodule(tolua_S); - + + tolua_beginmodule(tolua_S, "cCryptoHash"); + tolua_function(tolua_S, "md5", tolua_md5); + tolua_function(tolua_S, "md5HexString", tolua_md5HexString); + tolua_function(tolua_S, "sha1", tolua_sha1); + tolua_function(tolua_S, "sha1HexString", tolua_sha1HexString); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cEntity"); + tolua_constant(tolua_S, "INVALID_ID", cEntity::INVALID_ID); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cFile"); + tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); + tolua_function(tolua_S, "ReadWholeFile", tolua_cFile_ReadWholeFile); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cHopperEntity"); tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos); tolua_endmodule(tolua_S); - + + tolua_beginmodule(tolua_S, "cItemGrid"); + tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cLineBlockTracer"); tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cRoot"); - tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>); - tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>); - tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>); - tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>); - tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWorld"); - tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay); - tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>); - tolua_function(tolua_S, "DoWithBeaconAt", tolua_DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>); - tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>); - tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>); - tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>); - tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>); - tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>); - tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>); - tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>); - tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>); - tolua_function(tolua_S, "DoWithMobHeadAt", tolua_DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>); - tolua_function(tolua_S, "DoWithFlowerPotAt", tolua_DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>); - tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); - tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); - tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>); - tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); - tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>); - tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); - tolua_function(tolua_S, "ForEachEntityInBox", tolua_ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>); - tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>); - tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>); - tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); - tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); - tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); - tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); - tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk); - tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); - tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask); - tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); - tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); + + tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); + tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); tolua_endmodule(tolua_S); - + tolua_beginmodule(tolua_S, "cMapManager"); - tolua_function(tolua_S, "DoWithMap", tolua_DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>); + tolua_function(tolua_S, "DoWithMap", DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>); tolua_endmodule(tolua_S); - tolua_beginmodule(tolua_S, "cScoreboard"); - tolua_function(tolua_S, "ForEachObjective", tolua_ForEach<cScoreboard, cObjective, &cScoreboard::ForEachObjective>); - tolua_function(tolua_S, "ForEachTeam", tolua_ForEach<cScoreboard, cTeam, &cScoreboard::ForEachTeam>); + tolua_beginmodule(tolua_S, "cMojangAPI"); + tolua_function(tolua_S, "AddPlayerNameToUUIDMapping", tolua_cMojangAPI_AddPlayerNameToUUIDMapping); + tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID); + tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName); + tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames); + tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed); + tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort); tolua_endmodule(tolua_S); - + + tolua_beginmodule(tolua_S, "cPlayer"); + tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions); + tolua_function(tolua_S, "GetRestrictions", tolua_cPlayer_GetRestrictions); + tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); + tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cPlugin"); - tolua_function(tolua_S, "Call", tolua_cPlugin_Call); + tolua_function(tolua_S, "GetDirectory", tolua_cPlugin_GetDirectory); + tolua_function(tolua_S, "GetLocalDirectory", tolua_cPlugin_GetLocalDirectory); tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cPluginLua"); + tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cPluginManager"); tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); tolua_function(tolua_S, "CallPlugin", tolua_cPluginManager_CallPlugin); + tolua_function(tolua_S, "DoWithPlugin", StaticDoWith<cPluginManager, cPlugin, &cPluginManager::DoWithPlugin>); + tolua_function(tolua_S, "ExecuteConsoleCommand", tolua_cPluginManager_ExecuteConsoleCommand); + tolua_function(tolua_S, "FindPlugins", tolua_cPluginManager_FindPlugins); tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); + tolua_function(tolua_S, "ForEachPlugin", StaticForEach<cPluginManager, cPlugin, &cPluginManager::ForEachPlugin>); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); tolua_function(tolua_S, "GetCurrentPlugin", tolua_cPluginManager_GetCurrentPlugin); + tolua_function(tolua_S, "GetPlugin", tolua_cPluginManager_GetPlugin); tolua_function(tolua_S, "LogStackTrace", tolua_cPluginManager_LogStackTrace); tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cPlayer"); - tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions); - tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); - tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cLuaWindow"); - tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); - tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); + + tolua_beginmodule(tolua_S, "cRoot"); + tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>); + tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>); + tolua_function(tolua_S, "ForEachPlayer", ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>); + tolua_function(tolua_S, "ForEachWorld", ForEach<cRoot, cWorld, &cRoot::ForEachWorld>); + tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe); tolua_endmodule(tolua_S); - tolua_beginmodule(tolua_S, "cPluginLua"); - tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab); - tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab); + tolua_beginmodule(tolua_S, "cScoreboard"); + tolua_function(tolua_S, "ForEachObjective", ForEach<cScoreboard, cObjective, &cScoreboard::ForEachObjective>); + tolua_function(tolua_S, "ForEachTeam", ForEach<cScoreboard, cTeam, &cScoreboard::ForEachTeam>); tolua_endmodule(tolua_S); - tolua_cclass(tolua_S, "HTTPRequest", "HTTPRequest", "", nullptr); - tolua_beginmodule(tolua_S, "HTTPRequest"); - // tolua_variable(tolua_S, "Method", tolua_get_HTTPRequest_Method, tolua_set_HTTPRequest_Method); - // tolua_variable(tolua_S, "Path", tolua_get_HTTPRequest_Path, tolua_set_HTTPRequest_Path); - tolua_variable(tolua_S, "FormData", tolua_get_HTTPRequest_FormData, 0); - tolua_variable(tolua_S, "Params", tolua_get_HTTPRequest_Params, 0); - tolua_variable(tolua_S, "PostParams", tolua_get_HTTPRequest_PostParams, 0); + tolua_beginmodule(tolua_S, "cStringCompression"); + tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB); + tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB); + tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP); + tolua_function(tolua_S, "UncompressStringGZIP", tolua_UncompressStringGZIP); + tolua_function(tolua_S, "InflateString", tolua_InflateString); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWebAdmin"); @@ -3841,47 +2893,16 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cWebPlugin"); tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cClientHandle"); - tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE); - tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE); - tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage); - tolua_endmodule(tolua_S); - tolua_beginmodule(tolua_S, "cMojangAPI"); - tolua_function(tolua_S, "AddPlayerNameToUUIDMapping", tolua_cMojangAPI_AddPlayerNameToUUIDMapping); - tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID); - tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName); - tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames); - tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed); - tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cItemGrid"); - tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords); + tolua_beginmodule(tolua_S, "HTTPRequest"); + tolua_variable(tolua_S, "FormData", tolua_get_HTTPRequest_FormData, nullptr); + tolua_variable(tolua_S, "Params", tolua_get_HTTPRequest_Params, nullptr); + tolua_variable(tolua_S, "PostParams", tolua_get_HTTPRequest_PostParams, nullptr); tolua_endmodule(tolua_S); - tolua_beginmodule(tolua_S, "cCryptoHash"); - tolua_function(tolua_S, "md5", tolua_md5); - tolua_function(tolua_S, "md5HexString", tolua_md5HexString); - tolua_function(tolua_S, "sha1", tolua_sha1); - tolua_function(tolua_S, "sha1HexString", tolua_sha1HexString); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cStringCompression"); - tolua_function(tolua_S, "CompressStringZLIB", tolua_CompressStringZLIB); - tolua_function(tolua_S, "UncompressStringZLIB", tolua_UncompressStringZLIB); - tolua_function(tolua_S, "CompressStringGZIP", tolua_CompressStringGZIP); - tolua_function(tolua_S, "UncompressStringGZIP", tolua_UncompressStringGZIP); - tolua_function(tolua_S, "InflateString", tolua_InflateString); - tolua_endmodule(tolua_S); - - BindRankManager(tolua_S); BindNetwork(tolua_S); - - tolua_beginmodule(tolua_S, "cEntity"); - tolua_constant(tolua_S, "INVALID_ID", cEntity::INVALID_ID); - tolua_endmodule(tolua_S); + BindRankManager(tolua_S); + BindWorld(tolua_S); tolua_endmodule(tolua_S); } diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h index 74d24d5f5..e7a576588 100644 --- a/src/Bindings/ManualBindings.h +++ b/src/Bindings/ManualBindings.h @@ -1,33 +1,566 @@ + +// ManualBindings.h + +// Declares the cManualBindings class used as a namespace for functions exported to the Lua API manually + + + + + #pragma once -struct lua_State; -class cPluginLua; +#include "LuaState.h" + + + + +// fwd: +struct tolua_Error; /** Provides namespace for the bindings. */ -class ManualBindings +class cManualBindings { public: /** Binds all the manually implemented functions to tolua_S. */ static void Bind(lua_State * tolua_S); protected: + /** Binds the manually implemented cNetwork-related API to tolua_S. + Implemented in ManualBindings_Network.cpp. */ + static void BindNetwork(lua_State * tolua_S); + /** Binds the manually implemented cRankManager glue code to tolua_S. Implemented in ManualBindings_RankManager.cpp. */ static void BindRankManager(lua_State * tolua_S); - /** Binds the manually implemented cNetwork-related API to tolua_S. - Implemented in ManualBindings_Network.cpp. */ - static void BindNetwork(lua_State * tolua_S); -}; + /** Binds the manually implemented cWorld API functions to tolua_S. + Implemented in ManualBindings_World.cpp. */ + static void BindWorld(lua_State * tolua_S); + + +public: + // Helper functions: + static cPluginLua * GetLuaPlugin(lua_State * L); + static int tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError); + static int lua_do_error(lua_State * L, const char * a_pFormat, ...); + + + /** Binds the DoWith(ItemName) functions of regular classes. */ + template < + class Ty1, + class Ty2, + bool (Ty1::*DoWithFn)(const AString &, cItemCallback<Ty2> &) + > + static int DoWith(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamString(2) || + !L.CheckParamFunction(3) + ) + { + return 0; + } + + // Get parameters: + Ty1 * Self; + AString ItemName; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, ItemName, FnRef); + if (Self == nullptr) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + if (ItemName.empty() || (ItemName[0] == 0)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + virtual bool Item(Ty2 * a_Item) override + { + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + // Call the DoWith function: + bool res = (Self->*DoWithFn)(ItemName, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; + } + + + + + + /** Template for static functions DoWith(ItemName), on a type that has a static ::Get() function. */ + template < + class Ty1, + class Ty2, + bool (Ty1::*DoWithFn)(const AString &, cItemCallback<Ty2> &) + > + static int StaticDoWith(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamString(2) || + !L.CheckParamFunction(3) + ) + { + return 0; + } + + // Get parameters: + AString ItemName; + cLuaState::cRef FnRef; + L.GetStackValues(2, ItemName, FnRef); + if (ItemName.empty() || (ItemName[0] == 0)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + virtual bool Item(Ty2 * a_Item) override + { + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + // Call the DoWith function: + bool res = (Ty1::Get()->*DoWithFn)(ItemName, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; + } + + + + + + template < + class Ty1, + class Ty2, + bool (Ty1::*DoWithFn)(UInt32, cItemCallback<Ty2> &) + > + static int DoWithID(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamNumber(2) || + !L.CheckParamFunction(3) + ) + { + return 0; + } + + // Get parameters: + Ty1 * Self = nullptr; + int ItemID; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, ItemID, FnRef); + if (Self == nullptr) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + virtual bool Item(Ty2 * a_Item) override + { + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + // Call the DoWith function: + bool res = (Self->*DoWithFn)(ItemID, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; + } + -extern cPluginLua * GetLuaPlugin(lua_State * L); + template < + class Ty1, + class Ty2, + bool (Ty1::*DoWithFn)(int, int, int, cItemCallback<Ty2> &) + > + static int DoWithXYZ(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamNumber(2, 5) || + !L.CheckParamFunction(6) + ) + { + return 0; + } + + // Get parameters: + Ty1 * Self = nullptr; + int BlockX, BlockY, BlockZ; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, FnRef); + if (Self == nullptr) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #5"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + virtual bool Item(Ty2 * a_Item) override + { + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + // Call the DoWith function: + bool res = (Self->*DoWithFn)(BlockX, BlockY, BlockZ, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; + } + + + + + + template < + class Ty1, + class Ty2, + bool (Ty1::*ForEachFn)(int, int, cItemCallback<Ty2> &) + > + static int ForEachInChunk(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamNumber(2, 4) || + !L.CheckParamFunction(5) + ) + { + return 0; + } + + // Get parameters: + Ty1 * Self = nullptr; + int ChunkX, ChunkZ; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, ChunkX, ChunkZ, FnRef); + if (Self == nullptr) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #4"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + virtual bool Item(Ty2 * a_Item) override + { + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + // Call the DoWith function: + bool res = (Self->*ForEachFn)(ChunkX, ChunkZ, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; + } + + + + + + template < + class Ty1, + class Ty2, + bool (Ty1::*ForEachFn)(const cBoundingBox &, cItemCallback<Ty2> &) + > + static int ForEachInBox(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamUserType(2, "cBoundingBox") || + !L.CheckParamFunction(3) || + !L.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + Ty1 * Self = nullptr; + cBoundingBox * Box = nullptr; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, Box, FnRef); + if ((Self == nullptr) || (Box == nullptr)) + { + LOGWARNING("Invalid world (%p) or boundingbox (%p)", Self, Box); + L.LogStackTrace(); + return 0; + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2"); + } + + // Callback wrapper for the Lua function: + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) : + m_LuaState(a_LuaState), + m_FnRef(a_FuncRef) + { + } + + private: + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + + // cItemCallback<Ty2> overrides: + virtual bool Item(Ty2 * a_Item) override + { + bool res = false; + if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res)) + { + LOGWARNING("Failed to call Lua callback"); + m_LuaState.LogStackTrace(); + return true; // Abort enumeration + } + + return res; + } + } Callback(L, FnRef); + + bool res = (Self->*ForEachFn)(*Box, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; + } + + + + + + template < + class Ty1, + class Ty2, + bool (Ty1::*ForEachFn)(cItemCallback<Ty2> &) + > + static int ForEach(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamFunction(2) || + !L.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + Ty1 * Self = nullptr; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, FnRef); + if (Self == nullptr) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'."); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #1"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + + virtual bool Item(Ty2 * a_Item) override + { + bool res = false; // By default continue the enumeration + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res); + return res; + } + } Callback(L, FnRef); + + // Call the enumeration: + bool res = (Self->*ForEachFn)(Callback); + + // Push the return value: + L.Push(res); + return 1; + } + + + + + + /** Implements bindings for ForEach() functions in a class that is static (has a ::Get() static function). */ + template < + class Ty1, + class Ty2, + bool (Ty1::*ForEachFn)(cItemCallback<Ty2> &) + > + static int StaticForEach(lua_State * tolua_S) + { + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamFunction(2) || + !L.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + cLuaState::cRef FnRef(L, 2); + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #1"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } + + private: + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + + virtual bool Item(Ty2 * a_Item) override + { + bool res = false; // By default continue the enumeration + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res); + return res; + } + } Callback(L, FnRef); + + // Call the enumeration: + bool res = (Ty1::Get()->*ForEachFn)(Callback); + + // Push the return value: + L.Push(res); + return 1; + } +}; diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index 628cda7f0..df97d60b3 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -39,7 +39,7 @@ static int tolua_cNetwork_Connect(lua_State * L) } // Get the plugin instance: - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { // An error message has been already printed in GetLuaPlugin() @@ -92,7 +92,7 @@ static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L) } // Get the plugin instance: - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { // An error message has been already printed in GetLuaPlugin() @@ -171,7 +171,7 @@ static int tolua_cNetwork_HostnameToIP(lua_State * L) } // Get the plugin instance: - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { // An error message has been already printed in GetLuaPlugin() @@ -212,7 +212,7 @@ static int tolua_cNetwork_IPToHostname(lua_State * L) } // Get the plugin instance: - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { // An error message has been already printed in GetLuaPlugin() @@ -253,7 +253,7 @@ static int tolua_cNetwork_Listen(lua_State * L) } // Get the plugin instance: - cPluginLua * Plugin = GetLuaPlugin(L); + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { // An error message has been already printed in GetLuaPlugin() @@ -913,17 +913,17 @@ static int tolua_cUDPEndpoint_Send(lua_State * L) //////////////////////////////////////////////////////////////////////////////// // Register the bindings: -void ManualBindings::BindNetwork(lua_State * tolua_S) +void cManualBindings::BindNetwork(lua_State * tolua_S) { // Create the cNetwork API classes: tolua_usertype(tolua_S, "cNetwork"); - tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr); - tolua_usertype(tolua_S, "cTCPLink"); - tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr); tolua_usertype(tolua_S, "cServerHandle"); - tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle); + tolua_usertype(tolua_S, "cTCPLink"); tolua_usertype(tolua_S, "cUDPEndpoint"); - tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint); + tolua_cclass(tolua_S, "cNetwork", "cNetwork", "", nullptr); + tolua_cclass(tolua_S, "cServerHandle", "cServerHandle", "", tolua_collect_cServerHandle); + tolua_cclass(tolua_S, "cTCPLink", "cTCPLink", "", nullptr); + tolua_cclass(tolua_S, "cUDPEndpoint", "cUDPEndpoint", "", tolua_collect_cUDPEndpoint); // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cNetwork"); diff --git a/src/Bindings/ManualBindings_RankManager.cpp b/src/Bindings/ManualBindings_RankManager.cpp index fa1b88b6a..7797d1887 100644 --- a/src/Bindings/ManualBindings_RankManager.cpp +++ b/src/Bindings/ManualBindings_RankManager.cpp @@ -100,6 +100,35 @@ static int tolua_cRankManager_AddPermissionToGroup(lua_State * L) +/** Binds cRankManager::AddRestrictionToGroup */ +static int tolua_cRankManager_AddRestrictionToGroup(lua_State * L) +{ + // Function signature: + // cRankManager:AddRestrictionToGroup(Permission, GroupName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Read the params: + AString GroupName, Permission; + S.GetStackValues(2, Permission, GroupName); + + // Add the group to the rank: + S.Push(cRoot::Get()->GetRankManager()->AddRestrictionToGroup(Permission, GroupName)); + return 1; +} + + + + + /** Binds cRankManager::AddRank */ static int tolua_cRankManager_AddRank(lua_State * L) { @@ -204,6 +233,60 @@ static int tolua_cRankManager_GetAllPermissions(lua_State * L) +/** Binds cRankManager::GetAllPermissions */ +static int tolua_cRankManager_GetAllRestrictions(lua_State * L) +{ + // Function signature: + // cRankManager:GetAllRestrictions() -> arraytable of Permissions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the permissions: + AStringVector Permissions = cRoot::Get()->GetRankManager()->GetAllRestrictions(); + + // Push the results: + S.Push(Permissions); + return 1; +} + + + + + +/** Binds cRankManager::GetAllPermissionsRestrictions */ +static int tolua_cRankManager_GetAllPermissionsRestrictions(lua_State * L) +{ + // Function signature: + // cRankManager:GetAllPermissionsRestrictions() -> arraytable of Permissions and Restrictions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Get the permissions: + AStringVector Permissions = cRoot::Get()->GetRankManager()->GetAllPermissionsRestrictions(); + + // Push the results: + S.Push(Permissions); + return 1; +} + + + + + /** Binds cRankManager::GetAllPlayerUUIDs */ static int tolua_cRankManager_GetAllPlayerUUIDs(lua_State * L) { @@ -314,6 +397,38 @@ static int tolua_cRankManager_GetGroupPermissions(lua_State * L) +/** Binds cRankManager::GetGroupRestrictions */ +static int tolua_cRankManager_GetGroupRestrictions(lua_State * L) +{ + // Function signature: + // cRankManager:GetGroupRestrictions(GroupName) -> arraytable of restrictions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString GroupName; + S.GetStackValue(2, GroupName); + + // Get the restrictions: + AStringVector Restrictions = cRoot::Get()->GetRankManager()->GetGroupRestrictions(GroupName); + + // Push the results: + S.Push(Restrictions); + return 1; +} + + + + + /** Binds cRankManager::GetPlayerGroups */ static int tolua_cRankManager_GetPlayerGroups(lua_State * L) { @@ -416,6 +531,38 @@ static int tolua_cRankManager_GetPlayerPermissions(lua_State * L) +/** Binds cRankManager::GetPlayerRestrictions */ +static int tolua_cRankManager_GetPlayerRestrictions(lua_State * L) +{ + // Function signature: + // cRankManager:GetPlayerRestrictions(PlayerUUID) -> arraytable of restrictions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString PlayerUUID; + S.GetStackValue(2, PlayerUUID); + + // Get the permissions: + AStringVector Restrictions = cRoot::Get()->GetRankManager()->GetPlayerRestrictions(PlayerUUID); + + // Push the results: + S.Push(Restrictions); + return 1; +} + + + + + /** Binds cRankManager::GetPlayerRankName */ static int tolua_cRankManager_GetPlayerRankName(lua_State * L) { @@ -544,6 +691,38 @@ static int tolua_cRankManager_GetRankPermissions(lua_State * L) +/** Binds cRankManager::GetRankRestrictions */ +static int tolua_cRankManager_GetRankRestrictions(lua_State * L) +{ + // Function signature: + // cRankManager:GetRankRestrictions(RankName) -> arraytable of restrictions + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the params: + AString RankName; + S.GetStackValue(2, RankName); + + // Get the permissions: + AStringVector Restrictions = cRoot::Get()->GetRankManager()->GetRankRestrictions(RankName); + + // Push the results: + S.Push(Restrictions); + return 1; +} + + + + + /** Binds cRankManager::GetRankVisuals */ static int tolua_cRankManager_GetRankVisuals(lua_State * L) { @@ -679,6 +858,38 @@ static int tolua_cRankManager_IsPermissionInGroup(lua_State * L) +/** Binds cRankManager::IsRestrictionInGroup */ +static int tolua_cRankManager_IsRestrictionInGroup(lua_State * L) +{ + // Function signature: + // cRankManager:IsRestrictionInGroup(Restriction, GroupName) -> bool + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString GroupName, Restriction; + S.GetStackValues(2, Restriction, GroupName); + + // Get the response: + bool res = cRoot::Get()->GetRankManager()->IsRestrictionInGroup(Restriction, GroupName); + + // Push the result: + S.Push(res); + return 1; +} + + + + + /** Binds cRankManager::IsPlayerRankSet */ static int tolua_cRankManager_IsPlayerRankSet(lua_State * L) { @@ -821,7 +1032,7 @@ static int tolua_cRankManager_RemovePermissionFromGroup(lua_State * L) AString GroupName, Permission; S.GetStackValues(2, Permission, GroupName); - // Remove the group: + // Remove the permission: cRoot::Get()->GetRankManager()->RemovePermissionFromGroup(Permission, GroupName); return 0; } @@ -830,6 +1041,35 @@ static int tolua_cRankManager_RemovePermissionFromGroup(lua_State * L) +/** Binds cRankManager::RemoveRestrictionFromGroup */ +static int tolua_cRankManager_RemoveRestrictionFromGroup(lua_State * L) +{ + // Function signature: + // cRankManager:RemoveRestrictionFromGroup(Restriction, GroupName) + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cRankManager") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + + // Get the params: + AString GroupName, Restriction; + S.GetStackValues(2, Restriction, GroupName); + + // Remove the restriction: + cRoot::Get()->GetRankManager()->RemoveRestrictionFromGroup(Restriction, GroupName); + return 0; +} + + + + + /** Binds cRankManager::RemovePlayerRank */ static int tolua_cRankManager_RemovePlayerRank(lua_State * L) { @@ -1040,7 +1280,7 @@ static int tolua_cRankManager_SetRankVisuals(lua_State * L) -void ManualBindings::BindRankManager(lua_State * tolua_S) +void cManualBindings::BindRankManager(lua_State * tolua_S) { // Create the cRankManager class in the API: tolua_usertype(tolua_S, "cRankManager"); @@ -1048,40 +1288,48 @@ void ManualBindings::BindRankManager(lua_State * tolua_S) // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cRankManager"); - tolua_function(tolua_S, "AddGroup", tolua_cRankManager_AddGroup); - tolua_function(tolua_S, "AddGroupToRank", tolua_cRankManager_AddGroupToRank); - tolua_function(tolua_S, "AddPermissionToGroup", tolua_cRankManager_AddPermissionToGroup); - tolua_function(tolua_S, "AddRank", tolua_cRankManager_AddRank); - tolua_function(tolua_S, "ClearPlayerRanks", tolua_cRankManager_ClearPlayerRanks); - tolua_function(tolua_S, "GetAllGroups", tolua_cRankManager_GetAllGroups); - tolua_function(tolua_S, "GetAllPermissions", tolua_cRankManager_GetAllPermissions); - tolua_function(tolua_S, "GetAllPlayerUUIDs", tolua_cRankManager_GetAllPlayerUUIDs); - tolua_function(tolua_S, "GetAllRanks", tolua_cRankManager_GetAllRanks); - tolua_function(tolua_S, "GetDefaultRank", tolua_cRankManager_GetDefaultRank); - tolua_function(tolua_S, "GetGroupPermissions", tolua_cRankManager_GetGroupPermissions); - tolua_function(tolua_S, "GetPlayerGroups", tolua_cRankManager_GetPlayerGroups); - tolua_function(tolua_S, "GetPlayerMsgVisuals", tolua_cRankManager_GetPlayerMsgVisuals); - tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerPermissions); - tolua_function(tolua_S, "GetPlayerRankName", tolua_cRankManager_GetPlayerRankName); - tolua_function(tolua_S, "GetPlayerName", tolua_cRankManager_GetPlayerName); - tolua_function(tolua_S, "GetRankGroups", tolua_cRankManager_GetRankGroups); - tolua_function(tolua_S, "GetRankPermissions", tolua_cRankManager_GetRankPermissions); - tolua_function(tolua_S, "GetRankVisuals", tolua_cRankManager_GetRankVisuals); - tolua_function(tolua_S, "GroupExists", tolua_cRankManager_GroupExists); - tolua_function(tolua_S, "IsGroupInRank", tolua_cRankManager_IsGroupInRank); - tolua_function(tolua_S, "IsPermissionInGroup", tolua_cRankManager_IsPermissionInGroup); - tolua_function(tolua_S, "IsPlayerRankSet", tolua_cRankManager_IsPlayerRankSet); - tolua_function(tolua_S, "RankExists", tolua_cRankManager_RankExists); - tolua_function(tolua_S, "RemoveGroup", tolua_cRankManager_RemoveGroup); - tolua_function(tolua_S, "RemoveGroupFromRank", tolua_cRankManager_RemoveGroupFromRank); - tolua_function(tolua_S, "RemovePermissionFromGroup", tolua_cRankManager_RemovePermissionFromGroup); - tolua_function(tolua_S, "RemovePlayerRank", tolua_cRankManager_RemovePlayerRank); - tolua_function(tolua_S, "RemoveRank", tolua_cRankManager_RemoveRank); - tolua_function(tolua_S, "RenameGroup", tolua_cRankManager_RenameGroup); - tolua_function(tolua_S, "RenameRank", tolua_cRankManager_RenameRank); - tolua_function(tolua_S, "SetDefaultRank", tolua_cRankManager_SetDefaultRank); - tolua_function(tolua_S, "SetPlayerRank", tolua_cRankManager_SetPlayerRank); - tolua_function(tolua_S, "SetRankVisuals", tolua_cRankManager_SetRankVisuals); + tolua_function(tolua_S, "AddGroup", tolua_cRankManager_AddGroup); + tolua_function(tolua_S, "AddGroupToRank", tolua_cRankManager_AddGroupToRank); + tolua_function(tolua_S, "AddPermissionToGroup", tolua_cRankManager_AddPermissionToGroup); + tolua_function(tolua_S, "AddRestrictionToGroup", tolua_cRankManager_AddRestrictionToGroup); + tolua_function(tolua_S, "AddRank", tolua_cRankManager_AddRank); + tolua_function(tolua_S, "ClearPlayerRanks", tolua_cRankManager_ClearPlayerRanks); + tolua_function(tolua_S, "GetAllGroups", tolua_cRankManager_GetAllGroups); + tolua_function(tolua_S, "GetAllPermissions", tolua_cRankManager_GetAllPermissions); + tolua_function(tolua_S, "GetAllRestrictions", tolua_cRankManager_GetAllRestrictions); + tolua_function(tolua_S, "GetAllPermissionsRestrictions", tolua_cRankManager_GetAllPermissionsRestrictions); + tolua_function(tolua_S, "GetAllPlayerUUIDs", tolua_cRankManager_GetAllPlayerUUIDs); + tolua_function(tolua_S, "GetAllRanks", tolua_cRankManager_GetAllRanks); + tolua_function(tolua_S, "GetDefaultRank", tolua_cRankManager_GetDefaultRank); + tolua_function(tolua_S, "GetGroupPermissions", tolua_cRankManager_GetGroupPermissions); + tolua_function(tolua_S, "GetGroupRestrictions", tolua_cRankManager_GetGroupRestrictions); + tolua_function(tolua_S, "GetPlayerGroups", tolua_cRankManager_GetPlayerGroups); + tolua_function(tolua_S, "GetPlayerMsgVisuals", tolua_cRankManager_GetPlayerMsgVisuals); + tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerPermissions); + tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerRestrictions); + tolua_function(tolua_S, "GetPlayerRankName", tolua_cRankManager_GetPlayerRankName); + tolua_function(tolua_S, "GetPlayerName", tolua_cRankManager_GetPlayerName); + tolua_function(tolua_S, "GetRankGroups", tolua_cRankManager_GetRankGroups); + tolua_function(tolua_S, "GetRankPermissions", tolua_cRankManager_GetRankPermissions); + tolua_function(tolua_S, "GetRankRestrictions", tolua_cRankManager_GetRankRestrictions); + tolua_function(tolua_S, "GetRankVisuals", tolua_cRankManager_GetRankVisuals); + tolua_function(tolua_S, "GroupExists", tolua_cRankManager_GroupExists); + tolua_function(tolua_S, "IsGroupInRank", tolua_cRankManager_IsGroupInRank); + tolua_function(tolua_S, "IsPermissionInGroup", tolua_cRankManager_IsPermissionInGroup); + tolua_function(tolua_S, "IsRestrictionInGroup", tolua_cRankManager_IsRestrictionInGroup); + tolua_function(tolua_S, "IsPlayerRankSet", tolua_cRankManager_IsPlayerRankSet); + tolua_function(tolua_S, "RankExists", tolua_cRankManager_RankExists); + tolua_function(tolua_S, "RemoveGroup", tolua_cRankManager_RemoveGroup); + tolua_function(tolua_S, "RemoveGroupFromRank", tolua_cRankManager_RemoveGroupFromRank); + tolua_function(tolua_S, "RemovePermissionFromGroup", tolua_cRankManager_RemovePermissionFromGroup); + tolua_function(tolua_S, "RemoveRestrictionFromGroup", tolua_cRankManager_RemoveRestrictionFromGroup); + tolua_function(tolua_S, "RemovePlayerRank", tolua_cRankManager_RemovePlayerRank); + tolua_function(tolua_S, "RemoveRank", tolua_cRankManager_RemoveRank); + tolua_function(tolua_S, "RenameGroup", tolua_cRankManager_RenameGroup); + tolua_function(tolua_S, "RenameRank", tolua_cRankManager_RenameRank); + tolua_function(tolua_S, "SetDefaultRank", tolua_cRankManager_SetDefaultRank); + tolua_function(tolua_S, "SetPlayerRank", tolua_cRankManager_SetPlayerRank); + tolua_function(tolua_S, "SetRankVisuals", tolua_cRankManager_SetRankVisuals); tolua_endmodule(tolua_S); } diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp new file mode 100644 index 000000000..5becbba92 --- /dev/null +++ b/src/Bindings/ManualBindings_World.cpp @@ -0,0 +1,588 @@ + +// ManualBindings_World.cpp + +// Implements the manual Lua API bindings for the cWorld class + +#include "Globals.h" +#include "tolua++/include/tolua++.h" +#include "../World.h" +#include "../Broadcaster.h" +#include "ManualBindings.h" +#include "LuaState.h" +#include "PluginLua.h" +#include "LuaChunkStay.h" + + + + + +static int tolua_cWorld_BroadcastParticleEffect(lua_State * tolua_S) +{ + /* Function signature: + World:BroadcastParticleEffect("Name", PosX, PosY, PosZ, OffX, OffY, OffZ, ParticleData, ParticleAmount, [ExcludeClient], [OptionalParam1], [OptionalParam2] + */ + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamString (2) || + !L.CheckParamNumber (3, 10) + ) + { + return 0; + } + + // Read the params: + cWorld * World = nullptr; + AString Name; + float PosX, PosY, PosZ, OffX, OffY, OffZ; + float ParticleData; + int ParticleAmmount; + cClientHandle * ExcludeClient = nullptr; + L.GetStackValues(1, World, Name, PosX, PosY, PosZ, OffX, OffY, OffZ, ParticleData, ParticleAmmount, ExcludeClient); + if (World == nullptr) + { + LOGWARNING("World:BroadcastParticleEffect(): invalid world parameter"); + L.LogStackTrace(); + return 0; + } + + // Read up to 2 more optional data params: + std::array<int, 2> data; + for (int i = 0; (i < 2) && L.IsParamNumber(11 + i); i++) + { + L.GetStackValue(11 + i, data[i]); + } + + World->GetBroadcaster().BroadcastParticleEffect(Name, Vector3f(PosX, PosY, PosZ), Vector3f(OffX, OffY, OffZ), ParticleData, ParticleAmmount, ExcludeClient); + + return 0; +} + + + + + +static int tolua_cWorld_ChunkStay(lua_State * tolua_S) +{ + /* Function signature: + World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable) + ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... } + */ + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType (1, "cWorld") || + !L.CheckParamTable (2) || + !L.CheckParamFunctionOrNil(3, 4) + ) + { + return 0; + } + + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); + if (Plugin == nullptr) + { + return 0; + } + + // Read the params: + cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr); + if (World == nullptr) + { + LOGWARNING("World:ChunkStay(): invalid world parameter"); + L.LogStackTrace(); + return 0; + } + + cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin); + + if (!ChunkStay->AddChunks(2)) + { + delete ChunkStay; + ChunkStay = nullptr; + return 0; + } + + ChunkStay->Enable(*World->GetChunkMap(), 3, 4); + return 0; +} + + + + + +static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight) + // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight] + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamEnd(5) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + int BlockX, BlockY, BlockZ; + L.GetStackValues(1, Self, BlockX, BlockY, BlockZ); + if (Self == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + + // Call the function: + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight; + bool res = Self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight); + + // Push the returned values: + L.Push(res); + if (res) + { + L.Push(BlockType); + L.Push(BlockMeta); + L.Push(BlockSkyLight); + L.Push(BlockBlockLight); + return 5; + } + return 1; +} + + + + + +static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta) + // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta] + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamEnd(5) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + int BlockX, BlockY, BlockZ; + L.GetStackValues(1, Self, BlockX, BlockY, BlockZ); + if (Self == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + + // Call the function: + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + bool res = Self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); + + // Push the returned values: + L.Push(res); + if (res) + { + L.Push(BlockType); + L.Push(BlockMeta); + return 3; + } + return 1; +} + + + + + +static int tolua_cWorld_GetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamEnd(5) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + int BlockX, BlockY, BlockZ; + L.GetStackValues(1, Self, BlockX, BlockY, BlockZ); + if (Self == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + + // Call the function: + AString Line1, Line2, Line3, Line4; + bool res = Self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + + // Push the returned values: + L.Push(res); + if (res) + { + L.Push(Line1); + L.Push(Line2); + L.Push(Line3); + L.Push(Line4); + return 5; + } + return 1; +} + + + + + +static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) +{ + /* Function signature: + World:PrepareChunk(ChunkX, ChunkZ, Callback) + */ + + // Check the param types: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType (1, "cWorld") || + !L.CheckParamNumber (2, 3) || + !L.CheckParamFunctionOrNil(4) + ) + { + return 0; + } + + // Read the params: + cWorld * world = nullptr; + int chunkX = 0, chunkZ = 0; + L.GetStackValues(1, world, chunkX, chunkZ); + if (world == nullptr) + { + LOGWARNING("World:PrepareChunk(): invalid world parameter"); + L.LogStackTrace(); + return 0; + } + + // Wrap the Lua callback inside a C++ callback class: + class cCallback: + public cChunkCoordCallback + { + public: + cCallback(lua_State * a_LuaState): + m_LuaState(a_LuaState), + m_Callback(m_LuaState, 4) + { + } + + // cChunkCoordCallback override: + virtual void Call(int a_CBChunkX, int a_CBChunkZ) override + { + if (m_Callback.IsValid()) + { + m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ); + } + + // This is the last reference of this object, we must delete it so that it doesn't leak: + delete this; + } + + protected: + cLuaState m_LuaState; + cLuaState::cRef m_Callback; + }; + cCallback * callback = new cCallback(tolua_S); + + // Call the chunk preparation: + world->PrepareChunk(chunkX, chunkZ, callback); + return 0; +} + + + + + +class cLuaWorldTask : + public cWorld::cTask, + public cPluginLua::cResettable +{ +public: + cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : + cPluginLua::cResettable(a_Plugin), + m_FnRef(a_FnRef) + { + } + +protected: + int m_FnRef; + + // cWorld::cTask overrides: + virtual void Run(cWorld & a_World) override + { + cCSLock Lock(m_CSPlugin); + if (m_Plugin != nullptr) + { + m_Plugin->Call(m_FnRef, &a_World); + } + } +} ; + + + + + +static int tolua_cWorld_QueueTask(lua_State * tolua_S) +{ + // Binding for cWorld::QueueTask + // Params: function + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr); + if (self == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!lua_isfunction(tolua_S, 2)) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(task); + self->QueueTask(task); + return 0; +} + + + + + +static int tolua_cWorld_SetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5, 8) || + !L.CheckParamEnd(9) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + int BlockX, BlockY, BlockZ; + AString Line1, Line2, Line3, Line4; + L.GetStackValues(1, Self, BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + if (Self == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); + } + + // Call the function: + bool res = Self->SetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + + // Push the returned values: + L.Push(res); + return 1; +} + + + + + +class cLuaScheduledWorldTask : + public cWorld::cTask, + public cPluginLua::cResettable +{ +public: + cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) : + cPluginLua::cResettable(a_Plugin), + m_FnRef(a_FnRef) + { + } + +protected: + int m_FnRef; + + // cWorld::cTask overrides: + virtual void Run(cWorld & a_World) override + { + cCSLock Lock(m_CSPlugin); + if (m_Plugin != nullptr) + { + m_Plugin->Call(m_FnRef, &a_World); + } + } +}; + + + + + +static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) +{ + // Binding for cWorld::ScheduleTask + // Params: function, Ticks + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); + if (Plugin == nullptr) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber (2) || + !L.CheckParamFunction(3) + ) + { + return 0; + } + cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, nullptr); + if (World == nullptr) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0); + + auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(task); + World->ScheduleTask(DelayTicks, task); + return 0; +} + + + + + + +static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) +{ + /* Exported manually, because tolua would require the out-only param a_Height to be used when calling + Function signature: world:TryGetHeight(a_World, a_BlockX, a_BlockZ) -> IsValid, Height + */ + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber(2, 3) || + !L.CheckParamEnd(4) + ) + { + return 0; + } + + // Get params: + cWorld * self = nullptr; + int BlockX, BlockZ; + L.GetStackValues(1, self, BlockX, BlockZ); + if (self == nullptr) + { + tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", nullptr); + return 0; + } + + // Call the implementation: + int Height = 0; + bool res = self->TryGetHeight(BlockX, BlockZ, Height); + L.Push(res); + if (res) + { + L.Push(Height); + return 2; + } + return 1; +} + + + + + +void cManualBindings::BindWorld(lua_State * tolua_S) +{ + tolua_beginmodule(tolua_S, nullptr); + tolua_beginmodule(tolua_S, "cWorld"); + tolua_function(tolua_S, "BroadcastParticleEffect", tolua_cWorld_BroadcastParticleEffect); + tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay); + tolua_function(tolua_S, "DoWithBlockEntityAt", DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>); + tolua_function(tolua_S, "DoWithBeaconAt", DoWithXYZ<cWorld, cBeaconEntity, &cWorld::DoWithBeaconAt>); + tolua_function(tolua_S, "DoWithChestAt", DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>); + tolua_function(tolua_S, "DoWithDispenserAt", DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>); + tolua_function(tolua_S, "DoWithDropSpenserAt", DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>); + tolua_function(tolua_S, "DoWithDropperAt", DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>); + tolua_function(tolua_S, "DoWithEntityByID", DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>); + tolua_function(tolua_S, "DoWithFurnaceAt", DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>); + tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>); + tolua_function(tolua_S, "DoWithCommandBlockAt", DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>); + tolua_function(tolua_S, "DoWithMobHeadAt", DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>); + tolua_function(tolua_S, "DoWithFlowerPotAt", DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>); + tolua_function(tolua_S, "DoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); + tolua_function(tolua_S, "FindAndDoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); + tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayerByUUID>); + tolua_function(tolua_S, "ForEachBlockEntityInChunk", ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); + tolua_function(tolua_S, "ForEachChestInChunk", ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>); + tolua_function(tolua_S, "ForEachEntity", ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); + tolua_function(tolua_S, "ForEachEntityInBox", ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>); + tolua_function(tolua_S, "ForEachEntityInChunk", ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>); + tolua_function(tolua_S, "ForEachFurnaceInChunk", ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>); + tolua_function(tolua_S, "ForEachPlayer", ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); + tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); + tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); + tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk); + tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); + tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask); + tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); + tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); + tolua_endmodule(tolua_S); + tolua_endmodule(tolua_S); +} + + + + diff --git a/src/Bindings/Plugin.cpp b/src/Bindings/Plugin.cpp index 98ccfb88c..2f2771e38 100644 --- a/src/Bindings/Plugin.cpp +++ b/src/Bindings/Plugin.cpp @@ -7,11 +7,11 @@ -cPlugin::cPlugin(const AString & a_PluginDirectory) : - m_Language(E_CPP), - m_Name(a_PluginDirectory), +cPlugin::cPlugin(const AString & a_FolderName) : + m_Status(cPluginManager::psDisabled), + m_Name(a_FolderName), m_Version(0), - m_Directory(a_PluginDirectory) + m_FolderName(a_FolderName) { } @@ -28,9 +28,33 @@ cPlugin::~cPlugin() +void cPlugin::Unload(void) +{ + auto pm = cPluginManager::Get(); + pm->RemovePluginCommands(this); + pm->RemovePluginConsoleCommands(this); + pm->RemoveHooks(this); + OnDisable(); + m_Status = cPluginManager::psUnloaded; + m_LoadError.clear(); +} + + + + + AString cPlugin::GetLocalFolder(void) const { - return std::string("Plugins/") + m_Directory; + return std::string("Plugins/") + m_FolderName; +} + + + + +void cPlugin::SetLoadError(const AString & a_LoadError) +{ + m_Status = cPluginManager::psError; + m_LoadError = a_LoadError; } diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 3f9fa7655..1330bca0d 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -1,30 +1,16 @@ -#pragma once - -#include "Defines.h" +// Plugin.h -class cCommandOutputCallback; -class cItems; -class cHopperEntity; +// Declares the cPlugin class representing an interface that a plugin implementation needs to expose, with some helping functions -class cBlockEntityWithItems; -class cClientHandle; -class cPickup; -class cPlayer; -class cProjectileEntity; -class cEntity; -class cMonster; -class cWorld; -class cChunkDesc; -struct TakeDamageInfo; -// fwd: CraftingRecipes.h -class cCraftingGrid; -class cCraftingRecipe; +#pragma once +#include "Defines.h" +#include "PluginManager.h" @@ -35,11 +21,23 @@ class cPlugin public: // tolua_end - cPlugin( const AString & a_PluginDirectory); + /** Creates a new instance. + a_FolderName is the name of the folder (in the Plugins folder) from which the plugin is loaded. + The plugin's name defaults to the folder name. */ + cPlugin(const AString & a_FolderName); + virtual ~cPlugin(); + /** Called as the last call into the plugin before it is unloaded. */ virtual void OnDisable(void) {} - virtual bool Initialize(void) = 0; + + /** Loads and initializes the plugin. Sets m_Status to psLoaded or psError accordingly. + Returns true if the initialization succeeded, false otherwise. */ + virtual bool Load(void) = 0; + + /** Unloads the plugin. Sets m_Status to psDisabled. + The default implementation removes the plugin's associations with cPluginManager, descendants should call it as well. */ + virtual void Unload(void); // Called each tick virtual void Tick(float a_Dt) = 0; @@ -58,7 +56,9 @@ public: virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; - virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; + virtual bool OnEntityChangingWorld (cEntity & a_Entity, cWorld & a_World) = 0; + virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) = 0; + virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) = 0; @@ -109,19 +109,17 @@ public: /** Handles the command split into a_Split, issued by player a_Player. Command permissions have already been checked. - Returns true if command handled successfully - */ + Returns true if command handled successfully. */ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0; /** Handles the console command split into a_Split. - Returns true if command handled successfully. Output is to be sent to the a_Output callback. - */ + Returns true if command handled successfully. Output is to be sent to the a_Output callback. */ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0; - /// All bound commands are to be removed, do any language-dependent cleanup here + /** All bound commands are to be removed, do any language-dependent cleanup here */ virtual void ClearCommands(void) {} - /// All bound console commands are to be removed, do any language-dependent cleanup here + /** All bound console commands are to be removed, do any language-dependent cleanup here */ virtual void ClearConsoleCommands(void) {} // tolua_begin @@ -131,28 +129,43 @@ public: int GetVersion(void) const { return m_Version; } void SetVersion(int a_Version) { m_Version = a_Version; } - const AString & GetDirectory(void) const {return m_Directory; } - AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead + /** Returns the name of the folder (in the Plugins folder) from which the plugin is loaded. */ + const AString & GetFolderName(void) const {return m_FolderName; } + + /** Returns the folder relative to the MCS Executable, from which the plugin is loaded. */ AString GetLocalFolder(void) const; + + /** Returns the error encountered while loading the plugin. Only valid if m_Status == psError. */ + const AString & GetLoadError(void) const { return m_LoadError; } + + cPluginManager::ePluginStatus GetStatus(void) const { return m_Status; } + + bool IsLoaded(void) const { return (m_Status == cPluginManager::psLoaded); } // tolua_end + // Needed for ManualBindings' tolua_ForEach<> + static const char * GetClassStatic(void) { return "cPlugin"; } + +protected: + friend class cPluginManager; + + cPluginManager::ePluginStatus m_Status; - /* This should not be exposed to scripting languages */ - enum PluginLanguage - { - E_CPP, - E_LUA, - E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history - }; - PluginLanguage GetLanguage() { return m_Language; } - void SetLanguage( PluginLanguage a_Language) { m_Language = a_Language; } - -private: - PluginLanguage m_Language; + /** The name of the plugin, used to identify the plugin in the system and for inter-plugin calls. */ AString m_Name; + int m_Version; - AString m_Directory; + /** Name of the folder (in the Plugins folder) from which the plugin is loaded. */ + AString m_FolderName; + + /** The error encountered while loading the plugin. + Only valid if m_Status == psError. */ + AString m_LoadError; + + + /** Sets m_LoadError to the specified string and m_Status to psError. */ + void SetLoadError(const AString & a_LoadError); }; // tolua_export diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 0a2a8411d..234bf579b 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -63,6 +63,11 @@ void cPluginLua::Close(void) return; } + // Remove the command bindings and web tabs: + ClearCommands(); + ClearConsoleCommands(); + ClearTabs(); + // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them): cResettablePtrs resettables; std::swap(m_Resettables, resettables); @@ -93,7 +98,7 @@ void cPluginLua::Close(void) -bool cPluginLua::Initialize(void) +bool cPluginLua::Load(void) { cCSLock Lock(m_CriticalSection); if (!m_LuaState.IsValid()) @@ -144,6 +149,7 @@ bool cPluginLua::Initialize(void) // Warn if there are no Lua files in the plugin folder: if (LuaFiles.empty()) { + SetLoadError("No lua files found, plugin is probably missing."); LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str()); Close(); return false; @@ -155,6 +161,7 @@ bool cPluginLua::Initialize(void) AString Path = PluginPath + *itr; if (!m_LuaState.LoadFile(Path)) { + SetLoadError(Printf("Failed to load file %s.", itr->c_str())); Close(); return false; } @@ -164,6 +171,8 @@ bool cPluginLua::Initialize(void) AString Path = PluginPath + "Info.lua"; if (!m_LuaState.LoadFile(Path)) { + SetLoadError("Failed to load file Info.lua."); + m_Status = cPluginManager::psError; Close(); return false; } @@ -173,17 +182,20 @@ bool cPluginLua::Initialize(void) bool res = false; if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res)) { + SetLoadError("Cannot call the Initialize() function."); LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str()); Close(); return false; } if (!res) { + SetLoadError("The Initialize() function failed."); LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str()); Close(); return false; } + m_Status = cPluginManager::psLoaded; return true; } @@ -191,6 +203,17 @@ bool cPluginLua::Initialize(void) +void cPluginLua::Unload(void) +{ + ClearTabs(); + super::Unload(); + Close(); +} + + + + + void cPluginLua::OnDisable(void) { cCSLock Lock(m_CriticalSection); @@ -208,6 +231,10 @@ void cPluginLua::OnDisable(void) void cPluginLua::Tick(float a_Dt) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return; + } cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { @@ -222,6 +249,10 @@ void cPluginLua::Tick(float a_Dt) bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_SPREAD]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -242,6 +273,10 @@ bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -262,6 +297,10 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -282,6 +321,10 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -302,6 +345,10 @@ bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -322,6 +369,10 @@ bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -342,6 +393,10 @@ bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -362,6 +417,10 @@ bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -382,6 +441,10 @@ bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -402,6 +465,10 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -422,6 +489,10 @@ bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -442,6 +513,10 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -459,14 +534,66 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E -bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) +bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGING_WORLD]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, &a_World, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, &a_World, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); if (res) { return true; @@ -482,6 +609,10 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -519,6 +650,10 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -556,6 +691,10 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Username) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -576,8 +715,11 @@ bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Usernam bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { @@ -597,6 +739,10 @@ bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -617,6 +763,10 @@ bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -637,6 +787,10 @@ bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInf bool cPluginLua::OnLogin(cClientHandle & a_Client, int a_ProtocolVersion, const AString & a_Username) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -657,6 +811,10 @@ bool cPluginLua::OnLogin(cClientHandle & a_Client, int a_ProtocolVersion, const bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -677,6 +835,10 @@ bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -697,6 +859,10 @@ bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_B bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -717,6 +883,10 @@ bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_Blo bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_DESTROYED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -737,6 +907,10 @@ bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) bool cPluginLua::OnPlayerEating(cPlayer & a_Player) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -757,6 +931,10 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player) bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -777,6 +955,10 @@ bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -797,6 +979,10 @@ bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -817,6 +1003,10 @@ bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -837,6 +1027,10 @@ bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -857,6 +1051,10 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -877,6 +1075,10 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -897,6 +1099,10 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -922,6 +1128,10 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_Blo bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -947,6 +1157,10 @@ bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_Bl bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -967,6 +1181,10 @@ bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -987,6 +1205,10 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1007,6 +1229,10 @@ bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1027,6 +1253,10 @@ bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1047,6 +1277,10 @@ bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1067,6 +1301,10 @@ bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1087,6 +1325,10 @@ bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1107,6 +1349,10 @@ bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1127,6 +1373,10 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1147,6 +1397,10 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha bool cPluginLua::OnPluginsLoaded(void) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1165,6 +1419,10 @@ bool cPluginLua::OnPluginsLoaded(void) bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1185,6 +1443,10 @@ bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCra bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1205,6 +1467,10 @@ bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraf bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1225,6 +1491,10 @@ bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_Bl bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_ENTITY]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1245,6 +1515,10 @@ bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1265,6 +1539,10 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1285,6 +1563,10 @@ bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1305,6 +1587,10 @@ bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1325,6 +1611,10 @@ bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1345,6 +1635,10 @@ bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1370,6 +1664,10 @@ bool cPluginLua::OnUpdatedSign( ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1395,6 +1693,10 @@ bool cPluginLua::OnUpdatingSign( ) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1415,6 +1717,10 @@ bool cPluginLua::OnUpdatingSign( bool cPluginLua::OnWeatherChanged(cWorld & a_World) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1435,6 +1741,10 @@ bool cPluginLua::OnWeatherChanged(cWorld & a_World) bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) @@ -1455,6 +1765,10 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) bool cPluginLua::OnWorldStarted(cWorld & a_World) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_STARTED]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { @@ -1470,6 +1784,10 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { @@ -1614,6 +1932,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; + case cPluginManager::HOOK_ENTITY_CHANGING_WORLD: return "OnEntityChangingWorld"; + case cPluginManager::HOOK_ENTITY_CHANGED_WORLD: return "OnEntityChangedWorld"; case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; @@ -1736,40 +2056,29 @@ void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable) -AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request) +AString cPluginLua::HandleWebRequest(const HTTPRequest & a_Request) { - cCSLock Lock(m_CriticalSection); - std::string RetVal = ""; - - std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request); - std::string SafeTabName = TabName.second; - if (SafeTabName.empty()) + // Find the tab to use for the request: + auto TabName = GetTabNameForRequest(a_Request); + AString SafeTabTitle = TabName.second; + if (SafeTabTitle.empty()) { return ""; } - - sWebPluginTab * Tab = 0; - for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr) + auto Tab = GetTabBySafeTitle(SafeTabTitle); + if (Tab == nullptr) { - if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr - { - Tab = *itr; - break; - } + return ""; } - if (Tab != nullptr) + // Get the page content from the plugin: + cCSLock Lock(m_CriticalSection); + AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->m_Title.c_str()); + if (!m_LuaState.Call(Tab->m_UserData, &a_Request, cLuaState::Return, Contents)) { - AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str()); - if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents)) - { - return "Lua encountered error while processing the page request"; - } - - RetVal += Contents; + return "Lua encountered error while processing the page request"; } - - return RetVal; + return Contents; } @@ -1784,13 +2093,7 @@ bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); return false; } - sWebPluginTab * Tab = new sWebPluginTab(); - Tab->Title = a_Title; - Tab->SafeTitle = SafeString(a_Title); - - Tab->UserData = a_FunctionReference; - - GetTabs().push_back(Tab); + AddNewWebTab(a_Title, a_FunctionReference); return true; } diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index c14b02687..a763cdfdf 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -20,7 +20,7 @@ -// fwd: UI/Window.h +// fwd: "UI/Window.h" class cWindow; @@ -32,6 +32,8 @@ class cPluginLua : public cPlugin, public cWebPlugin { + typedef cPlugin super; + public: // tolua_end @@ -96,7 +98,8 @@ public: ~cPluginLua(); virtual void OnDisable(void) override; - virtual bool Initialize(void) override; + virtual bool Load(void) override; + virtual void Unload(void) override; virtual void Tick(float a_Dt) override; @@ -112,7 +115,9 @@ public: virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override; - virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; + virtual bool OnEntityChangingWorld (cEntity & a_Entity, cWorld & a_World) override; + virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) override; + virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) override; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) override; @@ -173,12 +178,13 @@ public: /** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */ bool CanAddOldStyleHook(int a_HookType); - // cWebPlugin override - virtual const AString GetWebTitle(void) const {return GetName(); } + // cWebPlugin overrides + virtual const AString GetWebTitle(void) const override {return GetName(); } + virtual AString HandleWebRequest(const HTTPRequest & a_Request) override; - // cWebPlugin and WebAdmin stuff - virtual AString HandleWebRequest(const HTTPRequest * a_Request) override; - bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS << + /** Adds a new web tab to webadmin. + Displaying the tab calls the referenced function. */ + bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // Exported in ManualBindings.cpp /** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */ void BindCommand(const AString & a_Command, int a_FnRef); diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 8935f7dd3..5b6bec728 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -59,39 +59,48 @@ void cPluginManager::ReloadPlugins(void) -void cPluginManager::FindPlugins(void) +void cPluginManager::RefreshPluginList(void) { + // Get a list of currently available folders: AString PluginsPath = GetPluginsPath() + "/"; - - // First get a clean list of only the currently running plugins, we don't want to mess those up - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) + AStringVector Contents = cFile::GetFolderContents(PluginsPath.c_str()); + AStringVector Folders; + for (auto & item: Contents) { - if (itr->second == nullptr) + if ((item == ".") || (item == "..") || (!cFile::IsFolder(PluginsPath + item))) { - PluginMap::iterator thiz = itr; - ++thiz; - m_Plugins.erase( itr); - itr = thiz; + // We only want folders, and don't want "." or ".." continue; } - ++itr; - } + Folders.push_back(item); + } // for item - Contents[] - AStringVector Files = cFile::GetFolderContents(PluginsPath.c_str()); - for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + // Set all plugins with invalid folders as psNotFound: + for (auto & plugin: m_Plugins) { - if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) + if (std::find(Folders.cbegin(), Folders.cend(), plugin->GetFolderName()) == Folders.end()) { - // We only want folders, and don't want "." or ".." - continue; + plugin->m_Status = psNotFound; } + } // for plugin - m_Plugins[] - // Add plugin name/directory to the list - if (m_Plugins.find(*itr) == m_Plugins.end()) + // Add all newly discovered plugins: + for (auto & folder: Folders) + { + bool hasFound = false; + for (auto & plugin: m_Plugins) { - m_Plugins[*itr] = nullptr; + if (plugin->GetFolderName() == folder) + { + hasFound = true; + break; + } + } // for plugin - m_Plugins[] + if (!hasFound) + { + m_Plugins.push_back(std::make_shared<cPluginLua>(folder)); } - } + } // for folder - Folders[] } @@ -109,60 +118,26 @@ void cPluginManager::ReloadPluginsNow(void) -void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) +void cPluginManager::ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings) { LOG("-- Loading Plugins --"); + + // Unload any existing plugins: m_bReloadPlugins = false; UnloadPluginsNow(); - FindPlugins(); - - cServer::BindBuiltInConsoleCommands(); - - // Check if the Plugins section exists. - int KeyNum = a_SettingsIni.FindKey("Plugins"); - - if (KeyNum == -1) - { - InsertDefaultPlugins(a_SettingsIni); - KeyNum = a_SettingsIni.FindKey("Plugins"); - } - - // How many plugins are there? - int NumPlugins = a_SettingsIni.GetNumValues(KeyNum); - - for (int i = 0; i < NumPlugins; i++) - { - AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); - if (ValueName.compare("Plugin") == 0) - { - AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); - if (!PluginFile.empty()) - { - if (m_Plugins.find(PluginFile) != m_Plugins.end()) - { - LoadPlugin(PluginFile); - } - } - } - } - + // Refresh the list of plugins to load new ones from disk / remove the deleted ones: + RefreshPluginList(); - // Remove invalid plugins from the PluginMap. - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) + // Load the plugins: + AStringVector ToLoad = GetFoldersToLoad(a_Settings); + for (auto & pluginFolder: ToLoad) { - if (itr->second == nullptr) - { - PluginMap::iterator thiz = itr; - ++thiz; - m_Plugins.erase(itr); - itr = thiz; - continue; - } - ++itr; - } + LoadPlugin(pluginFolder); + } // for pluginFolder - ToLoad[] - size_t NumLoadedPlugins = GetNumPlugins(); + // Log a report of the loading process + size_t NumLoadedPlugins = GetNumLoadedPlugins(); if (NumLoadedPlugins == 0) { LOG("-- No Plugins Loaded --"); @@ -173,7 +148,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) } else { - LOG("-- Loaded %i Plugins --", (int)NumLoadedPlugins); + LOG("-- Loaded %u Plugins --", static_cast<unsigned>(NumLoadedPlugins)); } CallHookPluginsLoaded(); } @@ -182,16 +157,16 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) -void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) +void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings) { - a_SettingsIni.AddKeyName("Plugins"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); - a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); - a_SettingsIni.AddValue("Plugins", "Plugin", "Core"); - a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI"); - a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog"); + a_Settings.AddKeyName("Plugins"); + a_Settings.AddKeyComment("Plugins", " Plugin=Debuggers"); + a_Settings.AddKeyComment("Plugins", " Plugin=HookNotify"); + a_Settings.AddKeyComment("Plugins", " Plugin=ChunkWorx"); + a_Settings.AddKeyComment("Plugins", " Plugin=APIDump"); + a_Settings.AddValue("Plugins", "Plugin", "Core"); + a_Settings.AddValue("Plugins", "Plugin", "TransAPI"); + a_Settings.AddValue("Plugins", "Plugin", "ChatLog"); } @@ -200,12 +175,39 @@ void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) void cPluginManager::Tick(float a_Dt) { - while (!m_DisablePluginList.empty()) + // Unload plugins that have been scheduled for unloading: + AStringVector PluginsToUnload; { - RemovePlugin(m_DisablePluginList.front()); - m_DisablePluginList.pop_front(); + cCSLock Lock(m_CSPluginsToUnload); + std::swap(m_PluginsToUnload, PluginsToUnload); } + for (auto & folder: PluginsToUnload) + { + bool HasUnloaded = false; + bool HasFound = false; + for (auto & plugin: m_Plugins) + { + if (plugin->GetFolderName() == folder) + { + HasFound = true; + if (plugin->IsLoaded()) + { + plugin->Unload(); + HasUnloaded = true; + } + } + } + if (!HasFound) + { + LOG("Cannot unload plugin in folder \"%s\", there's no such plugin folder", folder.c_str()); + } + else if (!HasUnloaded) + { + LOG("Cannot unload plugin in folder \"%s\", it has not been loaded.", folder.c_str()); + } + } // for plugin - m_Plugins[] + // If a plugin reload has been scheduled, reload now: if (m_bReloadPlugins) { ReloadPluginsNow(); @@ -523,14 +525,50 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d & -bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) +bool cPluginManager::CallHookEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) +{ + FIND_HOOK(HOOK_ENTITY_CHANGING_WORLD); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityChangingWorld(a_Entity, a_World)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) +{ + FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityChangedWorld(a_Entity, a_World)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result) { FIND_HOOK(HOOK_EXECUTE_COMMAND); VERIFY_HOOK; for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnExecuteCommand(a_Player, a_Split)) + if ((*itr)->OnExecuteCommand(a_Player, a_Split, a_EntireCommand, a_Result)) { return true; } @@ -1443,14 +1481,25 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, 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; } // Ask plugins first if a command is okay to execute the command: - if (CallHookExecuteCommand(&a_Player, Split)) + CommandResult Result = crBlocked; + if (CallHookExecuteCommand(&a_Player, Split, a_Command, Result)) { - 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 crBlocked; + 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 ( @@ -1477,68 +1526,56 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, -cPlugin * cPluginManager::GetPlugin(const AString & a_Plugin) const +void cPluginManager::UnloadPluginsNow() { - for (PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - if (itr->second == nullptr) - { - // The plugin is currently unloaded - continue; - } + // Remove all bindings: + m_Hooks.clear(); + m_Commands.clear(); + m_ConsoleCommands.clear(); - if (itr->second->GetName().compare(a_Plugin) == 0) + // Re-bind built-in console commands: + cServer::BindBuiltInConsoleCommands(); + + // Unload all loaded plugins: + for (auto & plugin: m_Plugins) + { + if (plugin->IsLoaded()) { - return itr->second; + plugin->Unload(); } } - return 0; } -const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const +void cPluginManager::UnloadPlugin(const AString & a_PluginFolder) { - return m_Plugins; + cCSLock Lock(m_CSPluginsToUnload); + m_PluginsToUnload.push_back(a_PluginFolder); } -void cPluginManager::UnloadPluginsNow() +bool cPluginManager::LoadPlugin(const AString & a_FolderName) { - m_Hooks.clear(); - - while (!m_Plugins.empty()) + for (auto & plugin: m_Plugins) { - RemovePlugin(m_Plugins.begin()->second); - } - - m_Commands.clear(); - m_ConsoleCommands.clear(); -} - - - - - -bool cPluginManager::DisablePlugin(const AString & a_PluginName) -{ - PluginMap::iterator itr = m_Plugins.find(a_PluginName); - if (itr == m_Plugins.end()) - { - return false; - } + if (plugin->GetFolderName() == a_FolderName) + { + if (!plugin->IsLoaded()) + { + return plugin->Load(); + } + return true; + } + } // for plugin - m_Plugins[] - if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does? - { - m_DisablePluginList.push_back(itr->second); - itr->second = nullptr; // Get rid of this thing right away - return true; - } + // Plugin not found + LOG("Cannot load plugin, folder \"%s\" not found.", a_FolderName.c_str()); return false; } @@ -1546,15 +1583,6 @@ bool cPluginManager::DisablePlugin(const AString & a_PluginName) -bool cPluginManager::LoadPlugin(const AString & a_PluginName) -{ - return AddPlugin(new cPluginLua(a_PluginName.c_str())); -} - - - - - void cPluginManager::RemoveHooks(cPlugin * a_Plugin) { for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr) @@ -1567,32 +1595,6 @@ void cPluginManager::RemoveHooks(cPlugin * a_Plugin) -void cPluginManager::RemovePlugin(cPlugin * a_Plugin) -{ - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - if (itr->second == a_Plugin) - { - m_Plugins.erase(itr); - break; - } - } - - RemovePluginCommands(a_Plugin); - RemovePluginConsoleCommands(a_Plugin); - RemoveHooks(a_Plugin); - if (a_Plugin != nullptr) - { - a_Plugin->OnDisable(); - } - delete a_Plugin; - a_Plugin = nullptr; -} - - - - - void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) { if (a_Plugin != nullptr) @@ -1619,6 +1621,22 @@ void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) +bool cPluginManager::IsPluginLoaded(const AString & a_PluginName) +{ + for (auto & plugin: m_Plugins) + { + if (plugin->GetName() == a_PluginName) + { + return true; + } + } + return false; +} + + + + + bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) { CommandMap::iterator cmd = m_Commands.find(a_Command); @@ -1779,7 +1797,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma if (cmd == m_ConsoleCommands.end()) { // Command not found - return false; + // 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 (res == crExecuted); } if (cmd->second.m_Plugin == nullptr) @@ -1789,10 +1810,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma } // Ask plugins first if a command is okay to execute the console command: - if (CallHookExecuteCommand(nullptr, a_Split)) + CommandResult res = crBlocked; + if (CallHookExecuteCommand(nullptr, a_Split, a_Command, res)) { - a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); - return false; + return (res == crExecuted); } return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command); @@ -1836,31 +1857,31 @@ bool cPluginManager::IsValidHookType(int a_HookType) bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback) { // TODO: Implement locking for plugins - PluginMap::iterator itr = m_Plugins.find(a_PluginName); - if ((itr == m_Plugins.end()) || (itr->second == nullptr)) + for (auto & plugin: m_Plugins) { - return false; + if (plugin->GetName() == a_PluginName) + { + return a_Callback.Item(plugin.get()); + } } - return a_Callback.Item(itr->second); + return false; } -bool cPluginManager::AddPlugin(cPlugin * a_Plugin) +bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback) { - m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; - - if (a_Plugin->Initialize()) + // TODO: Implement locking for plugins + for (auto & plugin: m_Plugins) { - // Initialization OK - return true; + if (a_Callback.Item(plugin.get())) + { + return false; + } } - - // Initialization failed - RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made - return false; + return true; } @@ -1869,21 +1890,23 @@ bool cPluginManager::AddPlugin(cPlugin * a_Plugin) void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) { - if (!a_Plugin) + if (a_Plugin == nullptr) { LOGWARN("Called cPluginManager::AddHook() with a_Plugin == nullptr"); return; } PluginList & Plugins = m_Hooks[a_Hook]; - Plugins.remove(a_Plugin); - Plugins.push_back(a_Plugin); + if (std::find(Plugins.cbegin(), Plugins.cend(), a_Plugin) == Plugins.cend()) + { + Plugins.push_back(a_Plugin); + } } -size_t cPluginManager::GetNumPlugins() const +size_t cPluginManager::GetNumPlugins(void) const { return m_Plugins.size(); } @@ -1891,3 +1914,51 @@ size_t cPluginManager::GetNumPlugins() const + +size_t cPluginManager::GetNumLoadedPlugins(void) const +{ + size_t res = 0; + for (auto & plugin: m_Plugins) + { + if (plugin->IsLoaded()) + { + res += 1; + } + } + return res; +} + + + + + +AStringVector cPluginManager::GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings) +{ + // Check if the Plugins section exists. + if (!a_Settings.KeyExists("Plugins")) + { + InsertDefaultPlugins(a_Settings); + } + + // Get the list of plugins to load: + AStringVector res; + auto Values = a_Settings.GetValues("Plugins"); + for (auto NameValue : Values) + { + AString ValueName = NameValue.first; + if (ValueName.compare("Plugin") == 0) + { + AString PluginFile = NameValue.second; + if (!PluginFile.empty()) + { + res.push_back(PluginFile); + } + } + } // for i - ini values + + return res; +} + + + + diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 4efcbb6f3..6bcef87bf 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -6,48 +6,31 @@ -class cPlugin; -// fwd: World.h -class cWorld; -// fwd: ChunkDesc.h +// fwd: +class cBlockEntityWithItems; class cChunkDesc; - -// fwd: Entities/Entity.h -class cEntity; - -// fwd: Entities/ProjectileEntity.h -class cProjectileEntity; - -// fwd: Mobs/Monster.h -class cMonster; - -// fwd: Player.h -class cPlayer; - -// fwd: CraftingRecipes.h +class cClientHandle; +class cCommandOutputCallback; class cCraftingGrid; class cCraftingRecipe; - -// fwd: Pickup.h +class cEntity; +class cHopperEntity; +class cItems; +class cMonster; class cPickup; - -// fwd: Pawn.h +class cPlayer; +class cPlugin; +class cProjectileEntity; +class cWorld; +class cSettingsRepositoryInterface; struct TakeDamageInfo; -// fwd: CommandOutput.h -class cCommandOutputCallback; - -// fwd: BlockEntities/HopperEntity.h -class cHopperEntity; - -// fwd: BlockEntities/BlockEntityWithItems.h -class cBlockEntityWithItems; - +typedef SharedPtr<cPlugin> cPluginPtr; +typedef std::vector<cPluginPtr> cPluginPtrs; -class cItems; @@ -55,12 +38,7 @@ class cItems; class cPluginManager { public: - // tolua_end - - // Called each tick - virtual void Tick(float a_Dt); - - // tolua_begin + enum CommandResult { crExecuted, @@ -70,6 +48,29 @@ public: crNoPermission, } ; + + /** Defines the status of a single plugin - whether it is loaded, disabled or errored. */ + enum ePluginStatus + { + /** The plugin has been loaded successfully. */ + psLoaded, + + /** The plugin is disabled in settings.ini. */ + psDisabled, + + /** The plugin is enabled in settings.ini but has been unloaded (by a command). */ + psUnloaded, + + /** The plugin is enabled in settings.ini but has failed to load. + m_LoadError is the description of the error. */ + psError, + + /** The plugin has been loaded before, but after a folder refresh it is no longer present. + The plugin will be unloaded in the next call to ReloadPlugins(). */ + psNotFound, + }; + + enum PluginHook { HOOK_BLOCK_SPREAD, @@ -85,6 +86,8 @@ public: HOOK_DISCONNECT, HOOK_PLAYER_ANIMATION, HOOK_ENTITY_ADD_EFFECT, + HOOK_ENTITY_CHANGING_WORLD, + HOOK_ENTITY_CHANGED_WORLD, HOOK_EXECUTE_COMMAND, HOOK_EXPLODED, HOOK_EXPLODING, @@ -134,6 +137,8 @@ public: HOOK_WEATHER_CHANGING, HOOK_WORLD_STARTED, HOOK_WORLD_TICK, + + // tolua_end // Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants, // and it definitely needs adding in cPluginLua::GetHookFnName() ! @@ -141,8 +146,7 @@ public: // Keep these two as the last items, they are used for validity checking and get their values automagically HOOK_NUM_HOOKS, HOOK_MAX = HOOK_NUM_HOOKS - 1, - } ; - // tolua_end + } ; // tolua_export /** Used as a callback for enumerating bound commands */ class cCommandEnumCallback @@ -159,24 +163,31 @@ public: /** The interface used for enumerating and extern-calling plugins */ typedef cItemCallback<cPlugin> cPluginCallback; + typedef std::list<cPlugin *> PluginList; + + + /** Called each tick, calls the plugins' OnTick hook, as well as processes plugin events (addition, removal) */ + void Tick(float a_Dt); /** Returns the instance of the Plugin Manager (there is only ever one) */ static cPluginManager * Get(void); // tolua_export - typedef std::map< AString, cPlugin * > PluginMap; - typedef std::list< cPlugin * > PluginList; - cPlugin * GetPlugin( const AString & a_Plugin) const; // tolua_export - const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << + /** Refreshes the m_Plugins list based on the current contents of the Plugins folder. + If an active plugin's folder is not found anymore, the plugin is set as psNotFound, but not yet unloaded. */ + void RefreshPluginList(); // tolua_export - // tolua_begin - void FindPlugins(); - void ReloadPlugins(); - // tolua_end + /** Schedules a reload of the plugins to happen within the next call to Tick(). */ + void ReloadPlugins(); // tolua_export - /** Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add */ + /** Adds the plugin to the list of plugins called for the specified hook type. + If a plugin adds multiple handlers for a single hook, it is added only once (ignore-duplicates). */ void AddHook(cPlugin * a_Plugin, int a_HookType); + /** Returns the number of all plugins in m_Plugins (includes disabled, unloaded and errored plugins). */ size_t GetNumPlugins() const; // tolua_export + + /** Returns the number of plugins that are psLoaded. */ + size_t GetNumLoadedPlugins(void) const; // tolua_export // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort bool CallHookBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source); @@ -192,7 +203,9 @@ public: bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); - bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd + bool CallHookEntityChangingWorld (cEntity & a_Entity, cWorld & a_World); + bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World); + bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookHandshake (cClientHandle & a_ClientHandle, const AString & a_Username); @@ -241,18 +254,26 @@ public: bool CallHookWorldStarted (cWorld & a_World); bool CallHookWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); - bool DisablePlugin(const AString & a_PluginName); // tolua_export - bool LoadPlugin (const AString & a_PluginName); // tolua_export + /** Queues the specified plugin to be unloaded in the next call to Tick(). + Note that this function returns before the plugin is unloaded, to avoid deadlocks. */ + void UnloadPlugin(const AString & a_PluginFolder); // tolua_export + + /** Loads the plugin from the specified plugin folder. + Returns true if the plugin was loaded successfully or was already loaded before, false otherwise. */ + bool LoadPlugin(const AString & a_PluginFolder); // tolua_export /** Removes all hooks the specified plugin has registered */ void RemoveHooks(cPlugin * a_Plugin); - /** Removes the plugin from the internal structures and deletes its object. */ - void RemovePlugin(cPlugin * a_Plugin); + /** Removes the plugin of the specified name from the internal structures and deletes its object. */ + void RemovePlugin(const AString & a_PluginName); /** Removes all command bindings that the specified plugin has made */ void RemovePluginCommands(cPlugin * a_Plugin); - + + /** Returns true if the specified plugin is loaded. */ + bool IsPluginLoaded(const AString & a_PluginName); // tolua_export + /** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */ bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param @@ -283,7 +304,9 @@ public: /** Returns true if the console command is in the command map */ bool IsConsoleCommandBound(const AString & a_Command); // tolua_export - /** 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 */ + /** 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 + Exported in ManualBindings.cpp with a different signature. */ bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command); /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. @@ -295,8 +318,12 @@ public: static bool IsValidHookType(int a_HookType); /** Calls the specified callback with the plugin object of the specified plugin. - Returns false if plugin not found, and the value that the callback has returned otherwise. */ + Returns false if plugin not found, otherwise returns the value that the callback has returned. */ bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback); + + /** Calls the specified callback for each plugin in m_Plugins. + Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */ + bool ForEachPlugin(cPluginCallback & a_Callback); /** Returns the path where individual plugins' folders are expected. The path doesn't end in a slash. */ @@ -316,34 +343,46 @@ private: typedef std::map<int, cPluginManager::PluginList> HookMap; typedef std::map<AString, cCommandReg> CommandMap; - PluginList m_DisablePluginList; - PluginMap m_Plugins; + + /** FolderNames of plugins that should be unloaded. + The plugins will be unloaded within the next call to Tick(), to avoid multithreading issues. + Protected against multithreaded access by m_CSPluginsToUnload. */ + AStringVector m_PluginsToUnload; + + /** Protects m_PluginsToUnload against multithreaded access. */ + mutable cCriticalSection m_CSPluginsToUnload; + + /** All plugins that have been found in the Plugins folder. */ + cPluginPtrs m_Plugins; + HookMap m_Hooks; CommandMap m_Commands; CommandMap m_ConsoleCommands; + /** If set to true, all the plugins will be reloaded within the next call to Tick(). */ bool m_bReloadPlugins; + cPluginManager(); virtual ~cPluginManager(); /** Reloads all plugins, defaulting to settings.ini for settings location */ void ReloadPluginsNow(void); - /** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */ - void ReloadPluginsNow(cIniFile & a_SettingsIni); + /** Reloads all plugins with a settings repo expected to be initialised to settings.ini */ + void ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings); /** Unloads all plugins */ void UnloadPluginsNow(void); - /** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */ - void InsertDefaultPlugins(cIniFile & a_SettingsIni); - - /** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */ - bool AddPlugin(cPlugin * a_Plugin); + /** Handles writing default plugins if 'Plugins' key not found using a settings repo expected to be intialised to settings.ini */ + void InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings); /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */ CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); + + /** Returns the folders that are specified in the settings ini to load plugins from. */ + AStringVector GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings); } ; // tolua_export diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp index 5759b20e7..1eca7de93 100644 --- a/src/Bindings/WebPlugin.cpp +++ b/src/Bindings/WebPlugin.cpp @@ -24,75 +24,82 @@ cWebPlugin::cWebPlugin() cWebPlugin::~cWebPlugin() { + ASSERT(m_Tabs.empty()); // Has ClearTabs() been called? + + // Remove from WebAdmin: cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); if (WebAdmin != nullptr) { WebAdmin->RemovePlugin(this); } +} + + + - for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr) + +cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const +{ + std::list< std::pair<AString, AString>> NameList; + for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) { - delete *itr; + NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle)); } - m_Tabs.clear(); + return NameList; } -std::list<std::pair<AString, AString> > cWebPlugin::GetTabNames(void) +cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const { - std::list< std::pair< AString, AString > > NameList; - for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr) + cCSLock Lock(m_CSTabs); + for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) { - std::pair< AString, AString > StringPair; - StringPair.first = (*itr)->Title; - StringPair.second = (*itr)->SafeTitle; - NameList.push_back( StringPair); + if ((*itr)->m_SafeTitle == a_SafeTitle) + { + return *itr; + } } - return NameList; + return nullptr; } -std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request) +std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request) { - std::pair< AString, AString > Names; - AStringVector Split = StringSplit(a_Request->Path, "/"); + AStringVector Split = StringSplit(a_Request.Path, "/"); + if (Split.empty()) + { + return std::make_pair(AString(), AString()); + } - if (Split.size() > 1) + cCSLock Lock(m_CSTabs); + cTabPtr Tab; + if (Split.size() > 2) // If we got the tab name, show that page { - sWebPluginTab * Tab = nullptr; - if (Split.size() > 2) // If we got the tab name, show that page + for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) { - for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr) + if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one! { - if ((*itr)->SafeTitle.compare(Split[2]) == 0) // This is the one! - { - Tab = *itr; - break; - } - } - } - else // Otherwise show the first tab - { - if (GetTabs().size() > 0) - { - Tab = *GetTabs().begin(); + return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle); } } + // Tab name not found, display an "empty" page: + return std::make_pair(AString(), AString()); + } - if (Tab != nullptr) - { - Names.first = Tab->Title; - Names.second = Tab->SafeTitle; - } + // Show the first tab: + if (!m_Tabs.empty()) + { + return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle); } - return Names; + // No tabs at all: + return std::make_pair(AString(), AString()); } @@ -101,14 +108,16 @@ std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest AString cWebPlugin::SafeString(const AString & a_String) { AString RetVal; - for (unsigned int i = 0; i < a_String.size(); ++i) + auto len = a_String.size(); + RetVal.reserve(len); + for (size_t i = 0; i < len; ++i) { char c = a_String[i]; if (c == ' ') { c = '_'; } - RetVal.push_back( c); + RetVal.push_back(c); } return RetVal; } @@ -116,3 +125,28 @@ AString cWebPlugin::SafeString(const AString & a_String) + +void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData) +{ + auto Tab = std::make_shared<cTab>(a_Title, a_UserData); + cCSLock Lock(m_CSTabs); + m_Tabs.push_back(Tab); +} + + + + + +void cWebPlugin::ClearTabs(void) +{ + // Remove the webadmin tabs: + cTabPtrs Tabs; + { + cCSLock Lock(m_CSTabs); + std::swap(Tabs, m_Tabs); + } +} + + + + diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h index 9b825b918..ade4f4359 100644 --- a/src/Bindings/WebPlugin.h +++ b/src/Bindings/WebPlugin.h @@ -12,34 +12,67 @@ class cWebPlugin { public: // tolua_end + + struct cTab + { + AString m_Title; + AString m_SafeTitle; + int m_UserData; + + cTab(const AString & a_Title, int a_UserData): + m_Title(a_Title), + m_SafeTitle(cWebPlugin::SafeString(a_Title)), + m_UserData(a_UserData) + { + } + }; + + typedef SharedPtr<cTab> cTabPtr; + typedef std::list<cTabPtr> cTabPtrs; + typedef std::list<std::pair<AString, AString>> cTabNames; + + cWebPlugin(); + virtual ~cWebPlugin(); // tolua_begin + + /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */ virtual const AString GetWebTitle(void) const = 0; - virtual AString HandleWebRequest(const HTTPRequest * a_Request) = 0; + /** Sanitizes the input string, replacing spaces with underscores. */ + static AString SafeString(const AString & a_String); - static AString SafeString( const AString & a_String); // tolua_end - struct sWebPluginTab - { - std::string Title; - std::string SafeTitle; + virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0; - int UserData; - }; + /** Adds a new web tab with the specified contents. */ + void AddNewWebTab(const AString & a_Title, int a_UserData); + + /** Removes all the tabs. */ + void ClearTabs(void); - typedef std::list< sWebPluginTab* > TabList; - TabList & GetTabs() { return m_Tabs; } + /** Returns all the tabs that this plugin has registered. */ + const cTabPtrs & GetTabs(void) const { return m_Tabs; } - typedef std::list< std::pair<AString, AString> > TabNameList; - TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS << - std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request); + /** Returns all of the tabs that this plugin has registered. */ + cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp + + /** Returns the tab that has the specified SafeTitle. + Returns nullptr if no such tab. */ + cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const; + + std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request); private: - TabList m_Tabs; + /** All tabs that this plugin has registered. + Protected against multithreaded access by m_CSTabs. */ + cTabPtrs m_Tabs; + + /** Protects m_Tabs against multithreaded access. */ + mutable cCriticalSection m_CSTabs; }; // tolua_export diff --git a/src/Bindings/virtual_method_hooks.lua b/src/Bindings/virtual_method_hooks.lua deleted file mode 100644 index a52728ff8..000000000 --- a/src/Bindings/virtual_method_hooks.lua +++ /dev/null @@ -1,518 +0,0 @@ --- flags -local disable_virtual_hooks = true -local enable_pure_virtual = true -local default_private_access = false - - - - - -local access = {public = 0, protected = 1, private = 2} - -function preparse_hook(p) - - if default_private_access then - -- we need to make all structs 'public' by default - p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") - end -end - - -function parser_hook(s) - - local container = classContainer.curr -- get the current container - - if default_private_access then - if not container.curr_member_access and container.classtype == 'class' then - -- default access for classes is private - container.curr_member_access = access.private - end - end - - -- try labels (public, private, etc) - do - local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' - if b then - - -- found a label, get the new access value from the global 'access' table - if access[label] then - container.curr_member_access = access[label] - end -- else ? - - return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] - end - end - - - local ret = nil - - if disable_virtual_hooks then - - return ret - end - - local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") - local const - if b then - local ret = string.sub(s, e+1) - if string.find(ret, "^%s*const") then - const = "const" - ret = string.gsub(ret, "^%s*const", "") - end - local purev = false - if string.find(ret, "^%s*=%s*0") then - purev = true - ret = string.gsub(ret, "^%s*=%s*0", "") - end - ret = string.gsub(ret, "^%s*%b{}", "") - - local func = Function(decl, arg, const) - func.pure_virtual = purev - --func.access = access - func.original_sig = decl - - local curflags = classContainer.curr.flags - if not curflags.virtual_class then - - curflags.virtual_class = VirtualClass() - end - curflags.virtual_class:add(func) - curflags.pure_virtual = curflags.pure_virtual or purev - - return ret - end - - return ret -end - - --- class VirtualClass -classVirtualClass = { - classtype = 'class', - name = '', - base = '', - type = '', - btype = '', - ctype = '', -} -classVirtualClass.__index = classVirtualClass -setmetatable(classVirtualClass,classClass) - -function classVirtualClass:add(f) - - local parent = classContainer.curr - pop() - - table.insert(self.methods, {f=f}) - - local name,sig - - -- doble negative means positive - if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then - - name = self.original_name - elseif f.name == 'delete' then - name = '~'..self.original_name - else - if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then - name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name - end - end - - if name then - sig = name..self:get_arg_list(f, true)..";\n" - push(self) - sig = preprocess(sig) - self:parse(sig) - pop() - end - - push(parent) -end - -function preprocess(sig) - - sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' - sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' - sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' - sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' - - return sig -end - -function classVirtualClass:get_arg_list(f, decl) - - local ret = "" - local sep = "" - local i=1 - while f.args[i] do - - local arg = f.args[i] - if decl then - local ptr - if arg.ret ~= '' then - ptr = arg.ret - else - ptr = arg.ptr - end - local def = "" - if arg.def and arg.def ~= "" then - - def = " = "..arg.def - end - ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def - else - ret = ret..sep..arg.name - end - - sep = "," - i = i+1 - end - - return "("..ret..")" -end - -function classVirtualClass:add_parent_virtual_methods(parent) - - parent = parent or _global_classes[self.flags.parent_object.btype] - - if not parent then return end - - if parent.flags.virtual_class then - - local vclass = parent.flags.virtual_class - for k,v in ipairs(vclass.methods) do - if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then - table.insert(self.methods, {f=v.f}) - end - end - end - - parent = _global_classes[parent.btype] - if parent then - self:add_parent_virtual_methods(parent) - end -end - -function classVirtualClass:has_method(f) - - for k,v in pairs(self.methods) do - -- just match name for now - if v.f.name == f.name then - return true - end - end - - return false -end - -function classVirtualClass:add_constructors() - - local i=1 - while self.flags.parent_object[i] do - - local v = self.flags.parent_object[i] - if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then - - self:add(v) - end - - i = i+1 - end - -end - ---[[ -function classVirtualClass:requirecollection(t) - - self:add_constructors() - local req = classClass.requirecollection(self, t) - if req then - output('class ',self.name,";") - end - return req -end ---]] - -function classVirtualClass:supcode() - - -- pure virtual classes can have no default constructors on gcc 4 - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') - end - - local ns - if self.prox.classtype == 'namespace' then - output('namespace ',self.prox.name, " {") - ns = true - end - - output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") - - output("public:\n") - - self:add_parent_virtual_methods() - - self:output_methods(self.btype) - self:output_parent_methods() - - self:add_constructors() - - -- no constructor for pure virtual classes - if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then - - self:output_constructors() - end - - output("};\n\n") - - if ns then - output("};") - end - - classClass.supcode(self) - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#endif // __GNUC__ >= 4\n') - end - - -- output collector for custom class if required - if self:requirecollection(_collect) and _collect[self.type] then - - output('\n') - output('/* function to release collected object via destructor */') - output('#ifdef __cplusplus\n') - --for i,v in pairs(collect) do - i,v = self.type, _collect[self.type] - output('\nstatic int '..v..' (lua_State* tolua_S)') - output('{') - output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') - output(' delete self;') - output(' return 0;') - output('}') - --end - output('#endif\n\n') - end - -end - -function classVirtualClass:register(pre) - - -- pure virtual classes can have no default constructors on gcc 4 - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') - end - - classClass.register(self, pre) - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#endif // __GNUC__ >= 4\n') - end -end - - ---function classVirtualClass:requirecollection(_c) --- if self.flags.parent_object.flags.pure_virtual then --- return false --- end --- return classClass.requirecollection(self, _c) ---end - -function classVirtualClass:output_parent_methods() - - for k,v in ipairs(self.methods) do - - if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then - - local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " - local parent_name = rettype..self.btype.."__"..v.f.name - - local par_list = self:get_arg_list(v.f, true) - local var_list = self:get_arg_list(v.f, false) - - -- the parent's virtual function - output("\t"..parent_name..par_list.." {") - - output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") - output("\t};") - end - end -end - -function classVirtualClass:output_methods(btype) - - for k,v in ipairs(self.methods) do - - if v.f.name ~= 'new' and v.f.name ~= 'delete' then - - self:output_method(v.f, btype) - end - end - output("\n") -end - -function classVirtualClass:output_constructors() - - for k,v in ipairs(self.methods) do - - if v.f.name == 'new' then - - local par_list = self:get_arg_list(v.f, true) - local var_list = self:get_arg_list(v.f, false) - - output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") - end - end -end - -function classVirtualClass:output_method(f, btype) - - if f.access == 2 then -- private - return - end - - local ptr - if f.ret ~= '' then - ptr = f.ret - else - ptr = f.ptr - end - - local rettype = f.mod.." "..f.type..f.ptr.." " - local par_list = self:get_arg_list(f, true) - local var_list = self:get_arg_list(f, false) - - if string.find(rettype, "%s*LuaQtGenericFlags%s*") then - - _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") - end - - -- the caller of the lua method - output("\t"..rettype.." "..f.name..par_list..f.const.." {") - local fn = f.cname - if f.access == 1 then - fn = "NULL" - end - output('\t\tif (push_method("',f.lname,'", ',fn,')) {') - - --if f.type ~= 'void' then - -- output("\t\t\tint top = lua_gettop(lua_state)-1;") - --end - - -- push the parameters - local argn = 0 - for i,arg in ipairs(f.args) do - if arg.type ~= 'void' then - local t,ct = isbasic(arg.type) - if t and t ~= '' then - if arg.ret == "*" then - t = 'userdata' - ct = 'void*' - end - output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); - else - local m = arg.ptr - if m and m~= "" then - if m == "*" then m = "" end - output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") - else - output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") - output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') - end - end - argn = argn+1 - end - end - - -- call the function - output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") - - -- return value - if f.type ~= 'void' then - output("1);") - - local t,ct = isbasic(f.type) - if t and t ~= '' then - --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") - output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") - else - - local mod = "" - if f.ptr ~= "*" then - mod = "*("..f.type.."*)" - end - - --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") - output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") - end - output("\t\t\tlua_pop(lua_state, 1);") - output("\t\t\treturn tolua_ret;") - else - output("0);") - end - - -- handle non-implemeted function - output("\t\t} else {") - - if f.pure_virtual then - - output('\t\t\tif (lua_state)') - --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') - output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') - output('\t\t\telse {') - output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') - output('\t\t\t\t::abort();') - output('\t\t\t};') - if( rettype == " std::string " ) then - output('\t\t\treturn "";') - else - output('\t\t\treturn (',rettype,')0;') - end - else - - output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') - end - - output("\t\t};") - - output("\t};") -end - -function VirtualClass() - - local parent = classContainer.curr - pop() - - local name = "Lua__"..parent.original_name - - local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) - setmetatable(c, classVirtualClass) - - local ft = getnamespace(c.parent)..c.original_name - append_global_type(ft, c) - - push(parent) - - c.flags.parent_object = parent - c.methods = {} - - push(c) - c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") - pop() - - return c -end - - - - - - -function post_output_hook() - print("Bindings have been generated.") -end - - - - diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 4c3da0535..89cf18d4a 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -245,6 +245,26 @@ void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBL +/** Combinator used for cBlockArea::msSimpleCompare merging */ +template <bool MetaValid> +void MergeCombinatorSimpleCompare(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta))) + { + // The blocktypes are the same, and the blockmetas are not present or are the same + a_DstType = E_BLOCK_AIR; + } + else + { + // The blocktypes or blockmetas differ + a_DstType = E_BLOCK_STONE; + } +} + + + + + /** Combinator used for cBlockArea::msMask merging */ template <bool MetaValid> void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) @@ -1614,6 +1634,104 @@ void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTY +size_t cBlockArea::CountNonAirBlocks(void) const +{ + // Check if blocktypes are valid: + if (m_BlockTypes == nullptr) + { + LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__); + return 0; + } + + // Count the blocks: + size_t res = 0; + for (int y = 0; y < m_Size.y; y++) + { + for (int z = 0; z < m_Size.z; z++) + { + for (int x = 0; x < m_Size.x; x++) + { + if (m_BlockTypes[MakeIndex(x, y, z)] != E_BLOCK_AIR) + { + ++res; + } + } // for x + } // for z + } // for y + return res; +} + + + + + +void cBlockArea::GetNonAirCropRelCoords(int & a_MinRelX, int & a_MinRelY, int & a_MinRelZ, int & a_MaxRelX, int & a_MaxRelY, int & a_MaxRelZ, BLOCKTYPE a_IgnoreBlockType) +{ + // Check if blocktypes are valid: + if (m_BlockTypes == nullptr) + { + LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__); + a_MinRelX = 1; + a_MaxRelX = 0; + return; + } + + // Walk all the blocks and find the min and max coords for the non-ignored ones: + int MaxX = 0, MinX = m_Size.x - 1; + int MaxY = 0, MinY = m_Size.y - 1; + int MaxZ = 0, MinZ = m_Size.z - 1; + for (int y = 0; y < m_Size.y; y++) + { + for (int z = 0; z < m_Size.z; z++) + { + for (int x = 0; x < m_Size.x; x++) + { + if (m_BlockTypes[MakeIndex(x, y, z)] == a_IgnoreBlockType) + { + continue; + } + // The block is not ignored, update any coords that need updating: + if (x < MinX) + { + MinX = x; + } + if (x > MaxX) + { + MaxX = x; + } + if (y < MinY) + { + MinY = y; + } + if (y > MaxY) + { + MaxY = y; + } + if (z < MinZ) + { + MinZ = z; + } + if (z > MaxZ) + { + MaxZ = z; + } + } // for x + } // for z + } // for y + + // Assign to the output: + a_MinRelX = MinX; + a_MinRelY = MinY; + a_MinRelZ = MinZ; + a_MaxRelX = MaxX; + a_MaxRelY = MaxY; + a_MaxRelZ = MaxZ; +} + + + + + int cBlockArea::GetDataTypes(void) const { int res = 0; @@ -2121,10 +2239,12 @@ void cBlockArea::RelSetData( + + template <bool MetasValid> void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas) { - // Block types are compulsory, block metas are voluntary + // Block types are compulsory, block metas are optional if (!HasBlockTypes() || !a_Src.HasBlockTypes()) { LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__); @@ -2230,6 +2350,20 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel return; } // case msDifference + case cBlockArea::msSimpleCompare: + { + InternalMergeBlocks<MetasValid, MergeCombinatorSimpleCompare<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + return; + } // case msSimpleCompare + case cBlockArea::msMask: { InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >( diff --git a/src/BlockArea.h b/src/BlockArea.h index 348e960dd..856df542f 100644 --- a/src/BlockArea.h +++ b/src/BlockArea.h @@ -54,6 +54,7 @@ public: msLake, msSpongePrint, msDifference, + msSimpleCompare, msMask, } ; @@ -156,6 +157,22 @@ public: | A | sponge | A | Sponge is the NOP block | * | B | B | Everything else overwrites anything + msDifference: + Used to determine the differences between two areas. Only the differring blocks are preserved: + | area block | | + | this | Src | result | + +------+-------+--------+ + | A | A | air | Same blocks are replaced with air + | A | non-A | A | Differring blocks are kept from "this" + + msSimpleCompare: + Used to determine the differences between two areas. Blocks that differ are replaced with stone, same blocks are replaced with air + | area block | | + | this | Src | result | + +------+-------+--------+ + | A | A | air | Same blocks are replaced with air + | A | non-A | stone | Differring blocks are replaced with stone + msMask: Combines two areas, the blocks that are the same are kept, differing ones are reset to air | area block | | @@ -286,8 +303,17 @@ public: bool HasBlockMetas (void) const { return (m_BlockMetas != nullptr); } bool HasBlockLights (void) const { return (m_BlockLight != nullptr); } bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != nullptr); } - + + /** Returns the count of blocks that are not air. + Returns 0 if blocktypes not available. Block metas are ignored (if present, air with any meta is still considered air). */ + size_t CountNonAirBlocks(void) const; + // tolua_end + + /** Returns the minimum and maximum coords in each direction for the first non-ignored block in each direction. + If there are no non-ignored blocks within the area, or blocktypes are not present, the returned values are reverse-ranges (MinX <- m_RangeX, MaxX <- 0 etc.) + Exported to Lua in ManualBindings.cpp. */ + void GetNonAirCropRelCoords(int & a_MinRelX, int & a_MinRelY, int & a_MinRelZ, int & a_MaxRelX, int & a_MaxRelY, int & a_MaxRelZ, BLOCKTYPE a_IgnoreBlockType = E_BLOCK_AIR); // Clients can use these for faster access to all blocktypes. Be careful though! /** Returns the internal pointer to the block types */ diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index 740dbca51..30a09bc02 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -76,7 +76,7 @@ protected: cItemGrid m_Contents; // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override { UNUSED(a_SlotNum); ASSERT(a_Grid == &m_Contents); diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index bfd4b8322..203f47534 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -430,7 +430,7 @@ bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) } // Try move from the output slot: - if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) + if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput)) { cItem NewOutput(Furnace->GetOutputSlot()); Furnace->SetOutputSlot(NewOutput.AddCount(-1)); @@ -440,7 +440,7 @@ bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) // No output moved, check if we can move an empty bucket out of the fuel slot: if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) { - if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true)) + if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel)) { Furnace->SetFuelSlot(cItem()); return true; @@ -460,28 +460,13 @@ bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity) cItemGrid & Grid = a_Entity.GetContents(); int NumSlots = Grid.GetNumSlots(); - // First try adding items of types already in the hopper: for (int i = 0; i < NumSlots; i++) { if (Grid.IsSlotEmpty(i)) { continue; } - if (MoveItemsFromSlot(a_Entity, i, false)) - { - Grid.ChangeSlotCount(i, -1); - return true; - } - } - - // No already existing stack can be topped up, try again with allowing new stacks: - for (int i = 0; i < NumSlots; i++) - { - if (Grid.IsSlotEmpty(i)) - { - continue; - } - if (MoveItemsFromSlot(a_Entity, i, true)) + if (MoveItemsFromSlot(a_Entity, i)) { Grid.ChangeSlotCount(i, -1); return true; @@ -495,21 +480,19 @@ bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity) /// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. -bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks) +bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum) { cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne()); for (int i = 0; i < ContentsWidth * ContentsHeight; i++) { if (m_Contents.IsSlotEmpty(i)) { - if (a_AllowNewStacks) + if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) { - if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) - { - // Plugin disagrees with the move - continue; - } + // Plugin disagrees with the move + continue; } + m_Contents.SetSlot(i, One); return true; } @@ -521,8 +504,14 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl continue; } + auto PreviousCount = m_Contents.GetSlot(i).m_ItemCount; m_Contents.ChangeSlotCount(i, 1); - return true; + + if (PreviousCount == m_Contents.GetSlot(i).m_ItemCount + 1) + { + // Successfully added a new item. (Failure condition consistutes: stack full) + return true; + } } } return false; diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index af99c526d..16d4cb6f9 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -74,7 +74,7 @@ protected: bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity); /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. - bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks); + bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum); /// Moves items to the chest at the specified coords. Returns true if contents have changed bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); diff --git a/src/BlockEntities/MobHeadEntity.cpp b/src/BlockEntities/MobHeadEntity.cpp index 67e13ffb2..328ade23a 100644 --- a/src/BlockEntities/MobHeadEntity.cpp +++ b/src/BlockEntities/MobHeadEntity.cpp @@ -1,7 +1,7 @@ // MobHeadEntity.cpp -// Implements the cMobHeadEntity class representing a single skull/head in the world +// Implements the cMobHeadEntity class representing a single skull / head in the world #include "Globals.h" #include "MobHeadEntity.h" diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h index 7f08c5ab2..b6760b40a 100644 --- a/src/BlockEntities/MobHeadEntity.h +++ b/src/BlockEntities/MobHeadEntity.h @@ -1,6 +1,6 @@ // MobHeadEntity.h -// Declares the cMobHeadEntity class representing a single skull/head in the world +// Declares the cMobHeadEntity class representing a single skull / head in the world diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index d3f85e9d2..f350ef4c4 100644 --- a/src/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h @@ -52,7 +52,7 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override {} - virtual void SetRedstonePower(bool a_Value) + virtual void SetRedstonePower(bool a_Value) override { if (a_Value) { diff --git a/src/BlockInServerPluginInterface.h b/src/BlockInServerPluginInterface.h index 70c9944a8..d4759ce83 100644 --- a/src/BlockInServerPluginInterface.h +++ b/src/BlockInServerPluginInterface.h @@ -1,4 +1,12 @@ +// BlockInServerPluginInterface.h + +// Defines the cBlockInServerPluginInterface class that implements the cBlockPluginInterface for blocks, using the plugin manager + + + + + #pragma once #include "Blocks/BlockPluginInterface.h" @@ -16,7 +24,7 @@ class cBlockInServerPluginInterface : public: cBlockInServerPluginInterface(cWorld & a_World) : m_World(a_World) {} - virtual bool CallHookBlockSpread(int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) + virtual bool CallHookBlockSpread(int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) override { return cPluginManager::Get()->CallHookBlockSpread(m_World, a_BlockX, a_BlockY, a_BlockZ, a_Source); } @@ -26,6 +34,16 @@ public: return cPluginManager::Get()->CallHookBlockToPickups(m_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups); } + virtual bool CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + return cPluginManager::Get()->CallHookPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); + } + + virtual bool CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + return cPluginManager::Get()->CallHookPlayerBrokenBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); + } + private: cWorld & m_World; }; diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index e56f4bfe0..dfa392d9b 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -14,7 +14,7 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ); - Vector3i Direction = MetaDataToDirection( OldMeta & 0x7); + Vector3i Direction = MetaDataToDirection( OldMeta & 0x3); if (OldMeta & 0x8) { // Was pillow @@ -111,7 +111,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface // Is foot end VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken - PillowDirection = MetaDataToDirection(Meta & 0x7); + PillowDirection = MetaDataToDirection(Meta & 0x3); if (a_ChunkInterface.GetBlock(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z) == E_BLOCK_BED) // Must always use pillow location for sleeping { a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z); diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h index 3443fc69e..ed73a162e 100644 --- a/src/Blocks/BlockComparator.h +++ b/src/Blocks/BlockComparator.h @@ -22,7 +22,7 @@ public: virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR + Meta ^= 0x04; // Toggle 3rd (addition / subtraction) bit with XOR a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); } diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index d2bf180be..a4e375cf0 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -50,10 +50,24 @@ void cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac UNUSED(a_CursorY); UNUSED(a_CursorZ); - if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) + switch (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ)) { - ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); - a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); + default: + { + ASSERT(!"Unhandled door block type"); + } + case E_BLOCK_ACACIA_DOOR: + case E_BLOCK_BIRCH_DOOR: + case E_BLOCK_DARK_OAK_DOOR: + case E_BLOCK_JUNGLE_DOOR: + case E_BLOCK_SPRUCE_DOOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_WOODEN_DOOR: + { + ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); + break; + } } } @@ -134,7 +148,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta) return a_Meta; } - // Holds open/closed meta data. 0x0C == 1100. + // Holds open / closed meta data. 0x0C == 1100. NIBBLETYPE OtherMeta = a_Meta & 0x0C; // Mirrors according to a table. 0x03 == 0011. @@ -152,7 +166,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta) NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta) { - // Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data + // Top bit (0x08) contains door panel type (Top / Bottom panel) Only Bottom panels contain position data // Return a_Meta if panel is a top panel (0x08 bit is set to 1) // Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored @@ -165,7 +179,7 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta) return a_Meta; } - // Holds open/closed meta data. 0x0C == 1100. + // Holds open / closed meta data. 0x0C == 1100. NIBBLETYPE OtherMeta = a_Meta & 0x0C; // Mirrors according to a table. 0x03 == 0011. diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 53f84b553..445383e7c 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -6,7 +6,7 @@ #include "Chunk.h" #include "MetaRotator.h" #include "ChunkInterface.h" - +#include "BlockSlab.h" @@ -109,16 +109,31 @@ public: virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { - return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))); + return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ), a_Chunk.GetMeta(a_RelX, a_RelY - 1, a_RelZ))); } /** Returns true if door can be placed on the specified block type. */ - static bool CanBeOn(BLOCKTYPE a_BlockType) + static bool CanBeOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - // Vanilla refuses to place doors on transparent blocks + // Vanilla refuses to place doors on transparent blocks, except top-half slabs and other doors // We need to keep the door compatible with itself, otherwise the top half drops while the bottom half stays - return !cBlockInfo::IsTransparent(a_BlockType) || IsDoorBlockType(a_BlockType); + + // Doors can be placed on upside-down slabs + if (cBlockSlabHandler::IsAnySlabType(a_BlockType) && ((a_BlockMeta & 0x08) != 0)) + { + return true; + } + // Doors can also be placed on other doors + else if (IsDoorBlockType(a_BlockType)) + { + return true; + } + // Doors can not be placed on transparent blocks, but on any other block + else + { + return !cBlockInfo::IsTransparent(a_BlockType); + } } diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h index bafd385ab..2038df94b 100644 --- a/src/Blocks/BlockFire.h +++ b/src/Blocks/BlockFire.h @@ -106,7 +106,7 @@ public: void FindAndSetPortalFrame(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface) { int MaxY = FindObsidianCeiling(X, Y, Z, a_ChunkInterface); // Get topmost obsidian block as reference for all other checks - int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above + int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add / subtract one as we've checked the original already the line above if (MaxY == 0) // Oh noes! Not a portal coordinate :( { @@ -139,7 +139,7 @@ public: return; } - /** Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable + /** Evaluates if coordinates are a portal going XP / XM; returns true if so, and writes boundaries to variable Takes coordinates of base block and Y coord of target obsidian ceiling */ bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cChunkInterface & a_ChunkInterface) { @@ -179,7 +179,7 @@ public: return (FoundFrameXP && FoundFrameXM); } - /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable + /// Evaluates if coords are a portal going ZP / ZM; returns true if so, and writes boundaries to variable bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cChunkInterface & a_ChunkInterface) { Dir = 2; diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 2de4a3e4c..452cc94a5 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -198,6 +198,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType); case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); + case E_BLOCK_CLAY: return new cBlockOreHandler (a_BlockType); case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_COCOA_POD: return new cBlockCocoaPodHandler (a_BlockType); case E_BLOCK_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType); @@ -353,7 +354,7 @@ bool cBlockHandler::GetPlacementBlockTypeMeta( { // By default, all blocks can be placed and the meta is copied over from the item's damage value: a_BlockType = m_BlockType; - a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); + a_BlockMeta = static_cast<NIBBLETYPE>(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); return true; } diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h index bd9a7414e..4d4610fd8 100644 --- a/src/Blocks/BlockLeaves.h +++ b/src/Blocks/BlockLeaves.h @@ -40,29 +40,41 @@ public: { cFastRandom rand; - // Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand - if (rand.NextInt(6) == 0) + // There is a chance to drop a sapling that varies depending on the type of leaf broken. + // TODO: Take into account fortune for sapling drops. + int chance; + if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE)) + { + // Jungle leaves have a 2.5% chance of dropping a sapling. + chance = rand.NextInt(40); + } + else + { + // Other leaves have a 5% chance of dropping a sapling. + chance = rand.NextInt(20); + } + if (chance == 0) { a_Pickups.push_back( cItem( E_BLOCK_SAPLING, 1, - (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (2 << (a_BlockMeta & 0x01)) + (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (4 + (a_BlockMeta & 0x01)) ) ); } - - // 1 % chance of dropping an apple, if the leaves' type is Apple Leaves + + // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE)) { - if (rand.NextInt(101) == 0) + if (rand.NextInt(200) == 0) { a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); } } } - - + + virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h index f5bedea6c..2da138e5f 100644 --- a/src/Blocks/BlockLever.h +++ b/src/Blocks/BlockLever.h @@ -19,7 +19,7 @@ public: virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override { - // Flip the ON bit on/off using the XOR bitwise operation + // Flip the ON bit on / off using the XOR bitwise operation NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h index 08d79f435..7e67e3450 100644 --- a/src/Blocks/BlockOre.h +++ b/src/Blocks/BlockOre.h @@ -50,6 +50,16 @@ public: a_Pickups.push_back(cItem(E_ITEM_COAL)); break; } + case E_BLOCK_NETHER_QUARTZ_ORE: + { + a_Pickups.push_back(cItem(E_ITEM_NETHER_QUARTZ)); + break; + } + case E_BLOCK_CLAY: + { + a_Pickups.push_back(cItem(E_ITEM_CLAY, 4)); + break; + } default: { a_Pickups.push_back(cItem(m_BlockType)); diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h index 9a891025a..5933d4885 100644 --- a/src/Blocks/BlockPiston.h +++ b/src/Blocks/BlockPiston.h @@ -108,7 +108,7 @@ private: case E_BLOCK_ENCHANTMENT_TABLE: case E_BLOCK_END_PORTAL: case E_BLOCK_END_PORTAL_FRAME: - // Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed/pulled in MCS :) + // Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed / pulled in MCS :) case E_BLOCK_FURNACE: case E_BLOCK_LIT_FURNACE: case E_BLOCK_INVERTED_DAYLIGHT_SENSOR: diff --git a/src/Blocks/BlockPluginInterface.h b/src/Blocks/BlockPluginInterface.h index b769bcf3e..6d49a248d 100644 --- a/src/Blocks/BlockPluginInterface.h +++ b/src/Blocks/BlockPluginInterface.h @@ -1,10 +1,25 @@ +// BlockPluginInterface.h + +// Declares the cBlockPluginInterface class representing an interface that the blockhandlers and itemhandlers use for calling plugins + + + + + #pragma once +// fwd: +class cPlayer; + + + + + /** This interface is used to decouple block handlers from the cPluginManager dependency through cWorld. The block handlers call this interface, which is then implemented by the specific classes that the caller provides. @@ -16,6 +31,8 @@ public: virtual bool CallHookBlockSpread(int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) = 0; virtual bool CallHookBlockToPickups(cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0; + virtual bool CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; }; diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h index 02fe3aa95..a2e27a351 100644 --- a/src/Blocks/BlockRail.h +++ b/src/Blocks/BlockRail.h @@ -502,11 +502,11 @@ public: // Save powered rail flag. NIBBLETYPE OtherMeta = a_Meta & 0x08; // Rotates according to table; 0x07 == 0111. - // Rails can either be flat (North/South) or Ascending (Asc. East) + // Rails can either be flat (North / South) or Ascending (Asc. East) switch (a_Meta & 0x07) { - case 0x00: return 0x01 + OtherMeta; // North/South -> East/West - case 0x01: return 0x00 + OtherMeta; // East/West -> North/South + case 0x00: return 0x01 + OtherMeta; // North / South -> East / West + case 0x01: return 0x00 + OtherMeta; // East / West -> North / South case 0x02: return 0x04 + OtherMeta; // Asc. East -> Asc. North case 0x04: return 0x03 + OtherMeta; // Asc. North -> Asc. West @@ -538,11 +538,11 @@ public: // Save powered rail flag. NIBBLETYPE OtherMeta = a_Meta & 0x08; // Rotates according to table; 0x07 == 0111. - // Rails can either be flat (North/South) or Ascending (Asc. East) + // Rails can either be flat (North / South) or Ascending (Asc. East) switch (a_Meta & 0x07) { - case 0x00: return 0x01 + OtherMeta; // North/South -> East/West - case 0x01: return 0x00 + OtherMeta; // East/West -> North/South + case 0x00: return 0x01 + OtherMeta; // North / South -> East / West + case 0x01: return 0x00 + OtherMeta; // East / West -> North / South case 0x02: return 0x05 + OtherMeta; // Asc. East -> Asc. South case 0x05: return 0x03 + OtherMeta; // Asc. South -> Asc. West @@ -574,7 +574,7 @@ public: // Save powered rail flag. NIBBLETYPE OtherMeta = a_Meta & 0x08; // Mirrors according to table; 0x07 == 0111. - // Rails can either be flat (North/South) or Ascending (Asc. East) + // Rails can either be flat (North / South) or Ascending (Asc. East) switch (a_Meta & 0x07) { case 0x05: return 0x04 + OtherMeta; // Asc. South -> Asc. North @@ -605,7 +605,7 @@ public: // Save powered rail flag. NIBBLETYPE OtherMeta = a_Meta & 0x08; // Mirrors according to table; 0x07 == 0111. - // Rails can either be flat (North/South) or Ascending (Asc. East) + // Rails can either be flat (North / South) or Ascending (Asc. East) switch (a_Meta & 0x07) { case 0x02: return 0x03 + OtherMeta; // Asc. East -> Asc. West diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h index d63df94cf..3abc572f3 100644 --- a/src/Blocks/BlockTorch.h +++ b/src/Blocks/BlockTorch.h @@ -103,6 +103,11 @@ public: case E_BLOCK_STAINED_GLASS: case E_BLOCK_FENCE: case E_BLOCK_NETHER_BRICK_FENCE: + case E_BLOCK_SPRUCE_FENCE: + case E_BLOCK_BIRCH_FENCE: + case E_BLOCK_JUNGLE_FENCE: + case E_BLOCK_DARK_OAK_FENCE: + case E_BLOCK_ACACIA_FENCE: case E_BLOCK_COBBLESTONE_WALL: { // Torches can only be placed on top of these blocks diff --git a/src/Blocks/BlockTrapdoor.h b/src/Blocks/BlockTrapdoor.h index 8c96de0f1..dbb0b5a5b 100644 --- a/src/Blocks/BlockTrapdoor.h +++ b/src/Blocks/BlockTrapdoor.h @@ -35,7 +35,7 @@ public: return; } - // Flip the ON bit on/off using the XOR bitwise operation + // Flip the ON bit on / off using the XOR bitwise operation NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h index 00d7a69b8..3d06a8223 100644 --- a/src/Blocks/BlockVine.h +++ b/src/Blocks/BlockVine.h @@ -177,7 +177,7 @@ public: } - virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) + virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override { UNUSED(a_ChunkInterface); UNUSED(a_WorldInterface); diff --git a/src/BoundingBox.h b/src/BoundingBox.h index 928e62afa..8ea758ac7 100644 --- a/src/BoundingBox.h +++ b/src/BoundingBox.h @@ -57,10 +57,10 @@ public: /// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max); - /// Returns true if the specified point is inside the bounding box specified by its min/max corners + /// Returns true if the specified point is inside the bounding box specified by its min / max corners static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point); - /// Returns true if the specified point is inside the bounding box specified by its min/max corners + /// Returns true if the specified point is inside the bounding box specified by its min / max corners static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z); /** Returns true if this bounding box is intersected by the line specified by its two points diff --git a/src/Broadcaster.cpp b/src/Broadcaster.cpp new file mode 100644 index 000000000..594d12208 --- /dev/null +++ b/src/Broadcaster.cpp @@ -0,0 +1,47 @@ + +#include "Globals.h" +#include "Broadcaster.h" +#include "World.h" +#include "Chunk.h" + +cBroadcaster::cBroadcaster(cWorld * a_World) : + m_World(a_World) +{ +} + + +void cBroadcaster::BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude) +{ + m_World->DoWithChunkAt(a_Src, + [=](cChunk & a_Chunk) -> bool + { + for (auto && client : a_Chunk.GetAllClients()) + { + if (client == a_Exclude) + { + continue; + } + client->SendParticleEffect(a_ParticleName, a_Src.x, a_Src.y, a_Src.z, a_Offset.x, a_Offset.y, a_Offset.z, a_ParticleData, a_ParticleAmount); + }; + return true; + }); +} + + +void cBroadcaster::BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data, cClientHandle * a_Exclude) +{ + m_World->DoWithChunkAt(a_Src, + [=](cChunk & a_Chunk) -> bool + { + for (auto && client : a_Chunk.GetAllClients()) + { + if (client == a_Exclude) + { + continue; + } + client->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data); + }; + return true; + }); +} + diff --git a/src/Broadcaster.h b/src/Broadcaster.h new file mode 100644 index 000000000..27d35fe4d --- /dev/null +++ b/src/Broadcaster.h @@ -0,0 +1,20 @@ + +class cWorld; + +#include <array> + +class cBroadcaster +{ + +public: + + cBroadcaster(cWorld * a_World); + + void BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr); + + void BroadcastParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data, cClientHandle * a_Exclude = nullptr); + +private: + cWorld * m_World; + +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b91c4f65a..1b721100d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ SET (SRCS BlockArea.cpp BlockID.cpp BlockInfo.cpp + Broadcaster.cpp BoundingBox.cpp ByteBuffer.cpp ChatColor.cpp @@ -47,11 +48,13 @@ SET (SRCS Logger.cpp Map.cpp MapManager.cpp + MemorySettingsRepository.cpp MobCensus.cpp MobFamilyCollecter.cpp MobProximityCounter.cpp MobSpawner.cpp MonsterConfig.cpp + OverridesSettingsRepository.cpp ProbabDistrib.cpp RankManager.cpp RCONServer.cpp @@ -77,6 +80,7 @@ SET (HDRS BlockInServerPluginInterface.h BlockInfo.h BlockTracer.h + Broadcaster.h BoundingBox.h BuildInfo.h.cmake ByteBuffer.h @@ -114,11 +118,13 @@ SET (HDRS Map.h MapManager.h Matrix4.h + MemorySettingsRepository.h MobCensus.h MobFamilyCollecter.h MobProximityCounter.h MobSpawner.h MonsterConfig.h + OverridesSettingsRepository.h ProbabDistrib.h RankManager.h RCONServer.h @@ -126,6 +132,7 @@ SET (HDRS Scoreboard.h Server.h SetChunkData.h + SettingsRepositoryInterface.h Statistics.h StringCompression.h StringUtils.h @@ -140,6 +147,7 @@ SET (HDRS include_directories(".") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/SQLiteCpp/include") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/TCLAP/include") configure_file("BuildInfo.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/BuildInfo.h") @@ -276,7 +284,7 @@ if (MSVC) COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/MCServer/lua51.dll ./lua51.dll # Regenerate bindings: - COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg + COMMAND tolua -L BindingsProcessor.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/ # add any new generation dependencies here @@ -324,4 +332,9 @@ endif () if (WIN32) target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) endif() -target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp event_core event_extra) + +if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + add_flags_lnk(-L/usr/local/lib) +endif() + +target_link_libraries(${EXECUTABLE} luaexpat jsoncpp mbedtls zlib sqlite lua SQLiteCpp event_core event_extra) diff --git a/src/CheckBasicStyle.lua b/src/CheckBasicStyle.lua index 19156b537..fa20f18f7 100644..100755 --- a/src/CheckBasicStyle.lua +++ b/src/CheckBasicStyle.lua @@ -43,6 +43,7 @@ local g_IgnoredFiles = { "Bindings/Bindings.h", "Bindings/Bindings.cpp", + "Bindings/LuaState_Implementation.cpp", "LeakFinder.cpp", "LeakFinder.h", "MersenneTwister.h", @@ -149,6 +150,39 @@ local g_ViolationPatterns = -- No space before a closing parenthesis: {" %)", "Remove the space before \")\""}, + + -- Check spaces around "+": + {"^[a-zA-Z0-9]+%+[a-zA-Z0-9]+", "Add space around +"}, + {"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%+[a-zA-Z0-9]+", "Add space around +"}, + --[[ + -- Cannot check these because of text such as "X+" and "+2" appearing in some comments. + {"^[a-zA-Z0-9]+ %+[a-zA-Z0-9]+", "Add space after +"}, + {"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+ %+[a-zA-Z0-9]+", "Add space after +"}, + {"^[a-zA-Z0-9]+%+ [a-zA-Z0-9]+", "Add space before +"}, + {"[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%+ [a-zA-Z0-9]+", "Add space before +"}, + --]] + + -- Cannot check spaces around "-", because the minus is sometimes used as a hyphen between-words + + -- Check spaces around "*": + {"^[a-zA-Z0-9]+%*[a-zA-Z0-9]+", "Add space around *"}, + {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%*[a-zA-Z0-9]+", "Add space around *"}, + {"^[a-zB-Z0-9]+%* [a-zA-Z0-9]+", "Add space before *"}, + {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zB-Z0-9]+%* [a-zA-Z0-9]+", "Add space before *"}, + + -- Check spaces around "/": + {"^[a-zA-Z0-9]+%/[a-zA-Z0-9]+", "Add space around /"}, + {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%/[a-zA-Z0-9]+", "Add space around /"}, + + -- Check spaces around "&": + {"^[a-zA-Z0-9]+%&[a-zA-Z0-9]+", "Add space around /"}, + {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%&[a-zA-Z0-9]+", "Add space around /"}, + {"^[a-zA-Z0-9]+%& [a-zA-Z0-9]+", "Add space before &"}, + {"^[^\"]*[!@#$%%%^&*() %[%]\t][a-zA-Z0-9]+%& [a-zA-Z0-9]+", "Add space before &"}, + + -- We don't like "Type const *" and "Type const &". Use "const Type *" and "const Type &" instead: + {"const %&", "Use 'const Type &' instead of 'Type const &'"}, + {"const %*", "Use 'const Type *' instead of 'Type const *'"}, } @@ -266,14 +300,113 @@ end +--- Array of files to process. Filled from cmdline arguments +local ToProcess = {} + + + + + +--- Handlers for the command-line arguments +-- Maps flag => function +local CmdLineHandlers = +{ + -- "-f file" checks the specified file + ["-f"] = function (a_Args, a_Idx) + local fnam = a_Args[a_Idx + 1] + if not(fnam) then + error("Invalid flag: '-f' needs a filename following it.") + end + table.insert(ToProcess, fnam) + return a_Idx + 2 -- skip the filename in param parsing + end, + + -- "-g" checks files reported by git as being committed. + ["-g"] = function (a_Args, a_Idx) + local f = io.popen("git diff --cached --name-only --diff-filter=ACMR") + for fnam in f:lines() do + table.insert(ToProcess, fnam) + end + end, + + -- "-h" prints help and exits + ["-h"] = function (a_Args, a_Idx) + print([[ +Usage:") +"CheckBasicStyle [<options>] + +Available options: +-f <filename> - checks the specified filename +-g - checks files reported by Git as being committed +-h - prints this help and exits +-l <listfile> - checks all files listed in the specified listfile +-- - reads the list of files to check from stdin + +When no options are given, the script checks all files listed in the AllFiles.lst file. + +Only .cpp and .h files are ever checked. +]]) + os.exit(0) + end, + + -- "-l listfile" loads the list of files to check from the specified listfile + ["-l"] = function (a_Args, a_Idx) + local listFile = a_Args[a_Idx + 1] + if not(listFile) then + error("Invalid flag: '-l' needs a filename following it.") + end + for fnam in io.lines(listFile) do + table.insert(ToProcess, fnam) + end + return a_Idx + 2 -- Skip the listfile in param parsing + end, + + -- "--" reads the list of files from stdin + ["--"] = function (a_Args, a_Idx) + for fnam in io.lines() do + table.insert(ToProcess, fnam) + end + end, +} + + + + + -- Remove buffering from stdout, so that the output appears immediately in IDEs: io.stdout:setvbuf("no") --- Process all files in the AllFiles.lst file (generated by cmake): -for fnam in io.lines("AllFiles.lst") do +-- Parse the cmdline arguments to see what files to check: +local idx = 1 +while (arg[idx]) do + local handler = CmdLineHandlers[arg[idx]] + if not(handler) then + error("Unknown command-line argument #" .. idx .. ": " .. arg[idx]) + end + idx = handler(arg, idx) or (idx + 1) -- Call the handler, let it change the next index if it wants +end + + +-- By default process all files in the AllFiles.lst file (generated by cmake): +if not(arg[1]) then + for fnam in io.lines("AllFiles.lst") do + table.insert(ToProcess, fnam) + end +end + + + + + +-- Process the files in the list: +for _, fnam in ipairs(ToProcess) do ProcessItem(fnam) end + + + + -- Report final verdict: print("Number of violations found: " .. g_NumViolations) if (g_NumViolations > 0) then diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 95dc7708e..7d5f54373 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -472,26 +472,26 @@ void cChunk::Stay(bool a_Stay) -void cChunk::CollectMobCensus(cMobCensus& toFill) +void cChunk::CollectMobCensus(cMobCensus & toFill) { toFill.CollectSpawnableChunk(*this); - std::list<const Vector3d*> playerPositions; - cPlayer* currentPlayer; - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + std::list<const Vector3d *> playerPositions; + cPlayer * currentPlayer; + for (auto itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) { currentPlayer = (*itr)->GetPlayer(); playerPositions.push_back(&(currentPlayer->GetPosition())); } Vector3d currentPosition; - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + for (auto itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) { // LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); if ((*itr)->IsMob()) { - cMonster& Monster = (cMonster&)(**itr); + auto & Monster = reinterpret_cast<cMonster &>(**itr); currentPosition = Monster.GetPosition(); - for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); ++itr2) + for (auto itr2 = playerPositions.cbegin(); itr2 != playerPositions.cend(); ++itr2) { toFill.CollectMob(Monster, *this, (currentPosition - **itr2).SqrLength()); } @@ -531,7 +531,7 @@ void cChunk::GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z) -void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) +void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner) { int CenterX, CenterY, CenterZ; GetRandomBlockCoords(CenterX, CenterY, CenterZ); @@ -737,7 +737,7 @@ void cChunk::ProcessQueuedSetBlocks(void) { if (GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == itr->m_PreviousType) { - // Current world age is bigger than/equal to target world age - delay time reached AND + // Current world age is bigger than / equal to target world age - delay time reached AND // Previous block type was the same as current block type (to prevent duplication) SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); // SetMeta doesn't send to client itr = m_SetBlockQueue.erase(itr); @@ -751,7 +751,7 @@ void cChunk::ProcessQueuedSetBlocks(void) } else { - // Current world age is bigger than/equal to target world age - delay time reached + // Current world age is bigger than / equal to target world age - delay time reached SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); itr = m_SetBlockQueue.erase(itr); LOGD("Successfully set queued block - previous type ignored"); @@ -1026,7 +1026,7 @@ void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl case E_BLOCK_FARMLAND: { // DEBUG: This is here to catch FS #349 - melons growing over other crops. - LOG("Growing melon/pumpkin overwriting %s, growing on %s", + LOG("Growing melon / pumpkin overwriting %s, growing on %s", ItemTypeToString(BlockType[CheckType]).c_str(), ItemTypeToString(Soil).c_str() ); @@ -1827,7 +1827,7 @@ bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_ ) { MarkDirty(); - (reinterpret_cast<cSignEntity *>(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); + reinterpret_cast<cSignEntity *>(*itr)->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); return true; } @@ -1839,7 +1839,7 @@ bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_ -void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity) +void cChunk::RemoveBlockEntity(cBlockEntity * a_BlockEntity) { MarkDirty(); m_BlockEntities.remove(a_BlockEntity); diff --git a/src/Chunk.h b/src/Chunk.h index e8c60a74b..f57769107 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -155,10 +155,10 @@ public: void Stay(bool a_Stay = true); /** Recence all mobs proximities to players in order to know what to do with them */ - void CollectMobCensus(cMobCensus& toFill); + void CollectMobCensus(cMobCensus & toFill); /** Try to Spawn Monsters inside chunk */ - void SpawnMobs(cMobSpawner& a_MobSpawner); + void SpawnMobs(cMobSpawner & a_MobSpawner); void Tick(std::chrono::milliseconds a_Dt); @@ -439,6 +439,9 @@ public: as at least one requests is active the chunk will be ticked). */ void SetAlwaysTicked(bool a_AlwaysTicked); + // Makes a copy of the list + cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } + private: friend class cChunkMap; @@ -530,9 +533,6 @@ private: /** Wakes up each simulator for its specific blocks; through all the blocks in the chunk */ void WakeUpSimulators(void); - - // Makes a copy of the list - cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } /** Sends m_PendingSendBlocks to all clients */ void BroadcastPendingBlockChanges(void); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 87da86131..44acc2013 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -65,7 +65,7 @@ cChunkMap::~cChunkMap() -void cChunkMap::RemoveLayer( cChunkLayer* a_Layer) +void cChunkMap::RemoveLayer(cChunkLayer * a_Layer) { cCSLock Lock(m_CSLayers); m_Layers.remove(a_Layer); @@ -791,6 +791,28 @@ bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callb } +bool cChunkMap::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback) +{ + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockPos.x, a_BlockPos.z, ChunkX, ChunkZ); + struct cCallBackWrapper : cChunkCallback + { + cCallBackWrapper(std::function<bool(cChunk &)> a_InnerCallback) : + m_Callback(a_InnerCallback) + { + } + + virtual bool Item(cChunk * a_Chunk) + { + return m_Callback(*a_Chunk); + } + + private: + std::function<bool(cChunk &)> m_Callback; + } callback(a_Callback); + return DoWithChunk(ChunkX, ChunkZ, callback); +} + @@ -1880,10 +1902,12 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_ if (ShouldDestroyBlocks) { cBlockArea area; - a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt); - - area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt)); + if (!area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt))) + { + return; + } + for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++) { for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++) @@ -2413,6 +2437,7 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * // Try loading the chunk: if ((Chunk == nullptr) || (!Chunk->IsValid())) { + Chunk->SetPresence(cChunk::cpQueued); class cPrepareLoadCallback: public cChunkCoordCallback { public: @@ -2427,6 +2452,7 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * virtual void Call(int a_CBChunkX, int a_CBChunkZ) override { // The chunk has been loaded or an error occurred, check if it's valid now: + cCSLock Lock(m_ChunkMap.m_CSLayers); cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ); if (CBChunk == nullptr) @@ -2690,12 +2716,12 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) -void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) +void cChunkMap::CollectMobCensus(cMobCensus & a_ToFill) { cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + for (auto && layer: m_Layers) { - (*itr)->CollectMobCensus(a_ToFill); + layer->CollectMobCensus(a_ToFill); } // for itr - m_Layers } @@ -2704,12 +2730,12 @@ void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) -void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) +void cChunkMap::SpawnMobs(cMobSpawner & a_MobSpawner) { cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + for (auto && layer: m_Layers) { - (*itr)->SpawnMobs(a_MobSpawner); + layer->SpawnMobs(a_MobSpawner); } // for itr - m_Layers } @@ -2910,7 +2936,7 @@ cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) -void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) +void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus & a_ToFill) { for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) { @@ -2929,7 +2955,7 @@ void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) -void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) +void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner & a_MobSpawner) { for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) { diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 0fac79c84..35f66f339 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -64,7 +64,7 @@ public: static const int LAYER_SIZE = 32; - cChunkMap(cWorld* a_World); + cChunkMap(cWorld * a_World); ~cChunkMap(); // Broadcast respective packets to all clients of the chunk where the event is taking place @@ -104,6 +104,9 @@ public: /** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */ bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); + /** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/ + bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback); + /** Wakes up simulators for the specified block */ void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); @@ -361,11 +364,11 @@ public: /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */ void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player */ - void CollectMobCensus(cMobCensus& a_ToFill); + /** Make a Mob census, of all mobs, their family, their chunk and their distance to closest player */ + void CollectMobCensus(cMobCensus & a_ToFill); /** Try to Spawn Monsters inside all Chunks */ - void SpawnMobs(cMobSpawner& a_MobSpawner); + void SpawnMobs(cMobSpawner & a_MobSpawner); void Tick(std::chrono::milliseconds a_Dt); @@ -430,9 +433,10 @@ private: void UnloadUnusedChunks(void); /** Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player */ - void CollectMobCensus(cMobCensus& a_ToFill); + void CollectMobCensus(cMobCensus & a_ToFill); + /** Try to Spawn Monsters inside all Chunks */ - void SpawnMobs(cMobSpawner& a_MobSpawner); + void SpawnMobs(cMobSpawner & a_MobSpawner); void Tick(std::chrono::milliseconds a_Dt); diff --git a/src/ChunkSender.h b/src/ChunkSender.h index 7cd7ddd86..8b187c5f9 100644 --- a/src/ChunkSender.h +++ b/src/ChunkSender.h @@ -13,7 +13,7 @@ And once they do, it requests the chunk data and sends it all away, either sends to a specific client (QueueSendChunkTo) Chunk data is queried using the cChunkDataCallback interface. It is cached inside the ChunkSender object during the query and then processed after the query ends. -Note that the data needs to be compressed only *after* the query finishes, +Note that the data needs to be compressed only after the query finishes, because the query callbacks run with ChunkMap's CS locked. A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient(); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 2ce212421..13972b0f5 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -23,6 +23,7 @@ #include "Blocks/BlockSlab.h" #include "Blocks/BlockBed.h" #include "Blocks/ChunkInterface.h" +#include "BlockInServerPluginInterface.h" #include "Root.h" @@ -435,7 +436,7 @@ bool cClientHandle::StreamNextChunk(void) { Vector3d Vector = Position + LookVector * cChunkDef::Width * Range; - // Get the chunk from the x/z coords. + // Get the chunk from the x / z coords. int RangeX, RangeZ = 0; cChunkDef::BlockToChunk(FloorC(Vector.x), FloorC(Vector.z), RangeX, RangeZ); @@ -453,7 +454,7 @@ bool cClientHandle::StreamNextChunk(void) continue; } - // If the chunk already loading/loaded -> skip + // If the chunk already loading / loaded -> skip if ( (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) || (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end()) @@ -491,7 +492,7 @@ bool cClientHandle::StreamNextChunk(void) { cChunkCoords Coords = *itr; - // If the chunk already loading/loaded -> skip + // If the chunk already loading / loaded -> skip if ( (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) || (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end()) @@ -1153,7 +1154,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc return; } - // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: + // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig / aim bug in the client: m_HasStartedDigging = true; m_LastDigBlockX = a_BlockX; m_LastDigBlockY = a_BlockY; @@ -1200,7 +1201,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo (m_LastDigBlockZ != a_BlockZ) ) { - LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)", + LOGD("Prevented a dig / aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)", a_BlockX, a_BlockY, a_BlockZ, m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, (m_HasStartedDigging ? "True" : "False") @@ -1340,7 +1341,14 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things + if (a_BlockY < cChunkDef::Height - 1) + { + World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things + } + if (a_BlockY > 1) + { + World->SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, m_Player); // 2 block high things + } m_Player->GetInventory().SendEquippedSlot(); } } @@ -1351,7 +1359,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e if (!CheckBlockInteractionsRate()) { - Kick("Too many blocks were placed/interacted with per unit time - hacked client?"); + Kick("Too many blocks were placed / interacted with per unit time - hacked client?"); return; } @@ -1432,7 +1440,8 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e // A plugin doesn't agree with using the item, abort return; } - ItemHandler->OnItemUse(World, m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + cBlockInServerPluginInterface PluginInterface(*World); + ItemHandler->OnItemUse(World, m_Player, PluginInterface, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } } @@ -1464,7 +1473,7 @@ void cClientHandle::HandleChat(const AString & a_Message) Color.clear(); } Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color); - Msg.ParseText(a_Message); + Msg.ParseText(Message); Msg.UnderlineUrls(); m_Player->GetWorld()->BroadcastChat(Msg); } @@ -2374,6 +2383,15 @@ void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_S +void cClientHandle::SendParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) +{ + m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data); +} + + + + + void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) { m_Protocol->SendPickupSpawn(a_Pickup); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index e004c3bfb..bcfa55825 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -22,6 +22,7 @@ #include "ChunkSender.h" +#include <array> @@ -178,6 +179,7 @@ public: // tolua_export void SendMapInfo (int a_ID, unsigned int a_Scale); void SendPaintingSpawn (const cPainting & a_Painting); void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount); + void SendParticleEffect (const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data); void SendPickupSpawn (const cPickup & a_Pickup); void SendPlayerAbilities (void); void SendPlayerListAddPlayer (const cPlayer & a_Player); @@ -396,7 +398,7 @@ private: cPlayer * m_Player; - bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction + bool m_HasSentDC; ///< True if a Disconnect packet has been sent in either direction // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk int m_LastStreamedChunkX; @@ -421,7 +423,7 @@ private: int m_BlockDigAnimY; int m_BlockDigAnimZ; - // To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet: + // To avoid dig / aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet: bool m_HasStartedDigging; int m_LastDigBlockX; int m_LastDigBlockY; diff --git a/src/CommandOutput.cpp b/src/CommandOutput.cpp index 510461d81..255ec3e9b 100644 --- a/src/CommandOutput.cpp +++ b/src/CommandOutput.cpp @@ -29,29 +29,32 @@ void cCommandOutputCallback::Out(const char * a_Fmt, ...) //////////////////////////////////////////////////////////////////////////////// -// cLogCommandOutputCallback: +// cStringAccumCommandOutputCallback: -void cLogCommandOutputCallback::Out(const AString & a_Text) +void cStringAccumCommandOutputCallback::Out(const AString & a_Text) { - m_Buffer.append(a_Text); + m_Accum.append(a_Text); } +//////////////////////////////////////////////////////////////////////////////// +// cLogCommandOutputCallback: + void cLogCommandOutputCallback::Finished(void) { // Log each line separately: - size_t len = m_Buffer.length(); + size_t len = m_Accum.length(); size_t last = 0; for (size_t i = 0; i < len; i++) { - switch (m_Buffer[i]) + switch (m_Accum[i]) { case '\n': { - LOG("%s", m_Buffer.substr(last, i - last).c_str()); + LOG("%s", m_Accum.substr(last, i - last).c_str()); last = i + 1; break; } @@ -59,11 +62,11 @@ void cLogCommandOutputCallback::Finished(void) } // for i - m_Buffer[] if (last < len) { - LOG("%s", m_Buffer.substr(last).c_str()); + LOG("%s", m_Accum.substr(last).c_str()); } // Clear the buffer for the next command output: - m_Buffer.clear(); + m_Accum.clear(); } diff --git a/src/CommandOutput.h b/src/CommandOutput.h index daa9430c0..6265b74ea 100644 --- a/src/CommandOutput.h +++ b/src/CommandOutput.h @@ -47,18 +47,36 @@ class cNullCommandOutputCallback : -/// Sends all command output to a log, line by line, when the command finishes processing -class cLogCommandOutputCallback : +/** Accumulates all command output into a string. */ +class cStringAccumCommandOutputCallback: public cCommandOutputCallback { + typedef cCommandOutputCallback super; + public: // cCommandOutputCallback overrides: virtual void Out(const AString & a_Text) override; - virtual void Finished(void) override; - + virtual void Finished(void) override {} + + /** Returns the accumulated command output in a string. */ + const AString & GetAccum(void) const { return m_Accum; } + protected: - /// Output is stored here until the command finishes processing - AString m_Buffer; + /** Output is stored here until the command finishes processing */ + AString m_Accum; +} ; + + + + + +/// Sends all command output to a log, line by line, when the command finishes processing +class cLogCommandOutputCallback : + public cStringAccumCommandOutputCallback +{ +public: + // cStringAccumCommandOutputCallback overrides: + virtual void Finished(void) override; } ; diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index 202fb900e..472044fa3 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -366,6 +366,7 @@ void cCraftingRecipes::ClearRecipes(void) void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine) { + // Remove any spaces within the line: AString RecipeLine(a_RecipeLine); RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end()); @@ -672,10 +673,10 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti if ( (itrS->x >= a_GridWidth) || (itrS->y >= a_GridHeight) || - (Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type? + (Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type? (Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items ( - (Item.m_ItemDamage > 0) && // should compare damage values? + (Item.m_ItemDamage >= 0) && // should compare damage values? (Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage) ) ) diff --git a/src/Defines.h b/src/Defines.h index c2f46c241..787eacab8 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -229,7 +229,7 @@ inline const char * ClickActionToString(eClickAction a_ClickAction) -/** Returns a blockface mirrored around the Y axis (doesn't change up/down). */ +/** Returns a blockface mirrored around the Y axis (doesn't change up / down). */ inline eBlockFace MirrorBlockFaceY(eBlockFace a_BlockFace) { switch (a_BlockFace) diff --git a/src/Enchantments.h b/src/Enchantments.h index e8e84d43c..9d3f342d4 100644 --- a/src/Enchantments.h +++ b/src/Enchantments.h @@ -14,7 +14,7 @@ -// fwd: WorldStorage/FastNBT.h +// fwd: "WorldStorage/FastNBT.h" class cFastNBTWriter; class cParsedNBT; @@ -138,10 +138,10 @@ public: bool operator !=(const cEnchantments & a_Other) const; /** Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") */ - friend void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName); + friend void EnchantmentSerializer::WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName); /** Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) */ - friend void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx); + friend void EnchantmentSerializer::ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx); protected: /** Maps enchantment ID -> enchantment level */ diff --git a/src/Endianness.h b/src/Endianness.h index ed9637fcc..a415903a5 100644 --- a/src/Endianness.h +++ b/src/Endianness.h @@ -21,7 +21,7 @@ inline UInt64 HostToNetwork8(const void * a_Value) -inline UInt32 HostToNetwork4(const void* a_Value) +inline UInt32 HostToNetwork4(const void * a_Value) { UInt32 buf; memcpy( &buf, a_Value, sizeof( buf)); diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 3c1fabb1b..32952100c 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -21,6 +21,8 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a { SetSpeed(a_Speed); SetMass(0.1); + SetGravity(-20.0f); + SetAirDrag(0.2f); SetYawFromSpeed(); SetPitchFromSpeed(); LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", @@ -48,6 +50,8 @@ cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : { m_PickupState = psInCreative; } + SetGravity(-20.0f); + SetAirDrag(0.01f); } diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 6177eb32f..4ad418be4 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -16,7 +16,9 @@ cBoat::cBoat(double a_X, double a_Y, double a_Z) : super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) { - SetMass(20.f); + SetMass(20.0f); + SetGravity(-16.0f); + SetAirDrag(0.05f); SetMaxHealth(6); SetHealth(6); } diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index c8df6b4b1..dca44488b 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -36,6 +36,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d m_bHasSentNoSpeed(true), m_bOnGround(false), m_Gravity(-9.81f), + m_AirDrag(0.02f), m_LastPos(a_X, a_Y, a_Z), m_IsInitialized(false), m_WorldTravellingFrom(nullptr), @@ -246,7 +247,6 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R if (a_Attacker != nullptr) { Heading = a_Attacker->GetLookVector() * (a_Attacker->IsSprinting() ? 16 : 11); - Heading.y = 1.6; } TDI.Knockback = Heading * a_KnockbackAmount; @@ -943,6 +943,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Normal gravity fallspeed = m_Gravity * DtSec.count(); + NextSpeed -= NextSpeed * (m_AirDrag * 20.0f) * DtSec.count(); } NextSpeed.y += static_cast<float>(fallspeed); } @@ -999,7 +1000,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) NextSpeed += m_WaterSpeed; - if (NextSpeed.SqrLength() > 0.f) + if (NextSpeed.SqrLength() > 0.0f) { cTracer Tracer(GetWorld()); // Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse @@ -1015,20 +1016,20 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // Block hit was within our projected path // Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1. // For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP - if (Tracer.HitNormal.x != 0.f) + if (Tracer.HitNormal.x != 0.0f) { - NextSpeed.x = 0.f; + NextSpeed.x = 0.0f; } - if (Tracer.HitNormal.y != 0.f) + if (Tracer.HitNormal.y != 0.0f) { - NextSpeed.y = 0.f; + NextSpeed.y = 0.0f; } - if (Tracer.HitNormal.z != 0.f) + if (Tracer.HitNormal.z != 0.0f) { - NextSpeed.z = 0.f; + NextSpeed.z = 0.0f; } - if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground + if (Tracer.HitNormal.y == 1.0f) // Hit BLOCK_FACE_YP, we are on the ground { m_bOnGround = true; } @@ -1308,7 +1309,8 @@ bool cEntity::DetectPortal() if (IsPlayer()) { - ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo + // Send a respawn packet before world is loaded / generated so the client isn't left in limbo + ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); } return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false); @@ -1401,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) return false; } + // Ask the plugins if the entity is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) + { + // A Plugin doesn't allow the entity to changing the world + return false; + } + // Remove all links to the old world SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal GetWorld()->BroadcastDestroyEntity(*this); // Queue add to new world a_World->AddEntity(this); + cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD SetWorld(a_World); + // Entity changed the world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld); + return true; } @@ -1687,8 +1700,8 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) { m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude); } - // Clients seem to store two positions, one for the velocity packet and one for the teleport/relmove packet - // The latter is only changed with a relmove/teleport, and m_LastPos stores this position + // Clients seem to store two positions, one for the velocity packet and one for the teleport / relmove packet + // The latter is only changed with a relmove / teleport, and m_LastPos stores this position m_LastPos = GetPosition(); } else @@ -1971,7 +1984,7 @@ void cEntity::SteerVehicle(float a_Forward, float a_Sideways) { return; } - if ((a_Forward != 0.f) || (a_Sideways != 0.f)) + if ((a_Forward != 0.0f) || (a_Sideways != 0.0f)) { m_AttachedTo->HandleSpeedFromAttachee(a_Forward, a_Sideways); } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 9bb1837f1..fecbb9bf5 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -270,6 +270,10 @@ public: float GetGravity(void) const { return m_Gravity; } void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } + + float GetAirDrag(void) const { return m_AirDrag; } + + void SetAirDrag(float a_AirDrag) { m_AirDrag = a_AirDrag; } /// Sets the rotation to match the speed vector (entity goes "face-forward") void SetYawFromSpeed(void); @@ -470,7 +474,7 @@ protected: static cCriticalSection m_CSCount; static UInt32 m_EntityCount; - /** Measured in meter/second (m/s) */ + /** Measured in meters / second (m / s) */ Vector3d m_Speed; /** The ID of the entity that is guaranteed to be unique within a single run of the server. @@ -490,7 +494,7 @@ protected: /** Stores whether head yaw has been set manually */ bool m_bDirtyHead; - /** Stores whether our yaw/pitch/roll (body orientation) has been set manually */ + /** Stores whether our yaw / pitch / roll (body orientation) has been set manually */ bool m_bDirtyOrientation; /** Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client @@ -504,6 +508,12 @@ protected: For realistic effects, this should be negative. For spaaaaaaace, this can be zero or even positive */ float m_Gravity; + /** Stores the air drag that is applied to the entity every tick, measured in speed ratio per tick + Acts as air friction and slows down flight + Will be interpolated if the server tick rate varies + Data: http://minecraft.gamepedia.com/Entity#Motion_of_entities */ + float m_AirDrag; + /** Last position sent to client via the Relative Move or Teleport packets (not Velocity) Only updated if cEntity::BroadcastMovementUpdate() is called! */ Vector3d m_LastPos; diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp index bae686b77..c8be414d4 100644 --- a/src/Entities/EntityEffect.cpp +++ b/src/Entities/EntityEffect.cpp @@ -102,11 +102,11 @@ int cEntityEffect::GetPotionEffectDuration(short a_ItemDamage) // If potion is level II, half the duration. If not, stays the same TierCoeff = (GetPotionEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1; - // If potion is extended, multiply duration by 8/3. If not, stays the same + // If potion is extended, multiply duration by 8 / 3. If not, stays the same // Extended potion if sixth lowest bit is set ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1; - // If potion is splash potion, multiply duration by 3/4. If not, stays the same + // If potion is splash potion, multiply duration by 3 / 4. If not, stays the same SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75; // Ref.: diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp index 7301a3c9d..4a165909a 100644 --- a/src/Entities/FallingBlock.cpp +++ b/src/Entities/FallingBlock.cpp @@ -16,6 +16,8 @@ cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_Block m_BlockMeta(a_BlockMeta), m_OriginalPosition(a_BlockPosition) { + SetGravity(-16.0f); + SetAirDrag(0.02f); } diff --git a/src/Entities/FireChargeEntity.cpp b/src/Entities/FireChargeEntity.cpp index aba32602f..f6c665156 100644 --- a/src/Entities/FireChargeEntity.cpp +++ b/src/Entities/FireChargeEntity.cpp @@ -12,6 +12,7 @@ cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y { SetSpeed(a_Speed); SetGravity(0); + SetAirDrag(0); } diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp index 32eaf669a..89f69f113 100644 --- a/src/Entities/FireworkEntity.cpp +++ b/src/Entities/FireworkEntity.cpp @@ -13,6 +13,8 @@ cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, do m_TicksToExplosion(a_Item.m_FireworkItem.m_FlightTimeInTicks), m_FireworkItem(a_Item) { + SetGravity(0); + SetAirDrag(0); } diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index cf8dd6c6f..0c868270d 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -6,7 +6,7 @@ #include "Floater.h" #include "Player.h" #include "../ClientHandle.h" - +#include "Broadcaster.h" @@ -145,12 +145,12 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { LOGD("Started producing particles for floater %i", GetUniqueID()); m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8))); - m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); + m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15); } else if (m_CountDownTime < 20) { m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6); - m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15); + m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15); } m_CountDownTime--; diff --git a/src/Entities/GhastFireballEntity.cpp b/src/Entities/GhastFireballEntity.cpp index 9e4cb387e..c64fb2a17 100644 --- a/src/Entities/GhastFireballEntity.cpp +++ b/src/Entities/GhastFireballEntity.cpp @@ -12,6 +12,7 @@ cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, doub { SetSpeed(a_Speed); SetGravity(0); + SetAirDrag(0); } diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h index 507502ac6..9d783006c 100644 --- a/src/Entities/HangingEntity.h +++ b/src/Entities/HangingEntity.h @@ -27,7 +27,10 @@ public: eBlockFace GetFacing() const { return cHangingEntity::ProtocolFaceToBlockFace(m_Facing); } /** Set the direction in which the entity is facing. */ - void SetFacing(eBlockFace a_Facing) { m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); } + void SetFacing(eBlockFace a_Facing) + { + m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); + } // tolua_end @@ -37,7 +40,7 @@ public: /** Set the direction in which the entity is facing. */ void SetProtocolFacing(Byte a_Facing) { - ASSERT((a_Facing <= 3) && (a_Facing >= 0)); + ASSERT(a_Facing <= 3); m_Facing = a_Facing; } diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index ee10cf6b3..b80759d24 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -92,7 +92,9 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : m_DetectorRailPosition(0, 0, 0), m_bIsOnDetectorRail(false) { - SetMass(20.f); + SetMass(20.0f); + SetGravity(-16.0f); + SetAirDrag(0.05f); SetMaxHealth(6); SetHealth(6); SetWidth(1); @@ -903,7 +905,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta) } /* Check to which side the minecart is to be pushed. - Let's consider a z-x-coordinate system where the minecart is the center (0/0). + Let's consider a z-x-coordinate system where the minecart is the center (0, 0). The minecart moves along the line x = -z, the perpendicular line to this is x = z. In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */ if ( @@ -952,7 +954,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta) } /* Check to which side the minecart is to be pushed. - Let's consider a z-x-coordinate system where the minecart is the center (0/0). + Let's consider a z-x-coordinate system where the minecart is the center (0, 0). The minecart moves along the line x = z, the perpendicular line to this is x = -z. In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */ if ( diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index 898776e71..05eaf16e9 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -29,7 +29,7 @@ public: enum ePayload { mpNone = 0, // Empty minecart, ridable by player or mobs - mpChest = 1, // Minecart-with-chest, can store a grid of 3*8 items + mpChest = 1, // Minecart-with-chest, can store a grid of 3 * 8 items mpFurnace = 2, // Minecart-with-furnace, can be powered mpTNT = 3, // Minecart-with-TNT, can be blown up with activator rail mpHopper = 5, // Minecart-with-hopper, can be hopper @@ -54,8 +54,7 @@ protected: cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); /** Handles physics on normal rails - For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails - */ + For each tick, slow down on flat rails, speed up or slow down on ascending / descending rails (depending on direction), and turn on curved rails. */ void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Handles powered rail physics @@ -136,7 +135,7 @@ protected: virtual void Destroyed() override; // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) override { UNUSED(a_SlotNum); ASSERT(a_Grid == &m_Contents); diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index baf8a2f3b..fcb686e28 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -13,6 +13,8 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) : super(a_EntityType, 0, 0, 0, a_Width, a_Height) , m_EntityEffects(tEffectMap()) { + SetGravity(-32.0f); + SetAirDrag(0.02f); } diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index 9f2609894..f2f76dbf9 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -91,7 +91,8 @@ cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_It , m_bCollected(false) , m_bIsPlayerCreated(IsPlayerCreated) { - SetGravity(-10.5f); + SetGravity(-16.0f); + SetAirDrag(0.02f); SetMaxHealth(5); SetHealth(5); SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index c89e7b87c..e3e3fac4f 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -94,7 +94,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; - + m_LastPlayerListTime = std::chrono::steady_clock::now(); m_PlayerName = a_PlayerName; @@ -106,7 +106,7 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : SetPosY(World->GetSpawnY()); SetPosZ(World->GetSpawnZ()); SetBedPos(Vector3i(static_cast<int>(World->GetSpawnX()), static_cast<int>(World->GetSpawnY()), static_cast<int>(World->GetSpawnZ()))); - + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() ); @@ -128,7 +128,14 @@ cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) : m_IsFlying = true; } } - + + if (m_GameMode == gmSpectator) // If player is reconnecting to the server in spectator mode + { + m_CanFly = true; + m_IsFlying = true; + m_bVisible = false; + } + cRoot::Get()->GetServer()->PlayerCreated(this); } @@ -145,17 +152,17 @@ cPlayer::~cPlayer(void) } LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID()); - + // Notify the server that the player is being destroyed cRoot::Get()->GetServer()->PlayerDestroying(this); SaveToDisk(); m_ClientHandle = nullptr; - + delete m_InventoryWindow; m_InventoryWindow = nullptr; - + LOGD("Player %p deleted", this); } @@ -201,7 +208,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_ClientHandle = nullptr; return; } - + if (!m_ClientHandle->IsPlaying()) { // We're not yet in the game, ignore everything @@ -210,21 +217,21 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } m_Stats.AddValue(statMinutesPlayed, 1); - + if (!a_Chunk.IsValid()) { // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) return; } - + super::Tick(a_Dt, a_Chunk); - + // Handle charging the bow: if (m_IsChargingBow) { m_BowCharge += 1; } - + // Handle updating experience if (m_bDirtyExperience) { @@ -236,7 +243,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); - + if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this, m_LastPos, GetPosition())) { CanMove = false; @@ -257,10 +264,10 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { FinishEating(); } - + HandleFood(); } - + if (m_IsFishing) { HandleFloater(); @@ -355,7 +362,7 @@ float cPlayer::GetXpPercentage() int currentLevel_XpBase = XpForLevel(currentLevel); return static_cast<float>(m_CurrentXp - currentLevel_XpBase) / - static_cast<float>(XpForLevel(1+currentLevel) - currentLevel_XpBase); + static_cast<float>(XpForLevel(1 + currentLevel) - currentLevel_XpBase); } @@ -364,7 +371,7 @@ float cPlayer::GetXpPercentage() bool cPlayer::SetCurrentExperience(int a_CurrentXp) { - if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>().max() - m_LifetimeTotalXp))) + if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<int>::max() - m_LifetimeTotalXp))) { LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); return false; // oops, they gave us a dodgey number @@ -403,7 +410,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta) m_LifetimeTotalXp += a_Xp_delta; } - LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp); + LOGD("Player \"%s\" gained / lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp); // Set experience to be updated m_bDirtyExperience = true; @@ -460,7 +467,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { return; } - + m_bTouchGround = a_bTouchGround; if (!m_bTouchGround) @@ -509,7 +516,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround) { // cPlayer makes sure damage isn't applied in creative, no need to check here TakeDamage(dtFalling, nullptr, Damage, Damage, 0); - + // Fall particles GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, static_cast<int>(GetPosY()) - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */); } @@ -541,7 +548,7 @@ void cPlayer::SetFoodLevel(int a_FoodLevel) m_FoodSaturationLevel = 5.0; return; } - + m_FoodLevel = FoodLevel; SendHealth(); } @@ -609,7 +616,7 @@ void cPlayer::StartEating(void) { // Set the timer: m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; - + // Send the packets: m_World->BroadcastEntityAnimation(*this, 3); m_World->BroadcastEntityMetadata(*this); @@ -623,7 +630,7 @@ void cPlayer::FinishEating(void) { // Reset the timer: m_EatingFinishTick = -1; - + // Send the packets: m_ClientHandle->SendEntityStatus(*this, esPlayerEatingAccepted); m_World->BroadcastEntityMetadata(*this); @@ -757,7 +764,7 @@ void cPlayer::SetSprintingMaxSpeed(double a_Speed) void cPlayer::SetFlyingMaxSpeed(double a_Speed) { m_FlyingMaxSpeed = a_Speed; - + // Update the flying speed, always: m_ClientHandle->SendPlayerAbilities(); } @@ -769,7 +776,7 @@ void cPlayer::SetFlyingMaxSpeed(double a_Speed) void cPlayer::SetCrouch(bool a_IsCrouched) { // Set the crouch status, broadcast to all visible players - + if (a_IsCrouched == m_IsCrouched) { // No change @@ -790,7 +797,7 @@ void cPlayer::SetSprint(bool a_IsSprinting) // No change return; } - + m_IsSprinting = a_IsSprinting; m_ClientHandle->SendPlayerMaxSpeed(); } @@ -876,7 +883,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) } } } - + if (super::DoTakeDamage(a_TDI)) { // Any kind of damage adds food exhaustion @@ -1005,7 +1012,7 @@ void cPlayer::Respawn(void) m_Health = GetMaxHealth(); SetInvulnerableTicks(20); - + // Reset food level: m_FoodLevel = MAX_FOOD_LEVEL; m_FoodSaturationLevel = 5.0; @@ -1017,7 +1024,7 @@ void cPlayer::Respawn(void) // ToDo: send score to client? How? m_ClientHandle->SendRespawn(GetWorld()->GetDimension(), true); - + // Extinguish the fire: StopBurning(); @@ -1151,7 +1158,7 @@ void cPlayer::CloseWindow(bool a_CanRefuse) m_CurrentWindow = m_InventoryWindow; return; } - + if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) { // Close accepted, go back to inventory window (the default): @@ -1189,21 +1196,17 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); return; } - + if (m_GameMode == a_GameMode) { // Gamemode already set return; } - + m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); - if (!(IsGameModeCreative() || IsGameModeSpectator())) - { - SetFlying(false); - SetCanFly(false); - } + SetCapabilities(); m_World->BroadcastPlayerListUpdateGameMode(*this); } @@ -1215,6 +1218,30 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) void cPlayer::LoginSetGameMode( eGameMode a_GameMode) { m_GameMode = a_GameMode; + + SetCapabilities(); +} + + + + + +void cPlayer::SetCapabilities() +{ + if (!IsGameModeCreative() || IsGameModeSpectator()) + { + SetFlying(false); + SetCanFly(false); + } + + if (IsGameModeSpectator()) + { + SetVisible(false); + } + else + { + SetVisible(true); + } } @@ -1306,12 +1333,12 @@ void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees) Vector3d cPlayer::GetThrowStartPos(void) const { Vector3d res = GetEyePosition(); - + // Adjust the position to be just outside the player's bounding box: res.x += 0.16 * cos(GetPitch()); res.y += -0.1; res.z += 0.16 * sin(GetPitch()); - + return res; } @@ -1323,9 +1350,9 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const { Vector3d res = GetLookVector(); res.Normalize(); - + // TODO: Add a slight random change (+-0.0075 in each direction) - + return res * a_SpeedCoeff; } @@ -1370,13 +1397,13 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos) } return; } - + // TODO: should do some checks to see if player is not moving through terrain // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too Vector3d DeltaPos = a_NewPos - GetPosition(); UpdateMovementStats(DeltaPos); - + SetPosition( a_NewPos); SetStance(a_NewPos.y + 1.62); } @@ -1411,17 +1438,26 @@ bool cPlayer::HasPermission(const AString & a_Permission) // Empty permission request is always granted return true; } - + AStringVector Split = StringSplit(a_Permission, "."); + // Iterate over all restrictions; if any matches, then return failure: + for (auto & Restriction: m_SplitRestrictions) + { + if (PermissionMatches(Split, Restriction)) + { + return false; + } + } // for Restriction - m_SplitRestrictions[] + // Iterate over all granted permissions; if any matches, then return success: - for (AStringVectorVector::const_iterator itr = m_SplitPermissions.begin(), end = m_SplitPermissions.end(); itr != end; ++itr) + for (auto & Permission: m_SplitPermissions) { - if (PermissionMatches(Split, *itr)) + if (PermissionMatches(Split, Permission)) { return true; } - } // for itr - m_SplitPermissions[] + } // for Permission - m_SplitPermissions[] // No granted permission matches return false; @@ -1574,7 +1610,7 @@ void cPlayer::TossItems(const cItems & a_Items) { return; } - + m_Stats.AddValue(statItemsDropped, (StatValue)a_Items.Size()); double vX = 0, vY = 0, vZ = 0; @@ -1596,19 +1632,30 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) // Don't move to same world return false; } - + + // Ask the plugins if the player is allowed to changing the world + if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World)) + { + // A Plugin doesn't allow the player to changing the world + return false; + } + // Send the respawn packet: if (a_ShouldSendRespawn && (m_ClientHandle != nullptr)) { m_ClientHandle->SendRespawn(a_World->GetDimension()); } + // Broadcast for other people that the player is gone. + GetWorld()->BroadcastDestroyEntity(*this); + // Remove player from the old world SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal GetWorld()->RemovePlayer(this, false); // Queue adding player to the new world, including all the necessary adjustments to the object a_World->AddPlayer(this); + cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value // Update the view distance. @@ -1619,7 +1666,13 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) { m_ClientHandle->SendWeather(a_World->GetWeather()); } - + + // Broadcast the player into the new world. + a_World->BroadcastSpawnEntity(*this); + + // Player changed the world, call the hook + cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld); + return true; } @@ -1636,7 +1689,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) { return true; } - + // Load from the offline UUID file, if allowed: AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName()); const char * OfflineUsage = " (unused)"; @@ -1648,7 +1701,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) return true; } } - + // Load from the old-style name-based file, if allowed: if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData()) { @@ -1663,7 +1716,7 @@ bool cPlayer::LoadFromDisk(cWorldPtr & a_World) return true; } } - + // None of the files loaded successfully LOG("Player data file not found for %s (%s, offline %s%s), will be reset to defaults.", GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str(), OfflineUsage @@ -1740,7 +1793,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) { m_CanFly = true; } - + m_Inventory.LoadFromJson(root["inventory"]); cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents); @@ -1756,14 +1809,14 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt(); // Load the player stats. - // We use the default world name (like bukkit) because stats are shared between dimensions/worlds. + // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - + LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), a_World->GetName().c_str() ); - + return true; } @@ -1853,7 +1906,7 @@ bool cPlayer::SaveToDisk() } // Save the player stats. - // We use the default world name (like bukkit) because stats are shared between dimensions/worlds. + // We use the default world name (like bukkit) because stats are shared between dimensions / worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); if (!StatSerializer.Save()) { @@ -2061,7 +2114,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) } else { - if (Value >= 25) // Ignore small/slow movement + if (Value >= 25) // Ignore small / slow movement { m_Stats.AddValue(statDistFlown, Value); } @@ -2119,7 +2172,7 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } - + // Calculate the distance travelled, update the last pos: Vector3d Movement(GetPosition() - m_LastPos); Movement.y = 0; // Only take XZ movement into account @@ -2163,15 +2216,24 @@ void cPlayer::LoadRank(void) RankMgr->UpdatePlayerName(m_UUID, m_PlayerName); } m_Permissions = RankMgr->GetPlayerPermissions(m_UUID); + m_Restrictions = RankMgr->GetPlayerRestrictions(m_UUID); RankMgr->GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode); // Break up the individual permissions on each dot, into m_SplitPermissions: m_SplitPermissions.clear(); m_SplitPermissions.reserve(m_Permissions.size()); - for (AStringVector::const_iterator itr = m_Permissions.begin(), end = m_Permissions.end(); itr != end; ++itr) + for (auto & Permission: m_Permissions) + { + m_SplitPermissions.push_back(StringSplit(Permission, ".")); + } // for Permission - m_Permissions[] + + // Break up the individual restrictions on each dot, into m_SplitRestrictions: + m_SplitRestrictions.clear(); + m_SplitRestrictions.reserve(m_Restrictions.size()); + for (auto & Restriction: m_Restrictions) { - m_SplitPermissions.push_back(StringSplit(*itr, ".")); - } // for itr - m_Permissions[] + m_SplitRestrictions.push_back(StringSplit(Restriction, ".")); + } // for itr - m_Restrictions[] } @@ -2313,7 +2375,7 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID) { AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID); ASSERT(UUID.length() == 36); - + AString res("players/"); res.append(UUID, 0, 2); res.push_back('/'); @@ -2321,7 +2383,3 @@ AString cPlayer::GetUUIDFileName(const AString & a_UUID) res.append(".json"); return res; } - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 3dae58dc1..a84fdd0c7 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -26,42 +26,42 @@ class cPlayer : public cPawn { typedef cPawn super; - + public: static const int MAX_HEALTH; - + static const int MAX_FOOD_LEVEL; - + /** Number of ticks it takes to eat an item */ static const int EATING_TICKS; - + // tolua_end - + CLASS_PROTODEF(cPlayer) - + cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName); - + virtual ~cPlayer(); virtual void SpawnOn(cClientHandle & a_Client) override; - + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } /** Returns the curently equipped weapon; empty item if none */ virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } - + /** Returns the currently equipped helmet; empty item if none */ virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } - + /** Returns the currently equipped chestplate; empty item if none */ virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } /** Returns the currently equipped leggings; empty item if none */ virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } - + /** Returns the currently equipped boots; empty item if none */ virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } @@ -104,16 +104,16 @@ public: static int CalcLevelFromXp(int a_CurrentXp); // tolua_end - + /** Starts charging the equipped bow */ void StartChargingBow(void); - + /** Finishes charging the current bow. Returns the number of ticks for which the bow has been charged */ int FinishChargingBow(void); - + /** Cancels the current bow charging */ void CancelChargingBow(void); - + /** Returns true if the player is currently charging the bow */ bool IsChargingBow(void) const { return m_IsChargingBow; } @@ -122,13 +122,13 @@ public: double GetEyeHeight(void) const; // tolua_export Vector3d GetEyePosition(void) const; // tolua_export virtual bool IsOnGround(void) const override { return m_bTouchGround; } - inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. + inline double GetStance(void) const { return m_Stance; } // tolua_export inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export inline const cInventory & GetInventory(void) const { return m_Inventory; } /** Gets the contents of the player's associated enderchest */ cItemGrid & GetEnderChestContents(void) { return m_EnderChestContents; } - + inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export /** Returns whether the player is climbing (ladders, vines etc.) */ @@ -137,43 +137,49 @@ public: virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; // tolua_begin - + /** Sends the "look" packet to the player, forcing them to set their rotation to the specified values. a_YawDegrees is clipped to range [-180, +180), a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90] */ void SendRotation(double a_YawDegrees, double a_PitchDegrees); - + /** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */ Vector3d GetThrowStartPos(void) const; - + /** Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. */ Vector3d GetThrowSpeed(double a_SpeedCoeff) const; - + /** Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */ eGameMode GetGameMode(void) const { return m_GameMode; } - + /** Returns the current effective gamemode (inherited gamemode is resolved before returning) */ eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } - + /** Sets the gamemode for the player. The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. Updates the gamemode on the client (sends the packet) */ void SetGameMode(eGameMode a_GameMode); + // Sets the current gamemode, doesn't check validity, doesn't send update packets to client + void LoginSetGameMode(eGameMode a_GameMode); + + // Updates player's capabilities - flying, visibility, etc. from their gamemode. + void SetCapabilities(); + /** Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world */ bool IsGameModeCreative(void) const; - + /** Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world */ bool IsGameModeSurvival(void) const; - + /** Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world */ bool IsGameModeAdventure(void) const; - + /** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */ bool IsGameModeSpectator(void) const; - + AString GetIP(void) const { return m_IP; } // tolua_export /** Returns the associated team, nullptr if none */ @@ -195,11 +201,8 @@ public: If the achievement has been already awarded to the player, this method will just increment the stat counter. Returns the _new_ stat value. (0 = Could not award achievement) */ unsigned int AwardAchievement(const eStatistic a_Ach); - + void SetIP(const AString & a_IP); - - // Sets the current gamemode, doesn't check validity, doesn't send update packets to client - void LoginSetGameMode(eGameMode a_GameMode); /** Forces the player to move in the given direction. @deprecated Use SetSpeed instead. */ @@ -210,15 +213,15 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - + /** Opens the specified window; closes the current one first using CloseWindow() */ void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - + // tolua_begin - + /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */ void CloseWindow(bool a_CanRefuse = true); - + /** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */ void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); @@ -243,7 +246,7 @@ public: const AString & GetName(void) const { return m_PlayerName; } void SetName(const AString & a_Name) { m_PlayerName = a_Name; } - + // tolua_end bool HasPermission(const AString & a_Permission); // tolua_export @@ -254,10 +257,13 @@ public: static bool PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template); // Exported in ManualBindings with AString params /** Returns all the permissions that the player has assigned to them. */ - const AStringVector & GetPermissions(void) { return m_Permissions; } // Exported in ManualBindings.cpp + const AStringVector & GetPermissions(void) const { return m_Permissions; } // Exported in ManualBindings.cpp + + /** Returns all the restrictions that the player has assigned to them. */ + const AStringVector & GetRestrictions(void) const { return m_Restrictions; } // Exported in ManualBindings.cpp // tolua_begin - + /** Returns the full color code to use for this player, based on their rank. The returned value either is empty, or includes the cChatColor::Delimiter. */ AString GetColor(void) const; @@ -276,15 +282,15 @@ public: /** Heals the player by the specified amount of HPs (positive only); sends health update */ virtual void Heal(int a_Health) override; - + int GetFoodLevel (void) const { return m_FoodLevel; } double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } int GetFoodTickTimer (void) const { return m_FoodTickTimer; } double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } - + /** Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore */ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } - + void SetFoodLevel (int a_FoodLevel); void SetFoodSaturationLevel (double a_FoodSaturationLevel); void SetFoodTickTimer (int a_FoodTickTimer); @@ -295,10 +301,10 @@ public: /** Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. */ void AddFoodExhaustion(double a_Exhaustion); - + /** Returns true if the player is currently in the process of eating the currently equipped item */ bool IsEating(void) const { return (m_EatingFinishTick >= 0); } - + /** Returns true if the player is currently flying. */ bool IsFlying(void) const { return m_IsFlying; } @@ -326,16 +332,16 @@ public: GetWorld()->BroadcastEntityAnimation(*this, 2); } } - + /** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */ void StartEating(void); - + /** Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets */ void FinishEating(void); - + /** Aborts the current eating operation */ void AbortEating(void); - + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void Killed(cEntity * a_Victim) override; @@ -353,69 +359,69 @@ public: bool SaveToDisk(void); typedef cWorld * cWorldPtr; - + /** Loads the player data from the disk file Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile() Returns true on success, false on failure */ bool LoadFromDisk(cWorldPtr & a_World); - + /** Loads the player data from the specified file Sets a_World to the world where the player will spawn, based on the stored world name or the default world Returns true on success, false on failure */ bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World); - + const AString & GetLoadedWorldName() { return m_LoadedWorldName; } void UseEquippedItem(int a_Amount = 1); - + void SendHealth(void); void SendExperience(void); - + /** In UI windows, get the item that the player is dragging */ cItem & GetDraggingItem(void) {return m_DraggingItem; } - + // In UI windows, when inventory-painting: /** Clears the list of slots that are being inventory-painted. To be used by cWindow only */ void ClearInventoryPaintSlots(void); - + /** Adds a slot to the list for inventory painting. To be used by cWindow only */ void AddInventoryPaintSlot(int a_SlotNum); - + /** Returns the list of slots currently stored for inventory painting. To be used by cWindow only */ const cSlotNums & GetInventoryPaintSlots(void) const; - + // tolua_begin - + /** Returns the current relative maximum speed (takes current sprinting / flying state into account) */ double GetMaxSpeed(void) const; - + /** Gets the normal relative maximum speed */ double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } - + /** Gets the sprinting relative maximum speed */ double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } - + /** Gets the flying relative maximum speed */ double GetFlyingMaxSpeed(void) const { return m_FlyingMaxSpeed; } - + /** Sets the normal relative maximum speed. Sends the update to player, if needed. */ void SetNormalMaxSpeed(double a_Speed); - + /** Sets the sprinting relative maximum speed. Sends the update to player, if needed. */ void SetSprintingMaxSpeed(double a_Speed); - + /** Sets the flying relative maximum speed. Sends the update to player, if needed. */ void SetFlyingMaxSpeed(double a_Speed); - + /** Sets the crouch status, broadcasts to all visible players */ void SetCrouch(bool a_IsCrouched); - + /** Starts or stops sprinting, sends the max speed update to the client, if needed */ void SetSprint(bool a_IsSprinting); - + /** Flags the player as flying */ void SetFlying(bool a_IsFlying); @@ -439,17 +445,17 @@ public: /** Sets the player's bed (home) position */ void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; } - + // tolua_end /** Update movement-related statistics. */ void UpdateMovementStats(const Vector3d & a_DeltaPos); - + // tolua_begin /** Returns wheter the player can fly or not. */ virtual bool CanFly(void) const { return m_CanFly; } - + /** Returns the UUID (short format) that has been read from the client, or empty string if not available. */ const AString & GetUUID(void) const { return m_UUID; } @@ -480,16 +486,16 @@ public: bool PlaceBlocks(const sSetBlockVector & a_Blocks); // cEntity overrides: - virtual bool IsCrouched (void) const { return m_IsCrouched; } - virtual bool IsSprinting(void) const { return m_IsSprinting; } - virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); } + virtual bool IsCrouched (void) const override { return m_IsCrouched; } + virtual bool IsSprinting(void) const override { return m_IsSprinting; } + virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); } - virtual void Detach(void); + virtual void Detach(void) override; /** Called by cClientHandle when the client is being destroyed. The player removes its m_ClientHandle ownership so that the ClientHandle gets deleted. */ void RemoveClientHandle(void); - + protected: typedef std::vector<std::vector<AString> > AStringVectorVector; @@ -500,10 +506,18 @@ protected: /** All the permissions that this player has, based on their rank. */ AStringVector m_Permissions; + /** All the restrictions that this player has, based on their rank. */ + AStringVector m_Restrictions; + /** All the permissions that this player has, based on their rank, split into individual dot-delimited parts. This is used mainly by the HasPermission() function to optimize the lookup. */ AStringVectorVector m_SplitPermissions; + /** All the restrictions that this player has, based on their rank, split into individual dot-delimited parts. + This is used mainly by the HasPermission() function to optimize the lookup. */ + AStringVectorVector m_SplitRestrictions; + + // Message visuals: AString m_MsgPrefix, m_MsgSuffix; AString m_MsgNameColorCode; @@ -524,16 +538,16 @@ protected: // Food-related variables: /** Represents the food bar, one point equals half a "drumstick" */ int m_FoodLevel; - + /** "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel */ double m_FoodSaturationLevel; - + /** Count-up to the healing or damaging action, based on m_FoodLevel */ int m_FoodTickTimer; - + /** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */ double m_FoodExhaustionLevel; - + float m_LastJumpHeight; float m_LastGroundHeight; bool m_bTouchGround; @@ -553,31 +567,31 @@ protected: eGameMode m_GameMode; AString m_IP; - + /** The item being dragged by the cursor while in a UI window */ cItem m_DraggingItem; std::chrono::steady_clock::time_point m_LastPlayerListTime; cClientHandlePtr m_ClientHandle; - + cSlotNums m_InventoryPaintSlots; - + /** Max speed, relative to the game default. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ double m_NormalMaxSpeed; - + /** Max speed, relative to the game default max speed, when sprinting. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1.3. */ double m_SprintingMaxSpeed; - + /** Max speed, relative to the game default flying max speed, when flying. 1 means regular speed, 2 means twice as fast, 0.5 means half-speed. Default value is 1. */ double m_FlyingMaxSpeed; - + bool m_IsCrouched; bool m_IsSprinting; bool m_IsFlying; @@ -618,7 +632,7 @@ protected: Will not apply food penalties if found to be true; will set to false after processing */ bool m_bIsTeleporting; - + /** The short UUID (no dashes) of the player, as read from the ClientHandle. If no ClientHandle is given, the UUID is initialized to empty. */ AString m_UUID; @@ -631,14 +645,14 @@ protected: void ResolvePermissions(void); void ResolveGroups(void); - virtual void Destroyed(void); + virtual void Destroyed(void) override; - /** Filters out damage for creative mode/friendly fire */ + /** Filters out damage for creative mode / friendly fire */ virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; /** Stops players from burning in creative mode */ virtual void TickBurning(cChunk & a_Chunk) override; - + /** Called in each tick to handle food-related processing */ void HandleFood(void); @@ -655,7 +669,3 @@ protected: This can be used both for online and offline UUIDs. */ AString GetUUIDFileName(const AString & a_UUID); } ; // tolua_export - - - - diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 4684e3e09..05b7669cd 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -227,6 +227,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a ), m_IsInGround(false) { + SetGravity(-12.0f); + SetAirDrag(0.01f); } @@ -242,6 +244,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve SetSpeed(a_Speed); SetYawFromSpeed(); SetPitchFromSpeed(); + SetGravity(-12.0f); + SetAirDrag(0.01f); } @@ -349,9 +353,11 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a return; } - const Vector3d PerTickSpeed = GetSpeed() / 20; + auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt); + + const Vector3d DeltaSpeed = GetSpeed() * DtSec.count(); const Vector3d Pos = GetPosition(); - const Vector3d NextPos = Pos + PerTickSpeed; + const Vector3d NextPos = Pos + DeltaSpeed; // Test for entity collisions: cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos); @@ -388,8 +394,8 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a // Add slowdown and gravity effect to the speed: Vector3d NewSpeed(GetSpeed()); - NewSpeed.y += m_Gravity / 20; - NewSpeed *= TracerCallback.GetSlowdownCoeff(); + NewSpeed.y += m_Gravity * DtSec.count(); + NewSpeed -= NewSpeed * (m_AirDrag * 20.0f) * DtSec.count(); SetSpeed(NewSpeed); SetYawFromSpeed(); SetPitchFromSpeed(); diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index de460a8ad..7ee87f93a 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -109,7 +109,7 @@ protected: eKind m_ProjectileKind; /** The structure for containing the entity ID and name who has created this projectile - The ID and/or name may be nullptr (e.g. for dispensers/mobs). */ + The ID and / or name may be nullptr (e.g. for dispensers / mobs). */ CreatorData m_CreatorData; /** True if the projectile has hit the ground and is stuck there */ diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp index a89d2f300..d849bd4c9 100644 --- a/src/Entities/TNTEntity.cpp +++ b/src/Entities/TNTEntity.cpp @@ -12,6 +12,8 @@ cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks) : super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), m_FuseTicks(a_FuseTicks) { + SetGravity(-16.0f); + SetAirDrag(0.02f); } @@ -22,6 +24,8 @@ cTNTEntity::cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks) : super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), m_FuseTicks(a_FuseTicks) { + SetGravity(-16.0f); + SetAirDrag(0.4f); } diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp index a7e774bba..dc95e3edd 100644 --- a/src/Entities/WitherSkullEntity.cpp +++ b/src/Entities/WitherSkullEntity.cpp @@ -16,6 +16,8 @@ cWitherSkullEntity::cWitherSkullEntity(cEntity * a_Creator, double a_X, double a super(pkWitherSkull, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) { SetSpeed(a_Speed); + SetGravity(0); + SetAirDrag(0); } diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp index 737b13535..c1716f026 100644 --- a/src/FastRandom.cpp +++ b/src/FastRandom.cpp @@ -6,12 +6,28 @@ #include "Globals.h" #include "FastRandom.h" +#include <random> + #ifdef _WIN32 - #define thread_local __declspec(thread) + #define thread_local static __declspec(thread) +#elif defined __APPLE__ + #define thread_local static __thread #endif -thread_local unsigned int m_Counter = 0; - +static unsigned int GetRandomSeed() +{ + thread_local bool SeedCounterInitialized = 0; + thread_local unsigned int SeedCounter = 0; + + if (!SeedCounterInitialized) + { + std::random_device rd; + std::uniform_int_distribution<unsigned int> dist; + SeedCounter = dist(rd); + SeedCounterInitialized = true; + } + return ++SeedCounter; +} @@ -92,7 +108,7 @@ public: cFastRandom::cFastRandom(void) : - m_LinearRand(m_Counter++) + m_LinearRand(GetRandomSeed()) { } @@ -136,7 +152,7 @@ int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End) // MTRand: MTRand::MTRand() : - m_MersenneRand(m_Counter++) + m_MersenneRand(GetRandomSeed()) { } diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp index 1e8dbef90..0a3673711 100644 --- a/src/Generating/Caves.cpp +++ b/src/Generating/Caves.cpp @@ -237,7 +237,7 @@ bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & return true; } - // Smoothing: for each line segment, add points on its 1/4 lengths + // Smoothing: for each line segment, add points on its 1 / 4 lengths bool res = false; size_t Num = a_Src.size() - 2; // this many intermediary points a_Dst.clear(); @@ -488,7 +488,7 @@ void cCaveTunnel::ProcessChunk( continue; } - // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: + // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3 / 7 off the top and bottom: int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc int DifY = itr->m_BlockY; int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc @@ -529,7 +529,7 @@ void cCaveTunnel::ProcessChunk( /* #ifdef _DEBUG - // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: + // For debugging purposes, outline the shape of the cave using glowstone, after carving the entire cave: for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) { int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 854aac60d..72d8f5258 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -209,7 +209,7 @@ void cChunkGenerator::Execute(void) { if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC)) { - LOG("Chunk generator performance: %.2f ch/s (%d ch total)", + LOG("Chunk generator performance: %.2f ch / sec (%d ch total)", (double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart), NumChunksGenerated ); @@ -241,7 +241,7 @@ void cChunkGenerator::Execute(void) // Display perf info once in a while: if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC)) { - LOG("Chunk generator performance: %.2f ch/s (%d ch total)", + LOG("Chunk generator performance: %.2f ch / sec (%d ch total)", (double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart), NumChunksGenerated ); diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp index cb9c04fd7..7cadc881a 100644 --- a/src/Generating/CompoGen.cpp +++ b/src/Generating/CompoGen.cpp @@ -218,7 +218,7 @@ void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile) cCompoGenNether::cCompoGenNether(int a_Seed) : m_Noise1(a_Seed + 10), m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000), - m_Threshold(0) + m_MaxThreshold(25000) { } @@ -282,17 +282,16 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc: // Interpolate between FloorLo and FloorHi: for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) { + int Threshold = static_cast<int>(m_Noise1.CubicNoise2D(static_cast<float>(BaseX + x) / 75, static_cast<float>(BaseZ + z) / 75) * m_MaxThreshold); int Lo = FloorLo[x + 17 * z] / 256; int Hi = FloorHi[x + 17 * z] / 256; for (int y = 0; y < SEGMENT_HEIGHT; y++) { int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; - BLOCKTYPE Block = E_BLOCK_AIR; - if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air. + if (Val < Threshold) // Don't calculate if the block should be Netherrack when it's already decided that it's air. { - Block = E_BLOCK_NETHERRACK; + a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_NETHERRACK); } - a_ChunkDesc.SetBlockType(x, y + Segment, z, Block); } } @@ -329,7 +328,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc: void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile) { - m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold); + m_MaxThreshold = a_IniFile.GetValueSetF("Generator", "NetherMaxThreshold", m_MaxThreshold); } diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h index 3847688cd..d4d38bfdd 100644 --- a/src/Generating/CompoGen.h +++ b/src/Generating/CompoGen.h @@ -99,7 +99,7 @@ protected: cNoise m_Noise1; cNoise m_Noise2; - int m_Threshold; + double m_MaxThreshold; // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override; diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 4a670b064..6b7643ddb 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -347,6 +347,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1"); m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib))); } + else if (NoCaseCompare(*itr, "GlowStone") == 0) + { + m_FinishGens.push_back(cFinishGenPtr(new cFinishGenGlowStone(Seed))); + } else if (NoCaseCompare(*itr, "Ice") == 0) { m_FinishGens.push_back(cFinishGenPtr(new cFinishGenIce)); @@ -447,7 +451,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) else if (NoCaseCompare(*itr, "NetherForts") == 0) { int GridSize = a_IniFile.GetValueSetI("Generator", "NetherFortsGridSize", 512); - int MaxOffset = a_IniFile.GetValueSetI("Generator", "NetherFortMaxOffset", 128); + int MaxOffset = a_IniFile.GetValueSetI("Generator", "NetherFortsMaxOffset", 128); int MaxDepth = a_IniFile.GetValueSetI("Generator", "NetherFortsMaxDepth", 12); m_FinishGens.push_back(cFinishGenPtr(new cNetherFortGen(Seed, GridSize, MaxOffset, MaxDepth))); } diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 5839a4ccc..c988224e6 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -180,6 +180,118 @@ void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a //////////////////////////////////////////////////////////////////////////////// +// cFinishGenGlowStone: + +void cFinishGenGlowStone::GenFinish(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + // Change the number of attempts to create a vein depending on the maximum height of the chunk. A standard Nether could have 5 veins at most. + int NumGlowStone = m_Noise.IntNoise2DInt(ChunkX, ChunkZ) % a_ChunkDesc.GetMaxHeight() / 23; + + for (int i = 1; i <= NumGlowStone; i++) + { + // The maximum size for a string of glowstone can get 3 - 5 blocks long + int Size = 3 + m_Noise.IntNoise3DInt(ChunkX, i, ChunkZ) % 3; + + // Generate X / Z coordinates. + int X = Size + (m_Noise.IntNoise2DInt(i, Size) % (cChunkDef::Width - Size * 2)); + int Z = Size + (m_Noise.IntNoise2DInt(X, i) % (cChunkDef::Width - Size * 2)); + + int Height = a_ChunkDesc.GetHeight(X, Z); + for (int y = Height; y > Size; y--) + { + if (!cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(X, y, Z))) + { + // Current block isn't solid, bail out + continue; + } + + if (a_ChunkDesc.GetBlockType(X, y - 1, Z) != E_BLOCK_AIR) + { + // The block below isn't air, bail out + continue; + } + + if ((m_Noise.IntNoise3DInt(X, y, Z) % 100) < 95) + { + // Have a 5% chance of creating the glowstone + continue; + } + + TryPlaceGlowstone(a_ChunkDesc, X, y, Z, Size, 5 + m_Noise.IntNoise3DInt(X, y, Z) % 7); + break; + } + } +} + + + + + +void cFinishGenGlowStone::TryPlaceGlowstone(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings) +{ + // The starting point of every glowstone string + Vector3i StartPoint = Vector3i(a_RelX, a_RelY, a_RelZ); + + // Array with possible directions for a string of glowstone to go to. + const Vector3i AvailableDirections[] = + { + { -1, 0, 0 }, { 1, 0, 0 }, + { 0, -1, 0 }, // Don't let the glowstone go up + { 0, 0, -1 }, { 0, 0, 1 }, + + // Diagonal direction. Only X or Z with Y. + // If all were changed the glowstone string looks awkward + { 0, -1, 1 }, { 1, -1, 0 }, + { 0, -1, -1 }, { -1, -1, 0 }, + + }; + + for (int i = 1; i <= a_NumStrings; i++) + { + // The current position of the string that is being generated + Vector3i CurrentPos = Vector3i(StartPoint); + + // A vector where the previous direction of a glowstone string is stored. + // This is used to make the strings change direction when going one block further + Vector3i PreviousDirection = Vector3i(); + + for (int j = 0; j < a_Size; j++) + { + Vector3i Direction = AvailableDirections[m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i, CurrentPos.z) % ARRAYCOUNT(AvailableDirections)]; + int Attempts = 2; // multiply by 1 would make no difference, so multiply by 2 instead + + while (Direction.Equals(PreviousDirection)) + { + // To make the glowstone branches look better we want to make the direction change every time. + Direction = AvailableDirections[m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i * Attempts, CurrentPos.z) % ARRAYCOUNT(AvailableDirections)]; + Attempts++; + } + + // Update the previous direction variable + PreviousDirection = Direction; + + // Update the position of the glowstone string + CurrentPos += Direction; + if (cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z)) && (a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z) != E_BLOCK_GLOWSTONE)) + { + // The glowstone hit something solid, and it wasn't glowstone. Stop the string. + break; + } + + // Place a glowstone block. + a_ChunkDesc.SetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z, E_BLOCK_GLOWSTONE); + } + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// // cFinishGenTallGrass: void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc) @@ -523,7 +635,7 @@ void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc) { // The current block is air. Let's bail ut. BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z); - if (Block == E_BLOCK_AIR) + if (Block != E_BLOCK_NETHERRACK) { continue; } @@ -1147,7 +1259,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX } if ( (BlockUnderFeet != E_BLOCK_GRASS) && - ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig)) + ((AnimalToSpawn == mtWolf) || (AnimalToSpawn == mtRabbit) || (AnimalToSpawn == mtCow) || (AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig)) ) { return false; @@ -1162,6 +1274,7 @@ bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5); cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn); + NewMob->SetHealth(NewMob->GetMaxHealth()); NewMob->SetPosition(AnimalX, AnimalY, AnimalZ); a_ChunkDesc.GetEntities().push_back(NewMob); LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ); diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index 70696c4f8..aa50335d4 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -70,6 +70,28 @@ protected: +class cFinishGenGlowStone : + public cFinishGen +{ +public: + cFinishGenGlowStone(int a_Seed) : + m_Noise(a_Seed), + m_Seed(a_Seed) + { + } + +protected: + cNoise m_Noise; + int m_Seed; + + void TryPlaceGlowstone(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings); + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + class cFinishGenTallGrass : public cFinishGen { diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 54567cb4d..e34ffc57c 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -667,7 +667,7 @@ public: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) { - // Generate the biomes for the 3*3 neighbors: + // Generate the biomes for the 3 * 3 neighbors: cChunkDef::BiomeMap neighborBiomes[3][3]; for (int z = 0; z < 3; z++) for (int x = 0; x < 3; x++) { diff --git a/src/Generating/IntGen.h b/src/Generating/IntGen.h index 854563f41..9cc881639 100644 --- a/src/Generating/IntGen.h +++ b/src/Generating/IntGen.h @@ -5,7 +5,7 @@ /* The integers generated may be interpreted in several ways: -- land/see designators +- land / sea designators - 0 = ocean - >0 = land - biome group @@ -310,7 +310,7 @@ public: } } - // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min: + // Copy from Cache into a_Values; take into account the even / odd offsets in a_Min: for (int z = 0; z < SizeZ; ++z) { memcpy(a_Values + z * SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), SizeX * sizeof(int)); diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index eadc66a4e..63e88c2a8 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -680,7 +680,7 @@ void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_Ch void cBiomalNoise3DComposable::CalcBiomeParamArrays(int a_ChunkX, int a_ChunkZ, ChunkParam & a_HeightAmp, ChunkParam & a_MidPoint) { - // Generate the 3*3 chunks of biomes around this chunk: + // Generate the 3 * 3 chunks of biomes around this chunk: cChunkDef::BiomeMap neighborBiomes[3 * 3]; for (int z = 0; z < 3; z++) { diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h index e709222fe..9e471e8bb 100644 --- a/src/Generating/ProtIntGen.h +++ b/src/Generating/ProtIntGen.h @@ -229,7 +229,7 @@ public: } } - // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min: + // Copy from Cache into a_Values; take into account the even / odd offsets in a_Min: for (int z = 0; z < a_SizeZ; ++z) { memcpy(a_Values + z * a_SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), a_SizeX * sizeof(int)); diff --git a/src/Generating/RainbowRoadsGen.cpp b/src/Generating/RainbowRoadsGen.cpp index c3c07cdec..fd4a81692 100644 --- a/src/Generating/RainbowRoadsGen.cpp +++ b/src/Generating/RainbowRoadsGen.cpp @@ -61,7 +61,7 @@ protected: /** The noise used as a pseudo-random generator */ cNoise m_Noise; - /** Maximum size, in X/Z blocks, of the village (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the village (radius from the origin) */ int m_MaxSize; /** Borders of the vilalge - no item may reach out of this cuboid. */ diff --git a/src/Generating/RainbowRoadsGen.h b/src/Generating/RainbowRoadsGen.h index 5813e1d14..52d48ceca 100644 --- a/src/Generating/RainbowRoadsGen.h +++ b/src/Generating/RainbowRoadsGen.h @@ -34,7 +34,7 @@ protected: /** Maximum depth of the generator tree*/ int m_MaxDepth; - /** Maximum size, in X/Z blocks, of the base (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */ int m_MaxSize; diff --git a/src/Generating/Ravines.cpp b/src/Generating/Ravines.cpp index 70b9d0b62..9f8f69139 100644 --- a/src/Generating/Ravines.cpp +++ b/src/Generating/Ravines.cpp @@ -124,7 +124,7 @@ cStructGenRavines::cRavine::cRavine(int a_GridX, int a_GridZ, int a_OriginX, int void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) { - // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): + // Modify the size slightly to have different-sized ravines (1 / 2 to 1 / 1 of a_Size): a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction @@ -177,7 +177,7 @@ void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cR return; } - // Smoothing: for each line segment, add points on its 1/4 lengths + // Smoothing: for each line segment, add points on its 1 / 4 lengths size_t Num = a_Src.size() - 2; // this many intermediary points a_Dst.clear(); a_Dst.reserve(Num * 2 + 2); diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index 2d5a73739..7572cdcbf 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -311,7 +311,7 @@ void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_Ore rnd /= cChunkDef::Width; int BaseY = rnd % a_MaxHeight; rnd /= a_MaxHeight; - int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger + int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1 / 4 larger int Num = 0; while (Num < NestSize) { diff --git a/src/Generating/TestRailsGen.cpp b/src/Generating/TestRailsGen.cpp index 66ab8b33f..8256b55a0 100644 --- a/src/Generating/TestRailsGen.cpp +++ b/src/Generating/TestRailsGen.cpp @@ -61,7 +61,7 @@ protected: /** The noise used as a pseudo-random generator */ cNoise m_Noise; - /** Maximum size, in X/Z blocks, of the structure (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */ int m_MaxSize; /** Borders of the structure - no item may reach out of this cuboid. */ diff --git a/src/Generating/TestRailsGen.h b/src/Generating/TestRailsGen.h index 5b0ce95f4..88e38b368 100644 --- a/src/Generating/TestRailsGen.h +++ b/src/Generating/TestRailsGen.h @@ -31,10 +31,10 @@ protected: /** The noise used for generating random numbers */ cNoise m_Noise; - /** Maximum depth of the generator tree*/ + /** Maximum depth of the generator tree */ int m_MaxDepth; - /** Maximum size, in X/Z blocks, of the base (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */ int m_MaxSize; diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index 9e72a688f..72fe5f819 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -499,7 +499,7 @@ void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Nois PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); h--; - // Third and fourth layers - BigO2 and maybe 2*Corners: + // Third and fourth layers - BigO2 and maybe 2 * Corners: for (int Row = 0; Row < 2; Row++) { PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); @@ -673,7 +673,7 @@ void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); h--; - // Third and fourth layers - BigO2 and maybe 2*Corners: + // Third and fourth layers - BigO2 and maybe 2 * Corners: for (int Row = 0; Row < 2; Row++) { PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); diff --git a/src/Generating/TwoHeights.cpp b/src/Generating/TwoHeights.cpp index e75c301de..06c474458 100644 --- a/src/Generating/TwoHeights.cpp +++ b/src/Generating/TwoHeights.cpp @@ -69,7 +69,7 @@ public: } - virtual void InitializeShapeGen(cIniFile & a_IniFile) + virtual void InitializeShapeGen(cIniFile & a_IniFile) override { m_FrequencyX = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyX", 40)); m_FrequencyY = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyY", 40)); diff --git a/src/Generating/UnderwaterBaseGen.cpp b/src/Generating/UnderwaterBaseGen.cpp index 8916f4b5f..dbc4aef1b 100644 --- a/src/Generating/UnderwaterBaseGen.cpp +++ b/src/Generating/UnderwaterBaseGen.cpp @@ -61,7 +61,7 @@ protected: /** The noise used as a pseudo-random generator */ cNoise m_Noise; - /** Maximum size, in X/Z blocks, of the village (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the base (radius from the origin) */ int m_MaxSize; /** Borders of the vilalge - no item may reach out of this cuboid. */ diff --git a/src/Generating/UnderwaterBaseGen.h b/src/Generating/UnderwaterBaseGen.h index b24eb1075..9433a1905 100644 --- a/src/Generating/UnderwaterBaseGen.h +++ b/src/Generating/UnderwaterBaseGen.h @@ -34,7 +34,7 @@ protected: /** Maximum depth of the generator tree*/ int m_MaxDepth; - /** Maximum size, in X/Z blocks, of the base (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the structure (radius from the origin) */ int m_MaxSize; /** The underlying biome generator that defines whether the base is created or not */ diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp index f7d9a8316..27146e757 100644 --- a/src/Generating/VillageGen.cpp +++ b/src/Generating/VillageGen.cpp @@ -162,7 +162,7 @@ protected: /** The noise used as a pseudo-random generator */ cNoise m_Noise; - /** Maximum size, in X/Z blocks, of the village (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the village (radius from the origin) */ int m_MaxSize; /** The density for this village. Used to refrain from populating all house connectors. Range [0, 100] */ @@ -259,13 +259,13 @@ protected: // cPiecePool overrides: - virtual cPieces GetPiecesWithConnector(int a_ConnectorType) + virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override { return m_Prefabs.GetPiecesWithConnector(a_ConnectorType); } - virtual cPieces GetStartingPieces(void) + virtual cPieces GetStartingPieces(void) override { return m_Prefabs.GetStartingPieces(); } diff --git a/src/Generating/VillageGen.h b/src/Generating/VillageGen.h index ed8d739df..c384ed9e5 100644 --- a/src/Generating/VillageGen.h +++ b/src/Generating/VillageGen.h @@ -32,7 +32,7 @@ protected: /** Maximum depth of the generator tree*/ int m_MaxDepth; - /** Maximum size, in X/Z blocks, of the village (radius from the origin) */ + /** Maximum size, in X / Z blocks, of the village (radius from the origin) */ int m_MaxSize; /** Minimum density - percentage of allowed house connections. Range [0, 100] */ diff --git a/src/Globals.h b/src/Globals.h index bd180c08f..27d944fcc 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -18,9 +18,9 @@ // Useful warnings from warning level 4: #pragma warning(3 : 4189) // Local variable is initialized but not referenced - #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed/unsigned mismatch + #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed / unsigned mismatch #pragma warning(3 : 4310) // Cast truncates constant value - #pragma warning(3 : 4389) // Signed/unsigned mismatch + #pragma warning(3 : 4389) // Signed / unsigned mismatch #pragma warning(3 : 4505) // Unreferenced local function has been removed #pragma warning(3 : 4701) // Potentially unitialized local variable used #pragma warning(3 : 4702) // Unreachable code @@ -433,10 +433,14 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value) //temporary replacement for std::make_unique until we get c++14 -template <class T, class... Args> -std::unique_ptr<T> make_unique(Args&&... args) + +namespace cpp14 { - return std::unique_ptr<T>(new T(args...)); + template <class T, class... Args> + std::unique_ptr<T> make_unique(Args&&... args) + { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } } // a tick is 50 ms diff --git a/src/HTTPServer/EnvelopeParser.h b/src/HTTPServer/EnvelopeParser.h index e96d80abe..fe226d1f8 100644 --- a/src/HTTPServer/EnvelopeParser.h +++ b/src/HTTPServer/EnvelopeParser.h @@ -61,7 +61,7 @@ public: AString m_LastValue; - /** Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them */ + /** Notifies the callback of the key / value stored in m_LastKey / m_LastValue, then erases them */ void NotifyLast(void); /** Parses one line of header data. Returns true if successful */ diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp index de12b36ce..a5c6afd18 100644 --- a/src/HTTPServer/HTTPConnection.cpp +++ b/src/HTTPServer/HTTPConnection.cpp @@ -38,7 +38,9 @@ cHTTPConnection::~cHTTPConnection() void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) { - SendData(Printf("%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str())); + SendData(Printf("HTTP/1.1 %d %s\r\n", a_StatusCode, a_Response.c_str())); + SendData(Printf("Content-Length: %u\r\n\r\n", static_cast<unsigned>(a_Response.size()))); + SendData(a_Response.data(), a_Response.size()); m_State = wcsRecvHeaders; } diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h index 8ecc4a4d4..e1ebeb9ee 100644 --- a/src/HTTPServer/HTTPConnection.h +++ b/src/HTTPServer/HTTPConnection.h @@ -41,7 +41,8 @@ public: cHTTPConnection(cHTTPServer & a_HTTPServer); virtual ~cHTTPConnection(); - /** Sends HTTP status code together with a_Reason (used for HTTP errors) */ + /** Sends HTTP status code together with a_Reason (used for HTTP errors). + Sends the a_Reason as the body as well, so that browsers display it. */ void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); /** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */ diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp index c87b3cc8b..6762a22f8 100644 --- a/src/HTTPServer/HTTPMessage.cpp +++ b/src/HTTPServer/HTTPMessage.cpp @@ -202,7 +202,7 @@ size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_Size) m_IsValid = false; return AString::npos; } - // Check that there's HTTP/version at the end + // Check that there's HTTP / version at the end if (strncmp(m_IncomingHeaderData.c_str() + URLEnd + 1, "HTTP/1.", 7) != 0) { m_IsValid = false; diff --git a/src/IniFile.cpp b/src/IniFile.cpp index 3a213a90e..cd98cce57 100644 --- a/src/IniFile.cpp +++ b/src/IniFile.cpp @@ -1,11 +1,11 @@ // IniFile.cpp: Implementation of the CIniFile class. // Written by: Adam Clauss // Email: cabadam@houston.rr.com -// You may use this class/code as you wish in your programs. Feel free to distribute it, and +// You may use this class / code as you wish in your programs. Feel free to distribute it, and // email suggested changes to me. // // Rewritten by: Shane Hill -// Date: 21/08/2001 +// Date: 2001-08-21 // Email: Shane.Hill@dsto.defence.gov.au // Reason: Remove dependancy on MFC. Code should compile on any // platform. @@ -49,6 +49,9 @@ cIniFile::cIniFile(void) : bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) { + + m_Filename = a_FileName; + // Normally you would use ifstream, but the SGI CC compiler has // a few bugs with ifstream. So ... fstream used. fstream f; @@ -56,7 +59,8 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) AString keyname, valuename, value; AString::size_type pLeft, pRight; bool IsFromExampleRedirect = false; - + + f.open((FILE_IO_PREFIX + a_FileName).c_str(), ios::in); if (f.fail()) { @@ -650,7 +654,7 @@ void cIniFile::Clear(void) -bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) +bool cIniFile::HasValue(const AString & a_KeyName, const AString & a_ValueName) const { // Find the key: int keyID = FindKey(a_KeyName); @@ -889,8 +893,36 @@ void cIniFile::RemoveBom(AString & a_line) const +bool cIniFile::KeyExists(AString a_keyname) const +{ + return FindKey(a_keyname) != noID; +} + + + + + +std::vector<std::pair<AString, AString>> cIniFile::GetValues(AString a_keyName) +{ + std::vector<std::pair<AString, AString>> ret; + int keyID = FindKey(a_keyName); + if (keyID == noID) + { + return ret; + } + for (size_t valueID = 0; valueID < keys[keyID].names.size(); ++valueID) + { + ret.emplace_back(keys[keyID].names[valueID], keys[keyID].values[valueID]); + } + return ret; +} + + + + + AStringVector ReadUpgradeIniPorts( - cIniFile & a_IniFile, + cSettingsRepositoryInterface & a_Settings, const AString & a_KeyName, const AString & a_PortsValueName, const AString & a_OldIPv4ValueName, @@ -899,23 +931,34 @@ AStringVector ReadUpgradeIniPorts( ) { // Read the regular value, but don't use the default (in order to detect missing value for upgrade): - AStringVector Ports = StringSplitAndTrim(a_IniFile.GetValue(a_KeyName, a_PortsValueName), ";,"); + + AStringVector Ports; + + for (auto pair : a_Settings.GetValues(a_KeyName)) + { + if (pair.first != a_PortsValueName) + { + continue; + } + AStringVector temp = StringSplitAndTrim(pair.second, ";,"); + Ports.insert(Ports.end(), temp.begin(), temp.end()); + } if (Ports.empty()) { // Historically there were two separate entries for IPv4 and IPv6, merge them and migrate: - AString Ports4 = a_IniFile.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue); - AString Ports6 = a_IniFile.GetValue(a_KeyName, a_OldIPv6ValueName); + AString Ports4 = a_Settings.GetValue(a_KeyName, a_OldIPv4ValueName, a_DefaultValue); + AString Ports6 = a_Settings.GetValue(a_KeyName, a_OldIPv6ValueName); Ports = MergeStringVectors(StringSplitAndTrim(Ports4, ";,"), StringSplitAndTrim(Ports6, ";,")); - a_IniFile.DeleteValue(a_KeyName, a_OldIPv4ValueName); - a_IniFile.DeleteValue(a_KeyName, a_OldIPv6ValueName); + a_Settings.DeleteValue(a_KeyName, a_OldIPv4ValueName); + a_Settings.DeleteValue(a_KeyName, a_OldIPv6ValueName); // If those weren't present or were empty, use the default:" if (Ports.empty()) { Ports = StringSplitAndTrim(a_DefaultValue, ";,"); } - a_IniFile.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ',')); + a_Settings.SetValue(a_KeyName, a_PortsValueName, StringsConcat(Ports, ',')); } return Ports; @@ -923,4 +966,3 @@ AStringVector ReadUpgradeIniPorts( - diff --git a/src/IniFile.h b/src/IniFile.h index 3e717723f..2a4113fb4 100644 --- a/src/IniFile.h +++ b/src/IniFile.h @@ -1,14 +1,14 @@ // IniFile.cpp: Implementation of the CIniFile class. // Written by: Adam Clauss // Email: cabadam@tamu.edu -// You may use this class/code as you wish in your programs. Feel free to distribute it, and +// You may use this class / code as you wish in your programs. Feel free to distribute it, and // email suggested changes to me. // // Rewritten by: Shane Hill -// Date: 21/08/2001 +// Date: 2001-08-21 // Email: Shane.Hill@dsto.defence.gov.au // Reason: Remove dependancy on MFC. Code should compile on any -// platform. Tested on Windows/Linux/Irix +// platform. Tested on Windows / Linux / Irix //////////////////////////////////////////////////////////////////////////////// /* @@ -18,7 +18,7 @@ #pragma once - +#include "SettingsRepositoryInterface.h" #define MAX_KEYNAME 128 #define MAX_VALUENAME 128 @@ -30,10 +30,12 @@ // tolua_begin -class cIniFile +class cIniFile : public cSettingsRepositoryInterface { private: bool m_IsCaseInsensitive; + + AString m_Filename; struct key { @@ -53,15 +55,19 @@ private: void RemoveBom(AString & a_line) const; public: - - enum errors - { - noID = -1, - }; /// Creates a new instance with no data cIniFile(void); +// tolua_end + virtual ~cIniFile() = default; + + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override; + + virtual bool KeyExists(const AString a_keyName) const override; + +// tolua_begin + // Sets whether or not keynames and valuenames should be case sensitive. // The default is case insensitive. void CaseSensitive (void) { m_IsCaseInsensitive = false; } @@ -77,11 +83,13 @@ public: /// Writes data stored in class to the specified ini file bool WriteFile(const AString & a_FileName) const; + virtual bool Flush() override { return WriteFile(m_Filename); } + /// Deletes all stored ini data (but doesn't touch the file) void Clear(void); /** Returns true iff the specified value exists. */ - bool HasValue(const AString & a_KeyName, const AString & a_ValueName); + bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override; /// Returns index of specified key, or noID if not found int FindKey(const AString & keyname) const; @@ -93,7 +101,7 @@ public: int GetNumKeys(void) const { return (int)keys.size(); } /// Add a key name - int AddKeyName(const AString & keyname); + int AddKeyName(const AString & keyname) override; // Returns key names by index. AString GetKeyName(const int keyID) const; @@ -108,8 +116,8 @@ public: // Gets value of [keyname] valuename =. // Overloaded to return string, int, and double. - // Returns defValue if key/value not found. - AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const; + // Returns defValue if key / value not found. + AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override; AString GetValue (const int keyID, const int valueID, const AString & defValue = "") const; double GetValueF(const AString & keyname, const AString & valuename, const double defValue = 0) const; int GetValueI(const AString & keyname, const AString & valuename, const int defValue = 0) const; @@ -119,18 +127,18 @@ public: } // Gets the value; if not found, write the default to the INI file - AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = ""); + AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override; double GetValueSetF(const AString & keyname, const AString & valuename, const double defValue = 0.0); - int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0); - Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0); - bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) + int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override; + Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override; + bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override { return (GetValueSetI(keyname, valuename, defValue ? 1 : 0) != 0); } // Adds a new value to the specified key. // If a value of the same name already exists, creates another one (non-standard INI file) - void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value); + void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override; void AddValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value); void AddValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value) { @@ -143,8 +151,8 @@ public: // Returns true if value set, false otherwise. // Overloaded to accept string, int, and double. bool SetValue (const int keyID, const int valueID, const AString & value); - bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true); - bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true); + bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override; + bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override; bool SetValueI(const AString & a_Keyname, const AString & a_ValueName, const Int64 a_Value, const bool a_CreateIfNotExists = true); bool SetValueB(const AString & a_KeyName, const AString & a_ValueName, const bool a_Value, const bool a_CreateIfNotExists = true) { @@ -155,7 +163,7 @@ public: // Deletes specified value. // Returns true if value existed and deleted, false otherwise. bool DeleteValueByID(const int keyID, const int valueID); - bool DeleteValue(const AString & keyname, const AString & valuename); + bool DeleteValue(const AString & keyname, const AString & valuename) override; // Deletes specified key and all values contained within. // Returns true if key existed and deleted, false otherwise. @@ -196,15 +204,15 @@ public: bool AddKeyComment(const int keyID, const AString & comment); /// Add a key comment - bool AddKeyComment(const AString & keyname, const AString & comment); + bool AddKeyComment(const AString & keyname, const AString & comment) override; /// Return a key comment AString GetKeyComment(const int keyID, const int commentID) const; - AString GetKeyComment(const AString & keyname, const int commentID) const; + AString GetKeyComment(const AString & keyname, const int commentID) const override; // Delete a key comment. bool DeleteKeyComment(const int keyID, const int commentID); - bool DeleteKeyComment(const AString & keyname, const int commentID); + bool DeleteKeyComment(const AString & keyname, const int commentID) override; // Delete all comments for a key. bool DeleteKeyComments(const int keyID); @@ -217,12 +225,12 @@ public: -/** Reads the list of ports from the INI file, possibly upgrading from IPv4/IPv6-specific values into new version-agnostic value. +/** Reads the list of ports from the INI file, possibly upgrading from IPv4 / IPv6-specific values into new version-agnostic value. Reads the list of ports from a_PortsValueName. If that value doesn't exist or is empty, the list is combined from values in a_OldIPv4ValueName and a_OldIPv6ValueName; in this case the old values are removed from the INI file. If there is none of the three values or they are all empty, the default is used and stored in the Ports value. */ AStringVector ReadUpgradeIniPorts( - cIniFile & a_IniFile, + cSettingsRepositoryInterface & a_Settings, const AString & a_KeyName, const AString & a_PortsValueName, const AString & a_OldIPv4ValueName, diff --git a/src/Inventory.cpp b/src/Inventory.cpp index fba6f4aea..c595da5b0 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -499,7 +499,7 @@ int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum) #if 0 -bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */) +bool cInventory::AddToBar(cItem & a_Item, const int a_Offset, const int a_Size, bool * a_bChangedSlots, int a_Mode /* = 0 */) { // Fill already present stacks if (a_Mode < 2) diff --git a/src/Inventory.h b/src/Inventory.h index 311f64562..b2a8f658b 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -171,7 +171,7 @@ public: bool LoadFromJson(Json::Value & a_Value); protected: - bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0); + bool AddToBar(cItem & a_Item, const int a_Offset, const int a_Size, bool * a_bChangedSlots, int a_Mode = 0); cItemGrid m_ArmorSlots; cItemGrid m_InventorySlots; diff --git a/src/Items/ItemArmor.h b/src/Items/ItemArmor.h index 2436df5bd..252b94df9 100644 --- a/src/Items/ItemArmor.h +++ b/src/Items/ItemArmor.h @@ -17,8 +17,13 @@ public: { } + + /** Move the armor to the armor slot of the player's inventory */ - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { int SlotNum; if (ItemCategory::IsHelmet(a_Item.m_ItemType)) @@ -60,6 +65,8 @@ public: return true; } + + virtual bool CanRepairWithRawMaterial(short a_ItemType) override { switch (m_ItemType) diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h index 7faac1e32..452d86775 100644 --- a/src/Items/ItemBoat.h +++ b/src/Items/ItemBoat.h @@ -28,9 +28,12 @@ public: - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { - if ((a_Dir != BLOCK_FACE_YM) && (a_Dir != BLOCK_FACE_NONE)) + if ((a_BlockFace != BLOCK_FACE_YM) && (a_BlockFace != BLOCK_FACE_NONE)) { return false; } diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h index cf2fe9ad2..5164ddf59 100644 --- a/src/Items/ItemBow.h +++ b/src/Items/ItemBow.h @@ -26,8 +26,12 @@ public: { } + - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { ASSERT(a_Player != nullptr); @@ -40,6 +44,7 @@ public: a_Player->StartChargingBow(); return true; } + virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 871db821c..45b4030a3 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -23,13 +23,18 @@ public: } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { switch (m_ItemType) { - case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir); - case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA); - case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER); + case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, E_BLOCK_LAVA); + case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, E_BLOCK_WATER); default: { ASSERT(!"Unhandled ItemType"); @@ -40,7 +45,7 @@ public: - bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) { if (a_BlockFace != BLOCK_FACE_NONE) { @@ -75,6 +80,19 @@ public: return false; } + // Check to see if destination block is too far away + // Reach Distance Multiplayer = 5 Blocks + if ((BlockPos.x - a_Player->GetPosX() > 5) || (BlockPos.z - a_Player->GetPosZ() > 5)) + { + return false; + } + + // Remove water / lava block (unless plugins disagree) + if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0)) + { + return false; + } + // Give new bucket, filled with fluid when the gamemode is not creative: if (!a_Player->IsGameModeCreative()) { @@ -85,25 +103,40 @@ public: ASSERT(!"Inventory bucket mismatch"); return true; } - a_Player->GetInventory().AddItem(cItem(NewItem), true, true); + if (a_Player->GetInventory().AddItem(cItem(NewItem), true, true) != 1) + { + // The bucket didn't fit, toss it as a pickup: + a_Player->TossPickup(cItem(NewItem)); + } } - // Remove water / lava block - a_Player->GetWorld()->SetBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0); return true; } - bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_FluidBlock) + + bool PlaceFluid( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_FluidBlock + ) { if (a_BlockFace != BLOCK_FACE_NONE) { return false; } - BLOCKTYPE CurrentBlock; + BLOCKTYPE CurrentBlockType; + NIBBLETYPE CurrentBlockMeta; + eBlockFace EntryFace; Vector3i BlockPos; - if (!GetPlacementCoordsFromTrace(a_World, a_Player, BlockPos, CurrentBlock)) + if (!GetPlacementCoordsFromTrace(a_World, a_Player, BlockPos, CurrentBlockType, CurrentBlockMeta, EntryFace)) + { + return false; + } + + // Check to see if destination block is too far away + // Reach Distance Multiplayer = 5 Blocks + if ((BlockPos.x - a_Player->GetPosX() > 5) || (BlockPos.z - a_Player->GetPosZ() > 5)) { return false; } @@ -125,23 +158,29 @@ public: } // Wash away anything that was there prior to placing: - if (cFluidSimulator::CanWashAway(CurrentBlock)) + if (cFluidSimulator::CanWashAway(CurrentBlockType)) { - cBlockHandler * Handler = BlockHandler(CurrentBlock); + if (a_PluginInterface.CallHookPlayerBreakingBlock(*a_Player, BlockPos.x, BlockPos.y, BlockPos.z, EntryFace, CurrentBlockType, CurrentBlockMeta)) + { + // Plugin disagrees with the washing-away + return false; + } + + cBlockHandler * Handler = BlockHandler(CurrentBlockType); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); - cBlockInServerPluginInterface PluginInterface(*a_World); - Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ); + Handler->DropBlock(ChunkInterface, *a_World, a_PluginInterface, a_Player, BlockPos.x, BlockPos.y, BlockPos.z); } + a_PluginInterface.CallHookPlayerBrokenBlock(*a_Player, BlockPos.x, BlockPos.y, BlockPos.z, EntryFace, CurrentBlockType, CurrentBlockMeta); } - a_World->SetBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0); - - return true; + // Place the actual fluid block: + return a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0); } + bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos) { class cCallbacks : @@ -190,20 +229,25 @@ public: } - bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType) + + bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta, eBlockFace & a_BlockFace) { class cCallbacks : public cBlockTracer::cCallbacks { public: Vector3i m_Pos; - BLOCKTYPE m_ReplacedBlock; + BLOCKTYPE m_ReplacedBlockType; + NIBBLETYPE m_ReplacedBlockMeta; + eBlockFace m_EntryFace; virtual bool OnNextBlock(int a_CBBlockX, int a_CBBlockY, int a_CBBlockZ, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, char a_CBEntryFace) override { if (a_CBBlockType != E_BLOCK_AIR) { - m_ReplacedBlock = a_CBBlockType; + m_ReplacedBlockType = a_CBBlockType; + m_ReplacedBlockMeta = a_CBBlockMeta; + m_EntryFace = static_cast<eBlockFace>(a_CBEntryFace); if (!cFluidSimulator::CanWashAway(a_CBBlockType) && !IsBlockLiquid(a_CBBlockType)) { AddFaceDirection(a_CBBlockX, a_CBBlockY, a_CBBlockZ, (eBlockFace)a_CBEntryFace); // Was an unwashawayable block, can't overwrite it! @@ -219,12 +263,14 @@ public: Vector3d Start(a_Player->GetEyePosition()); Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5); - // cTracer::Trace returns true when whole line was traversed. By returning true when we hit something, we ensure that this never happens if liquid could be placed + // cTracer::Trace returns true when whole line was traversed. By returning true from the callback when we hit something, we ensure that this never happens if liquid could be placed // Use this to judge whether the position is valid if (!Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z)) { a_BlockPos = Callbacks.m_Pos; - a_BlockType = Callbacks.m_ReplacedBlock; + a_BlockType = Callbacks.m_ReplacedBlockType; + a_BlockMeta = Callbacks.m_ReplacedBlockMeta; + a_BlockFace = Callbacks.m_EntryFace; return true; } diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h index b6579c423..1d23975cd 100644 --- a/src/Items/ItemChest.h +++ b/src/Items/ItemChest.h @@ -78,7 +78,7 @@ public: !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) ) { - // Tried to place a block *into* another? + // Tried to place a block into another? // Happens when you place a block aiming at side of block with a torch on it or stem beside it return false; } diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index 524c49a5c..ddd3d4e20 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -40,7 +40,7 @@ public: } // The door needs a compatible block below it: - if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ), a_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ))) { return false; } diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h index bfcd0bac4..273af826a 100644 --- a/src/Items/ItemDye.h +++ b/src/Items/ItemDye.h @@ -19,7 +19,12 @@ public: { } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { // Handle growing the plants: if (a_Item.m_ItemDamage == E_META_DYE_WHITE) diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h index 9238d771b..6e944b4da 100644 --- a/src/Items/ItemEmptyMap.h +++ b/src/Items/ItemEmptyMap.h @@ -27,12 +27,17 @@ public: { } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { UNUSED(a_Item); UNUSED(a_BlockX); UNUSED(a_BlockZ); - UNUSED(a_Dir); + UNUSED(a_BlockFace); // The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it. @@ -60,3 +65,7 @@ public: return true; } } ; + + + + diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h index 6350a38ba..5bf4b29b7 100644 --- a/src/Items/ItemFishingRod.h +++ b/src/Items/ItemFishingRod.h @@ -93,9 +93,14 @@ public: { } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { - if (a_Dir != BLOCK_FACE_NONE) + if (a_BlockFace != BLOCK_FACE_NONE) { return false; } diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index 621cf9501..b7f89809e 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -72,7 +72,7 @@ cItemHandler * cItemHandler::m_ItemHandler[2268]; cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) { - if ((a_ItemType < 0) || ((size_t)a_ItemType >= ARRAYCOUNT(m_ItemHandler))) + if ((a_ItemType < 0) || (static_cast<size_t>(a_ItemType) >= ARRAYCOUNT(m_ItemHandler))) { // Either nothing (-1), or bad value, both cases should return the air handler if (a_ItemType < -1) @@ -287,7 +287,7 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType) void cItemHandler::Deinit() { - for (int i = 0; i < 2267; i++) + for (size_t i = 0; i < ARRAYCOUNT(m_ItemHandler); i++) { delete m_ItemHandler[i]; m_ItemHandler[i] = nullptr; @@ -336,7 +336,7 @@ bool cItemHandler::OnPlayerPlace( if ( BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) - ) + ) { cChunkInterface ChunkInterface(a_World.GetChunkMap()); BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); @@ -362,7 +362,7 @@ bool cItemHandler::OnPlayerPlace( !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) ) { - // Tried to place a block *into* another? + // Tried to place a block into another? // Happens when you place a block aiming at side of block with a torch on it or stem beside it return false; } @@ -411,15 +411,19 @@ bool cItemHandler::OnPlayerPlace( -bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) +bool cItemHandler::OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace +) { UNUSED(a_World); UNUSED(a_Player); + UNUSED(a_PluginInterface); UNUSED(a_Item); UNUSED(a_BlockX); UNUSED(a_BlockY); UNUSED(a_BlockZ); - UNUSED(a_Dir); + UNUSED(a_BlockFace); return false; } @@ -539,8 +543,9 @@ char cItemHandler::GetMaxStackSize(void) case E_ITEM_COMPASS: return 64; case E_ITEM_COOKED_CHICKEN: return 64; case E_ITEM_COOKED_FISH: return 64; - case E_ITEM_COOKED_PORKCHOP: return 64; case E_ITEM_COOKED_MUTTON: return 64; + case E_ITEM_COOKED_PORKCHOP: return 64; + case E_ITEM_COOKED_RABBIT: return 64; case E_ITEM_COOKIE: return 64; case E_ITEM_DARK_OAK_DOOR: return 64; case E_ITEM_DIAMOND: return 64; @@ -575,6 +580,7 @@ char cItemHandler::GetMaxStackSize(void) case E_ITEM_MELON_SEEDS: return 64; case E_ITEM_MELON_SLICE: return 64; case E_ITEM_NETHER_BRICK: return 64; + case E_ITEM_NETHER_QUARTZ: return 64; case E_ITEM_NETHER_WART: return 64; case E_ITEM_PAINTING: return 64; case E_ITEM_PAPER: return 64; @@ -591,6 +597,7 @@ char cItemHandler::GetMaxStackSize(void) case E_ITEM_RAW_FISH: return 64; case E_ITEM_RAW_MUTTON: return 64; case E_ITEM_RAW_PORKCHOP: return 64; + case E_ITEM_RAW_RABBIT: return 64; case E_ITEM_RED_APPLE: return 64; case E_ITEM_REDSTONE_DUST: return 64; case E_ITEM_REDSTONE_REPEATER: return 64; @@ -753,7 +760,7 @@ bool cItemHandler::GetPlacementBlockTypeMeta( return false; } - cBlockHandler * BlockH = BlockHandler((BLOCKTYPE)m_ItemType); + cBlockHandler * BlockH = BlockHandler(static_cast<BLOCKTYPE>(m_ItemType)); cChunkInterface ChunkInterface(a_World->GetChunkMap()); return BlockH->GetPlacementBlockTypeMeta( ChunkInterface, a_Player, diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index 3ac664798..ec88aeb99 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -4,6 +4,7 @@ #include "../Defines.h" #include "../Item.h" #include "../Entities/EntityEffect.h" +#include "../Blocks/BlockPluginInterface.h" @@ -56,8 +57,12 @@ public: ); - /** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */ - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir); + /** Called when the player tries to use the item (right mouse button). + Return false to abort the usage. DEFAULT: False */ + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ); /** Called when the client sends the SHOOT status in the lclk packet */ diff --git a/src/Items/ItemHoe.h b/src/Items/ItemHoe.h index ae3723323..ce6355ee7 100644 --- a/src/Items/ItemHoe.h +++ b/src/Items/ItemHoe.h @@ -18,9 +18,14 @@ public: { } - virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { - if ((a_Dir == BLOCK_FACE_NONE) || (a_BlockY >= cChunkDef::Height)) + if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockY >= cChunkDef::Height)) { return false; } diff --git a/src/Items/ItemItemFrame.h b/src/Items/ItemItemFrame.h index 5d22c1cb8..77a5bf47c 100644 --- a/src/Items/ItemItemFrame.h +++ b/src/Items/ItemItemFrame.h @@ -19,21 +19,26 @@ public: } - virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { - if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YP) || (a_Dir == BLOCK_FACE_YM)) + if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_YP) || (a_BlockFace == BLOCK_FACE_YM)) { // Client sends this if clicked on top or bottom face return false; } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir, true); // We want the clicked block, so go back again + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); // We want the clicked block, so go back again if (Block == E_BLOCK_AIR) { - cItemFrame * ItemFrame = new cItemFrame(a_Dir, a_BlockX, a_BlockY, a_BlockZ); + cItemFrame * ItemFrame = new cItemFrame(a_BlockFace, a_BlockX, a_BlockY, a_BlockZ); if (!ItemFrame->Initialize(*a_World)) { delete ItemFrame; diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h index 9f98bf85f..24641dce6 100644 --- a/src/Items/ItemLighter.h +++ b/src/Items/ItemLighter.h @@ -19,7 +19,12 @@ public: { } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { if (a_BlockFace < 0) { @@ -59,7 +64,7 @@ public: } default: { - // Light a fire next to/on top of the block if air: + // Light a fire next to / on top of the block if air: AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) { diff --git a/src/Items/ItemLilypad.h b/src/Items/ItemLilypad.h index b9d837384..7eede5864 100644 --- a/src/Items/ItemLilypad.h +++ b/src/Items/ItemLilypad.h @@ -29,7 +29,11 @@ public: } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { if (a_BlockFace > BLOCK_FACE_NONE) { @@ -67,7 +71,7 @@ public: cBlockInfo::FullyOccupiesVoxel(a_CBBlockType) ) { - // Can't place lilypad on air/in another block! + // Can't place lilypad on air / in another block! return true; } m_HasHitFluid = true; diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h index ed0a4711c..e7d2cf8cd 100644 --- a/src/Items/ItemMinecart.h +++ b/src/Items/ItemMinecart.h @@ -27,9 +27,12 @@ public: - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { - if (a_Dir < 0) + if (a_BlockFace < 0) { return false; } diff --git a/src/Items/ItemPainting.h b/src/Items/ItemPainting.h index d6f2e24b4..dd35931dd 100644 --- a/src/Items/ItemPainting.h +++ b/src/Items/ItemPainting.h @@ -19,15 +19,20 @@ public: { } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { - if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YM) || (a_Dir == BLOCK_FACE_YP)) + if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_YM) || (a_BlockFace == BLOCK_FACE_YP)) { // Paintings can't be flatly placed return false; } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); if (Block == E_BLOCK_AIR) @@ -65,7 +70,7 @@ public: { "BurningSkull" } }; - cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_Dir, a_BlockX, a_BlockY, a_BlockZ); + cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_BlockFace, a_BlockX, a_BlockY, a_BlockZ); Painting->Initialize(*a_World); if (!a_Player->IsGameModeCreative()) diff --git a/src/Items/ItemPotion.h b/src/Items/ItemPotion.h index 798573846..a176c591e 100644 --- a/src/Items/ItemPotion.h +++ b/src/Items/ItemPotion.h @@ -26,7 +26,10 @@ public: } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { short PotionDamage = a_Item.m_ItemDamage; diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h index dabbdbba1..3da93e2f1 100644 --- a/src/Items/ItemSign.h +++ b/src/Items/ItemSign.h @@ -25,7 +25,7 @@ public: cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ - ) + ) override { // If the regular placement doesn't work, do no further processing: if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h index b0b5ce005..3a78cc016 100644 --- a/src/Items/ItemSlab.h +++ b/src/Items/ItemSlab.h @@ -39,10 +39,15 @@ public: int a_CursorX, int a_CursorY, int a_CursorZ ) override { + // Prepare sound effect + AString PlaceSound = cBlockInfo::GetPlaceSound(m_ItemType); + float Volume = 1.0f, Pitch = 0.8f; + // Special slab handling - placing a slab onto another slab produces a dblslab instead: BLOCKTYPE ClickedBlockType; NIBBLETYPE ClickedBlockMeta; a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta); + // If clicked on a half slab directly if ( (ClickedBlockType == m_ItemType) && // Placing the same slab material ((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single) @@ -54,6 +59,7 @@ public: ((ClickedBlockMeta & 0x08) == 0) ) { + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07); } @@ -63,11 +69,28 @@ public: ((ClickedBlockMeta & 0x08) != 0) ) { + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07); } } + // Checking the type of block that should be placed + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + BLOCKTYPE PlaceBlockType; + NIBBLETYPE PlaceBlockMeta; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlockType, PlaceBlockMeta); + // If it's a slab combine into a doubleslab (means that clicked on side, top or bottom of a block adjacent to a half slab) + if ( + (PlaceBlockType == m_ItemType) && // Placing the same slab material + ((PlaceBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single) + ) + { + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07); + } + // The slabs didn't combine, use the default handler to place the slab: + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); /* diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index a07e4ef49..b67fe074d 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -19,7 +19,11 @@ public: } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { if (a_BlockFace < 0) { diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h index cdcbdab3b..0e06623fc 100644 --- a/src/Items/ItemThrowable.h +++ b/src/Items/ItemThrowable.h @@ -26,7 +26,11 @@ public: } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { Vector3d Pos = a_Player->GetThrowStartPos(); Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; @@ -128,7 +132,12 @@ public: { } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + + + virtual bool OnItemUse( + cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) override { if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) { @@ -149,3 +158,7 @@ public: } }; + + + + diff --git a/src/Logger.h b/src/Logger.h index 5e65de8a8..176c6e810 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -46,10 +46,10 @@ private: -extern void LOG(const char* a_Format, ...) FORMATSTRING(1, 2); -extern void LOGINFO(const char* a_Format, ...) FORMATSTRING(1, 2); -extern void LOGWARN(const char* a_Format, ...) FORMATSTRING(1, 2); -extern void LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2); +extern void LOG (const char * a_Format, ...) FORMATSTRING(1, 2); +extern void LOGINFO (const char * a_Format, ...) FORMATSTRING(1, 2); +extern void LOGWARN (const char * a_Format, ...) FORMATSTRING(1, 2); +extern void LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2); diff --git a/src/Map.cpp b/src/Map.cpp index 5e57cc8ec..5f296a5b2 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -591,7 +591,7 @@ void cMap::SendTo(cClientHandle & a_Client) for (unsigned int i = 0; i < m_Width; ++i) { - const Byte* Colors = &m_Data[i * m_Height]; + const Byte * Colors = &m_Data[i * m_Height]; a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height, m_Scale); } @@ -140,7 +140,7 @@ public: /** Update a circular region around the specified player. */ void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius); - /** Send next update packet to the specified player and remove invalid decorators/clients. */ + /** Send next update packet to the specified player and remove invalid decorators / clients. */ void UpdateClient(cPlayer * a_Player); // tolua_begin diff --git a/src/MemorySettingsRepository.cpp b/src/MemorySettingsRepository.cpp new file mode 100644 index 000000000..21b4c769c --- /dev/null +++ b/src/MemorySettingsRepository.cpp @@ -0,0 +1,312 @@ + +#include "Globals.h" + +#include "MemorySettingsRepository.h" + + + + +bool cMemorySettingsRepository::KeyExists(const AString keyname) const +{ + return m_Map.count(keyname) != 0; +} + + + + + +bool cMemorySettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + return false; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + return false; + } + return true; +} + + + + +int cMemorySettingsRepository::AddKeyName(const AString & a_keyname) +{ + m_Map.emplace(a_keyname, std::unordered_multimap<AString, sValue>{}); + return 0; +} + + + + + +bool cMemorySettingsRepository::AddKeyComment(const AString & keyname, const AString & comment) +{ + return false; +} + + + + + +AString cMemorySettingsRepository::GetKeyComment(const AString & keyname, const int commentID) const +{ + return ""; +} + + + + + +bool cMemorySettingsRepository::DeleteKeyComment(const AString & keyname, const int commentID) +{ + return false; +} + + + + + + +void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) +{ + if (m_Writable) + { + m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value)); + } +} + + + + +void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, Int64 a_Value) +{ + if (m_Writable) + { + m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value)); + } +} + + + + + +void cMemorySettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, bool a_Value) +{ + if (m_Writable) + { + m_Map[a_KeyName].emplace(a_ValueName, sValue(a_Value)); + } +} + + + + + +std::vector<std::pair<AString, AString>> cMemorySettingsRepository::GetValues(AString a_keyName) +{ + std::vector<std::pair<AString, AString>> ret; + for (auto pair : m_Map[a_keyName]) + { + ret.emplace_back(pair.first, pair.second.getStringValue()); + } + return ret; +} + + + + + +AString cMemorySettingsRepository::GetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + return defValue; + } + return iter->second.getStringValue(); +} + + + + + +AString cMemorySettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + return iter->second.getStringValue(); +} + + + + + +int cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue)); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(defValue)); + return defValue; + } + return static_cast<int>(iter->second.getIntValue()); +} + + + + + +Int64 cMemorySettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + return iter->second.getIntValue(); +} + + + + +bool cMemorySettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue) +{ + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + AddValue(a_KeyName, a_ValueName, defValue); + return defValue; + } + return iter->second.getBoolValue(); +} + + + + + +bool cMemorySettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists) +{ + if (!m_Writable) + { + return false; + } + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, a_Value); + } + return a_CreateIfNotExists; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, a_Value); + } + return a_CreateIfNotExists; + } + iter->second = sValue(a_Value); + return true; +} + + + + +bool cMemorySettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists) +{ + if (!m_Writable) + { + return false; + } + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value)); + } + return a_CreateIfNotExists; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + if (a_CreateIfNotExists) + { + AddValue(a_KeyName, a_ValueName, static_cast<Int64>(a_Value)); + } + return a_CreateIfNotExists; + } + iter->second = sValue(static_cast<Int64>(a_Value)); + return true; +} + + + + + +bool cMemorySettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName) +{ + if (!m_Writable) + { + return false; + } + auto outerIter = m_Map.find(a_KeyName); + if (outerIter == m_Map.end()) + { + return false; + } + auto iter = outerIter->second.find(a_ValueName); + if (iter == outerIter->second.end()) + { + return false; + } + outerIter->second.erase(iter); + return true; +} + +bool cMemorySettingsRepository::Flush() +{ + return true; +} + diff --git a/src/MemorySettingsRepository.h b/src/MemorySettingsRepository.h new file mode 100644 index 000000000..335bc4513 --- /dev/null +++ b/src/MemorySettingsRepository.h @@ -0,0 +1,80 @@ + +#pragma once + +#include "SettingsRepositoryInterface.h" + +#include <unordered_map> + +class cMemorySettingsRepository : public cSettingsRepositoryInterface +{ +public: + + virtual bool KeyExists(const AString keyname) const override; + + virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override; + + virtual int AddKeyName(const AString & keyname) override; + + virtual bool AddKeyComment(const AString & keyname, const AString & comment) override; + + virtual AString GetKeyComment(const AString & keyname, const int commentID) const override; + + virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override; + + virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override; + void AddValue (const AString & a_KeyName, const AString & a_ValueName, const Int64 a_Value); + void AddValue (const AString & a_KeyName, const AString & a_ValueName, const bool a_Value); + + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override; + + virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override; + + + virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override; + virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override; + virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override; + virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override; + + virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override; + virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override; + + virtual bool DeleteValue(const AString & keyname, const AString & valuename) override; + + virtual bool Flush() override; + + void SetReadOnly() + { + m_Writable = false; + } + +private: + + bool m_Writable = true; + + struct sValue + { + sValue(AString value) : m_Type(eType::String), m_stringValue (value) {} + sValue(Int64 value) : m_Type(eType::Int64), m_intValue(value) {} + sValue(bool value) : m_Type(eType::Bool), m_boolValue(value) {} + + AString getStringValue() const { ASSERT(m_Type == eType::String); return m_stringValue; } + Int64 getIntValue() const { ASSERT(m_Type == eType::Int64); return m_intValue; } + bool getBoolValue() const { ASSERT(m_Type == eType::Bool); return m_boolValue; } + private: + enum class eType + { + String, + Int64, + Bool + } m_Type; + AString m_stringValue; + union + { + Int64 m_intValue; + bool m_boolValue; + }; + }; + + std::unordered_map<AString, std::unordered_multimap<AString, sValue>> m_Map{}; +}; + diff --git a/src/MobCensus.cpp b/src/MobCensus.cpp index 4109aff07..1a69d8370 100644 --- a/src/MobCensus.cpp +++ b/src/MobCensus.cpp @@ -19,8 +19,8 @@ void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Dis bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily) { - const int ratio = 319; // This should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player - // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319 + const int ratio = 319; // This should be 256 as we are only supposed to take account from chunks that are in 17 x 17 from a player + // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19 * 19) / (17 * 17) = 319 // MG TODO : code the correct count if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily)) { diff --git a/src/MobCensus.h b/src/MobCensus.h index e3892bec6..3dc7b2a8e 100644 --- a/src/MobCensus.h +++ b/src/MobCensus.h @@ -32,8 +32,8 @@ public: // MG TODO : code the correct rule (not loaded chunk but short distant from players) void CollectSpawnableChunk(cChunk & a_Chunk); - /// Collect a mob - it's distance to player, it's family ... - void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance); + /// Collect a mob - its distance to player, its family ... + void CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance); /// Returns true if the family is capped (i.e. there are more mobs of this family than max) bool IsCapped(cMonster::eFamily a_MobFamily); diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp index cfd52440b..1bdac2f88 100644 --- a/src/MobProximityCounter.cpp +++ b/src/MobProximityCounter.cpp @@ -6,7 +6,11 @@ #include "Entities/Entity.h" #include "Chunk.h" -void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance) + + + + +void cMobProximityCounter::CollectMob(cEntity & a_Monster, cChunk & a_Chunk, double a_Distance) { // LOGD("Collecting monster %s, with distance %f", a_Monster->GetClass(), a_Distance); tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster); diff --git a/src/MobProximityCounter.h b/src/MobProximityCounter.h index 2dabeaa21..a3c0c125a 100644 --- a/src/MobProximityCounter.h +++ b/src/MobProximityCounter.h @@ -16,19 +16,19 @@ protected : // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster) struct sDistanceAndChunk { - sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(&a_Chunk) {} + sDistanceAndChunk(double a_Distance, cChunk & a_Chunk) : m_Distance(a_Distance), m_Chunk(&a_Chunk) {} double m_Distance; - cChunk* m_Chunk; + cChunk * m_Chunk; }; struct sMonsterAndChunk { - sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} - cEntity& m_Monster; - cChunk& m_Chunk; + sMonsterAndChunk(cEntity & a_Monster, cChunk & a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} + cEntity & m_Monster; + cChunk & m_Chunk; }; public : - typedef std::map<cEntity*, sDistanceAndChunk> tMonsterToDistance; + typedef std::map<cEntity *, sDistanceAndChunk> tMonsterToDistance; typedef std::multimap<double, sMonsterAndChunk> tDistanceToMonster; protected : @@ -50,7 +50,7 @@ public : // count a mob on a specified chunk with specified distance to an unkown player // if the distance is shortest than the one collected, this become the new closest // distance and the chunk become the "hosting" chunk (that is the one that will perform the action) - void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance); + void CollectMob(cEntity & a_Monster, cChunk & a_Chunk, double a_Distance); // return the mobs that are within the range of distance of the closest player they are // that means that if a mob is 30 m from a player and 150 m from another one. It will be diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index dfeab1461..468b0cfb2 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -321,9 +321,9 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R -cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize) +cMonster * cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int & a_MaxPackSize) { - cMonster* toReturn = nullptr; + cMonster * toReturn = nullptr; if (m_NewPack) { m_MobType = ChooseMobType(a_Biome); diff --git a/src/MobSpawner.h b/src/MobSpawner.h index e8b8f191b..8b651d6bf 100644 --- a/src/MobSpawner.h +++ b/src/MobSpawner.h @@ -39,7 +39,7 @@ public : // if this is the first of a Pack : determine the type of monster // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here // MaxPackSize is set to the maximal size for a pack this type of mob - cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize); + cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int & a_MaxPackSize); // mark the beginning of a new Pack // all mobs of the same Pack are the same type diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 526b39e39..648599999 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -36,11 +36,7 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt) return; } } - - if (!IsMovingToTargetPosition()) - { - MoveToPosition(m_Target->GetPosition()); - } + MoveToPosition(m_Target->GetPosition()); } } @@ -80,9 +76,11 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } cTracer LineOfSight(GetWorld()); - Vector3d AttackDirection(m_Target->GetPosition() - GetPosition()); + Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0); + Vector3d AttackDirection(m_Target->GetPosition() + Vector3d(0, m_Target->GetHeight(), 0) - MyHeadPosition); - if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length())) + + if (ReachedFinalDestination() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length()))) { // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) Attack(a_Dt); @@ -100,7 +98,7 @@ void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) { return; } - + // Setting this higher gives us more wiggle room for attackrate m_AttackInterval = 0.0; m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp index c072d4f48..e187e928a 100644 --- a/src/Mobs/Bat.cpp +++ b/src/Mobs/Bat.cpp @@ -9,6 +9,8 @@ cBat::cBat(void) : super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9) { + SetGravity(-2.0f); + SetAirDrag(0.05f); } diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 89eeb3709..d4ad24166 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -11,6 +11,8 @@ cBlaze::cBlaze(void) : super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.6, 1.8) { + SetGravity(-8.0f); + SetAirDrag(0.05f); } diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index 7a291dcf2..ffbcdf3ea 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -24,6 +24,7 @@ SET (SRCS Mooshroom.cpp PassiveAggressiveMonster.cpp PassiveMonster.cpp + Path.cpp Pig.cpp Rabbit.cpp Sheep.cpp @@ -62,6 +63,7 @@ SET (HDRS Ocelot.h PassiveAggressiveMonster.h PassiveMonster.h + Path.h Pig.h Rabbit.h Sheep.h diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index be283705e..27168ebae 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -26,7 +26,7 @@ public: bool IsEating (void) const {return m_bIsEating; } bool IsRearing (void) const {return m_bIsRearing; } bool IsMthOpen (void) const {return m_bIsMouthOpen; } - bool IsTame (void) const {return m_bIsTame; } + bool IsTame (void) const override {return m_bIsTame; } int GetHorseType (void) const {return m_Type; } int GetHorseColor (void) const {return m_Color; } int GetHorseStyle (void) const {return m_Style; } diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp index dae4615e4..b0e76daca 100644 --- a/src/Mobs/IronGolem.cpp +++ b/src/Mobs/IronGolem.cpp @@ -8,7 +8,7 @@ cIronGolem::cIronGolem(void) : - super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) + super("IronGolem", mtIronGolem, "mob.irongolem.hit", "mob.irongolem.death", 1.4, 2.9) { } diff --git a/src/Mobs/MagmaCube.cpp b/src/Mobs/MagmaCube.cpp index 3e9abc108..c5dd0def0 100644 --- a/src/Mobs/MagmaCube.cpp +++ b/src/Mobs/MagmaCube.cpp @@ -7,7 +7,7 @@ cMagmaCube::cMagmaCube(int a_Size) : - super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), + super("MagmaCube", mtMagmaCube, Printf("mob.magmacube.%s", GetSizeName(a_Size).c_str()), Printf("mob.magmacube.%s", GetSizeName(a_Size).c_str()), 0.6 * a_Size, 0.6 * a_Size), m_Size(a_Size) { } @@ -27,4 +27,14 @@ void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer) - +AString cMagmaCube::GetSizeName(int a_Size) +{ + if (a_Size > 1) + { + return "big"; + } + else + { + return "small"; + } +} diff --git a/src/Mobs/MagmaCube.h b/src/Mobs/MagmaCube.h index d66ea423a..b914dc867 100644 --- a/src/Mobs/MagmaCube.h +++ b/src/Mobs/MagmaCube.h @@ -19,10 +19,14 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; int GetSize(void) const { return m_Size; } + + /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds. + Returns either "big" or "small". */ + static AString GetSizeName(int a_Size); protected: - /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest + /// Size of the MagmaCube, 1, 2 and 4, with 1 being the smallest int m_Size; } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index a86497753..1da4124ed 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -13,7 +13,7 @@ #include "../Chunk.h" #include "../FastRandom.h" - +#include "Path.h" @@ -38,6 +38,7 @@ static const struct {mtEnderman, "enderman", "Enderman"}, {mtEnderDragon, "enderdragon", "EnderDragon"}, {mtGhast, "ghast", "Ghast"}, + {mtGiant, "giant", "Giant"}, {mtGuardian, "guardian", "Guardian"}, {mtHorse, "horse", "EntityHorse"}, {mtIronGolem, "irongolem", "VillagerGolem"}, @@ -73,8 +74,12 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_EMState(IDLE) , m_EMPersonality(AGGRESSIVE) , m_Target(nullptr) - , m_bMovingToDestination(false) + , m_Path(nullptr) + , m_IsFollowingPath(false) + , m_GiveUpCounter(0) + , m_TicksSinceLastPathReset(1000) , m_LastGroundHeight(POSY_TOINT) + , m_JumpCoolDown(0) , m_IdleInterval(0) , m_DestroyTimer(0) , m_MobType(a_MobType) @@ -84,7 +89,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_SoundDeath(a_SoundDeath) , m_AttackRate(3) , m_AttackDamage(1) - , m_AttackRange(2) + , m_AttackRange(1) , m_AttackInterval(0) , m_SightDistance(25) , m_DropChanceWeapon(0.085f) @@ -93,8 +98,9 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_DropChanceLeggings(0.085f) , m_DropChanceBoots(0.085f) , m_CanPickUpLoot(true) + , m_TicksSinceLastDamaged(100) , m_BurnsInDaylight(false) - , m_RelativeWalkSpeed(1.0) + , m_RelativeWalkSpeed(1) { if (!a_ConfigName.empty()) { @@ -115,89 +121,159 @@ void cMonster::SpawnOn(cClientHandle & a_Client) -void cMonster::TickPathFinding() +bool cMonster::TickPathFinding(cChunk & a_Chunk) { - const int PosX = POSX_TOINT; - const int PosY = POSY_TOINT; - const int PosZ = POSZ_TOINT; - - std::vector<Vector3d> m_PotentialCoordinates; - m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ)); - - static const struct // Define which directions to try to move to + if (!m_IsFollowingPath) { - int x, z; - } gCrossCoords[] = + return false; + } + if (m_TicksSinceLastPathReset < 1000) { - { 1, 0}, - {-1, 0}, - { 0, 1}, - { 0, -1}, - } ; - - if ((PosY - 1 < 0) || (PosY + 2 >= cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */) + // No need to count beyond 1000. 1000 is arbitary here. + ++m_TicksSinceLastPathReset; + } + + if (ReachedFinalDestination()) { - // Too low/high, can't really do anything - FinishPathFinding(); - return; + StopMovingToPosition(); + return false; } - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + if ((m_FinalDestination - m_PathFinderDestination).Length() > 0.25) // if the distance between where we're going and where we should go is too big. { - if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ))) + /* If we reached the last path waypoint, + Or if we haven't re-calculated for too long. + Interval is proportional to distance squared, and its minimum is 10. + (Recalculate lots when close, calculate rarely when far) */ + if ( + ((GetPosition() - m_PathFinderDestination).Length() < 0.25) || + ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength()))) + ) { - continue; + /* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating. + This is a workaround till we get better path recalculation. */ + if (!m_NoPathToTarget) + { + ResetPathFinding(); + } } + } - BLOCKTYPE BlockAtY = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ); - int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtLowestY = (LowestY >= cChunkDef::Height) ? E_BLOCK_AIR : m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ); + if (m_Path == nullptr) + { + if (!EnsureProperDestination(a_Chunk)) + { + StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement. + return false; + } + m_NoPathToTarget = false; + m_NoMoreWayPoints = false; + m_PathFinderDestination = m_FinalDestination; + m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight()); + } - if ( - (!cBlockInfo::IsSolid(BlockAtY)) && - (!cBlockInfo::IsSolid(BlockAtYP)) && - (!IsBlockLava(BlockAtLowestY)) && - (BlockAtLowestY != E_BLOCK_CACTUS) && - (PosY - LowestY < FALL_DAMAGE_HEIGHT) - ) + switch (m_Path->Step(a_Chunk)) + { + case ePathFinderStatus::NEARBY_FOUND: { - m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ)); + m_NoPathToTarget = true; + m_PathFinderDestination = m_Path->AcceptNearbyPath(); + break; } - else if ( - (cBlockInfo::IsSolid(BlockAtY)) && - (BlockAtY != E_BLOCK_CACTUS) && - (!cBlockInfo::IsSolid(BlockAtYP)) && - (!cBlockInfo::IsSolid(BlockAtYPP)) && - (BlockAtY != E_BLOCK_FENCE) && - (BlockAtY != E_BLOCK_FENCE_GATE) - ) + + case ePathFinderStatus::PATH_NOT_FOUND: + { + StopMovingToPosition(); // Try to calculate a path again. + // Note that the next time may succeed, e.g. if a player breaks a barrier. + break; + } + case ePathFinderStatus::CALCULATING: { - m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ)); + // Pathfinder needs more time + break; + } + case ePathFinderStatus::PATH_FOUND: + { + if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0)) + { + ResetPathFinding(); // Try to calculate a path again. + return false; + } + else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition? + { + if ((m_Path->IsFirstPoint() || ReachedNextWaypoint())) + { + m_NextWayPointPosition = m_Path->GetNextPoint(); + m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition. + } + } + else + { + m_NoMoreWayPoints = true; + } + return true; } } - if (!m_PotentialCoordinates.empty()) + return false; +} + + + + + +void cMonster::MoveToWayPoint(cChunk & a_Chunk) +{ + if (m_JumpCoolDown == 0) { - Vector3f ShortestCoords = m_PotentialCoordinates.front(); - for (std::vector<Vector3d>::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr) + if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) { - Vector3f Distance = m_FinalDestination - ShortestCoords; - Vector3f Distance2 = m_FinalDestination - *itr; - if (Distance.SqrLength() > Distance2.SqrLength()) + if ( + (IsOnGround() && (GetSpeedX() == 0) && (GetSpeedY() == 0)) || + (IsSwimming() && (m_GiveUpCounter < 15)) + ) { - ShortestCoords = *itr; + m_bOnGround = false; + m_JumpCoolDown = 20; + // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport + AddPosY(1.6); // Jump!! + SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed. + SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error and 1.6 and 3.2 are perfect. } } - - m_Destination = ShortestCoords; - m_Destination.z += 0.5f; - m_Destination.x += 0.5f; } else { - FinishPathFinding(); + --m_JumpCoolDown; + } + + Vector3d Distance = m_NextWayPointPosition - GetPosition(); + if ((Distance.x != 0) || (Distance.z != 0)) + { + Distance.y = 0; + Distance.Normalize(); + + if (m_bOnGround) + { + Distance *= 2.5f; + } + else if (IsSwimming()) + { + Distance *= 1.3f; + } + else + { + // Don't let the mob move too much if he's falling. + Distance *= 0.25f; + } + // Apply walk speed: + Distance *= m_RelativeWalkSpeed; + /* Reduced default speed. + Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence + better pathfinding. */ + Distance *= 0.5; + AddSpeedX(Distance.x); + AddSpeedZ(Distance.z); } } @@ -205,47 +281,130 @@ void cMonster::TickPathFinding() -void cMonster::MoveToPosition(const Vector3d & a_Position) +bool cMonster::EnsureProperDestination(cChunk & a_Chunk) { - FinishPathFinding(); + cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z)); + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return false; + } + + int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width; + int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width; + + // If destination in the air, first try to go 1 block north, or east, or west. + // This fixes the player leaning issue. + // If that failed, we instead go down to the lowest air block. + Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta); + if (!cBlockInfo::IsSolid(BlockType)) + { + bool InTheAir = true; + int x, z; + for (z = -1; z <= 1; ++z) + { + for (x = -1; x <= 1; ++x) + { + if ((x==0) && (z==0)) + { + continue; + } + Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z)); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return false; + } + RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width; + RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width; + Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta); + if (cBlockInfo::IsSolid(BlockType)) + { + m_FinalDestination.x += x; + m_FinalDestination.z += z; + InTheAir = false; + goto breakBothLoops; + } + } + } + breakBothLoops: + + // Go down to the lowest air block. + if (InTheAir) + { + while (m_FinalDestination.y > 0) + { + Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta); + if (cBlockInfo::IsSolid(BlockType)) + { + break; + } + m_FinalDestination.y -= 1; + } + } + } + + // If destination in water, go up to the highest water block. + // If destination in solid, go up to first air block. + bool InWater = false; + while (m_FinalDestination.y < cChunkDef::Height) + { + Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta); + if (BlockType == E_BLOCK_STATIONARY_WATER) + { + InWater = true; + } + else if (cBlockInfo::IsSolid(BlockType)) + { + InWater = false; + } + else + { + break; + } + m_FinalDestination.y += 1; + } + if (InWater) + { + m_FinalDestination.y -= 1; + } - m_FinalDestination = a_Position; - m_bMovingToDestination = true; - TickPathFinding(); + + return true; } -bool cMonster::IsCoordinateInTraversedList(Vector3i a_Coords) + + +void cMonster::MoveToPosition(const Vector3d & a_Position) { - return (std::find(m_TraversedCoordinates.begin(), m_TraversedCoordinates.end(), a_Coords) != m_TraversedCoordinates.end()); + m_FinalDestination = a_Position; + m_IsFollowingPath = true; } -bool cMonster::ReachedDestination() +void cMonster::StopMovingToPosition() { - if ((m_Destination - GetPosition()).Length() < 0.5f) - { - return true; - } - - return false; + m_IsFollowingPath = false; } -bool cMonster::ReachedFinalDestination() + +void cMonster::ResetPathFinding(void) { - if ((GetPosition() - m_FinalDestination).Length() <= m_AttackRange) + m_TicksSinceLastPathReset = 0; + if (m_Path != nullptr) { - return true; + delete m_Path; + m_Path = nullptr; } - - return false; } @@ -255,10 +414,11 @@ bool cMonster::ReachedFinalDestination() void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); + GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); if (m_Health <= 0) { - // The mob is dead, but we're still animating the "puff" they leave when they die + // The mob is dead, but we're still animating the "puff" they leave when they die. m_DestroyTimer += a_Dt; if (m_DestroyTimer > std::chrono::seconds(1)) { @@ -267,73 +427,36 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } + if (m_TicksSinceLastDamaged < 100) + { + ++m_TicksSinceLastDamaged; + } if ((m_Target != nullptr) && m_Target->IsDestroyed()) { m_Target = nullptr; } - // Burning in daylight - HandleDaylightBurning(a_Chunk); - - if (m_bMovingToDestination) + // Process the undead burning in daylight. + HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk)); + if (TickPathFinding(*Chunk)) { - if (m_bOnGround) - { - if (DoesPosYRequireJump((int)floor(m_Destination.y))) - { - m_bOnGround = false; - - // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport - AddPosY(1.2); // Jump!! - } - } - - Vector3d Distance = m_Destination - GetPosition(); - if (!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move + /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true: + 1. I am idle + 2. I was not hurt by a player recently. + Then STOP. */ + if ( + m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) && + WouldBurnAt(m_NextWayPointPosition, *Chunk) && + !WouldBurnAt(GetPosition(), *Chunk) + ) { - Distance.y = 0; - Distance.Normalize(); - - if (m_bOnGround) - { - Distance *= 2.5f; - } - else if (IsSwimming()) - { - Distance *= 1.3f; - } - else - { - // Don't let the mob move too much if he's falling. - Distance *= 0.25f; - } - - // Apply walk speed: - Distance *= m_RelativeWalkSpeed; - - AddSpeedX(Distance.x); - AddSpeedZ(Distance.z); - - // It's too buggy! - /* - if (m_EMState == ESCAPING) - { - // Runs Faster when escaping :D otherwise they just walk away - SetSpeedX (GetSpeedX() * 2.f); - SetSpeedZ (GetSpeedZ() * 2.f); - } - */ + // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently: + StopMovingToPosition(); + m_GiveUpCounter = 40; // This doesn't count as giving up, keep the giveup timer as is. } else { - if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate - { - FinishPathFinding(); - } - else - { - TickPathFinding(); // We have reached the next point in our path, calculate another point - } + MoveToWayPoint(*Chunk); } } @@ -344,13 +467,13 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { case IDLE: { - // If enemy passive we ignore checks for player visibility + // If enemy passive we ignore checks for player visibility. InStateIdle(a_Dt); break; } case CHASING: { - // If we do not see a player anymore skip chasing action + // If we do not see a player anymore skip chasing action. InStateChasing(a_Dt); break; } @@ -359,7 +482,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) InStateEscaping(a_Dt); break; } - case ATTACKING: break; } // switch (m_EMState) @@ -369,6 +491,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) + void cMonster::SetPitchAndYawFromDestination() { Vector3d FinalDestination = m_FinalDestination; @@ -376,31 +499,36 @@ void cMonster::SetPitchAndYawFromDestination() { if (m_Target->IsPlayer()) { - FinalDestination.y = ((cPlayer *)m_Target)->GetStance(); + FinalDestination.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1; } else { - FinalDestination.y = GetHeight(); + FinalDestination.y = m_Target->GetPosY() + GetHeight(); } } + + + Vector3d BodyDistance = m_NextWayPointPosition - GetPosition(); + double BodyRotation, BodyPitch; + BodyDistance.Normalize(); + VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch); + SetYaw(BodyRotation); + Vector3d Distance = FinalDestination - GetPosition(); - if (Distance.SqrLength() > 0.1f) { + double HeadRotation, HeadPitch; + Distance.Normalize(); + VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch); + if (std::abs(BodyRotation - HeadRotation) < 120) { - double Rotation, Pitch; - Distance.Normalize(); - VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch); - SetHeadYaw(Rotation); - SetPitch(-Pitch); + SetHeadYaw(HeadRotation); + SetPitch(-HeadPitch); } - + else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking. { - Vector3d BodyDistance = m_Destination - GetPosition(); - double Rotation, Pitch; - Distance.Normalize(); - VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch); - SetYaw(Rotation); + SetHeadYaw(BodyRotation); + SetPitch(-BodyPitch); } } } @@ -408,6 +536,7 @@ void cMonster::SetPitchAndYawFromDestination() + void cMonster::HandleFalling() { if (m_bOnGround) @@ -459,7 +588,6 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) - bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { if (!super::DoTakeDamage(a_TDI)) @@ -475,6 +603,7 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) if (a_TDI.Attacker != nullptr) { m_Target = a_TDI.Attacker; + m_TicksSinceLastDamaged = 0; } return true; } @@ -640,7 +769,7 @@ void cMonster::EventLosePlayer(void) void cMonster::InStateIdle(std::chrono::milliseconds a_Dt) { - if (m_bMovingToDestination) + if (m_IsFollowingPath) { return; // Still getting there } @@ -660,14 +789,8 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt) if ((Dist.SqrLength() > 2) && (rem >= 3)) { Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z); - - int NextHeight = FindFirstNonAirBlockPosition(Destination.x, Destination.z); - - if (IsNextYPosReachable(NextHeight)) - { - Destination.y = NextHeight; - MoveToPosition(Destination); - } + Destination.y = FindFirstNonAirBlockPosition(Destination.x, Destination.z); + MoveToPosition(Destination); } } } @@ -691,7 +814,7 @@ void cMonster::InStateChasing(std::chrono::milliseconds a_Dt) void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); - + if (m_Target != nullptr) { Vector3d newloc = GetPosition(); @@ -770,7 +893,7 @@ AString cMonster::MobTypeToString(eMonsterType a_MobType) return g_MobTypeNames[i].m_lcName; } } - + // Not found: return ""; } @@ -865,7 +988,7 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtWolf: return mfHostile; case mtZombie: return mfHostile; case mtZombiePigman: return mfHostile; - + case mtInvalidType: break; } ASSERT(!"Unhandled mob type"); @@ -905,7 +1028,7 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) { case mtMagmaCube: { - toReturn = new cMagmaCube(Random.NextInt(2) + 1); + toReturn = new cMagmaCube(1 << Random.NextInt(3)); // Size 1, 2 or 4 break; } case mtSlime: @@ -1040,7 +1163,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) a_Drops.push_back(GetEquippedHelmet()); } } - + if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2))) { if (!GetEquippedChestplate().IsEmpty()) @@ -1048,7 +1171,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) a_Drops.push_back(GetEquippedChestplate()); } } - + if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2))) { if (!GetEquippedLeggings().IsEmpty()) @@ -1056,7 +1179,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) a_Drops.push_back(GetEquippedLeggings()); } } - + if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2))) { if (!GetEquippedBoots().IsEmpty()) @@ -1086,36 +1209,26 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel) -void cMonster::HandleDaylightBurning(cChunk & a_Chunk) +void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn) { if (!m_BurnsInDaylight) { return; } - + int RelY = POSY_TOINT; if ((RelY < 0) || (RelY >= cChunkDef::Height)) { // Outside the world return; } - - int RelX = POSX_TOINT - GetChunkX() * cChunkDef::Width; - int RelZ = POSZ_TOINT - GetChunkZ() * cChunkDef::Width; - if (!a_Chunk.IsLightValid()) { m_World->QueueLightChunk(GetChunkX(), GetChunkZ()); return; } - if ( - (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight - (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime - !IsOnFire() && // Not already burning - GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining - ) + if (!IsOnFire() && WouldBurn) { // Burn for 100 ticks, then decide again StartBurning(100); @@ -1125,11 +1238,35 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk) -cMonster::eFamily cMonster::GetMobFamily(void) const +bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) { - return FamilyFromType(m_MobType); + cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Location.x), FloorC(a_Location.z)); + if ((Chunk == nullptr) || (!Chunk->IsValid())) + { + return false; + } + + int RelX = FloorC(a_Location.x) - Chunk->GetPosX() * cChunkDef::Width; + int RelY = FloorC(a_Location.y); + int RelZ = FloorC(a_Location.z) - Chunk->GetPosZ() * cChunkDef::Width; + + if ( + (Chunk->GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight + (Chunk->GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand + (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime + GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining + ) + { + return true; + } + return false; } + +cMonster::eFamily cMonster::GetMobFamily(void) const +{ + return FamilyFromType(m_MobType); +} diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 21ed0c25a..c4043b0e5 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -10,11 +10,12 @@ - - class cClientHandle; class cWorld; +// Fwd: cPath +enum class ePathFinderStatus; +class cPath; @@ -60,8 +61,9 @@ public: virtual void OnRightClicked(cPlayer & a_Player) override; + /** Engage pathfinder and tell it to calculate a path to a given position, and move the mobile accordingly + Currently, the mob will only start moving to a new position after the position it is currently going to is reached. */ virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export - virtual bool ReachedDestination(void); // tolua_begin eMonsterType GetMobType(void) const { return m_MobType; } @@ -158,19 +160,32 @@ public: protected: - /* ======= PATHFINDING ======= */ - /** A pointer to the entity this mobile is aiming to reach */ cEntity * m_Target; + cPath * m_Path; // TODO unique ptr + + /** Stores if mobile is currently moving towards the ultimate, final destination */ + bool m_IsFollowingPath; + + /* If 0, will give up reaching the next m_NextWayPointPosition and will re-compute path. */ + int m_GiveUpCounter; + int m_TicksSinceLastPathReset; + /** Coordinates of the next position that should be reached */ - Vector3d m_Destination; + Vector3d m_NextWayPointPosition; + /** Coordinates for the ultimate, final destination. */ Vector3d m_FinalDestination; - /** Returns if the ultimate, final destination has been reached */ - bool ReachedFinalDestination(void); - /** Stores if mobile is currently moving towards the ultimate, final destination */ - bool m_bMovingToDestination; + /** Coordinates for the ultimate, final destination last given to the pathfinder. */ + Vector3d m_PathFinderDestination; + + /** True if there's no path to target and we're walking to an approximated location. */ + bool m_NoPathToTarget; + + /** Whether The mob has finished their path, note that this does not imply reaching the destination, + the destination may sometimes differ from the current path. */ + bool m_NoMoreWayPoints; /** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does) If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1 @@ -178,44 +193,50 @@ protected: If no suitable position is found, returns cChunkDef::Height. */ int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ); - /** Returns if a monster can actually reach a given height by jumping or walking */ - inline bool IsNextYPosReachable(int a_PosY) - { - return ( - (a_PosY <= POSY_TOINT) || - DoesPosYRequireJump(a_PosY) - ); - } + /** Returns if the ultimate, final destination has been reached */ + bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); } + + /** Returns if the intermediate waypoint of m_NextWayPointPosition has been reached */ + bool ReachedNextWaypoint(void) { return ((m_NextWayPointPosition - GetPosition()).SqrLength() < 0.25); } + /** Returns if a monster can reach a given height by jumping */ inline bool DoesPosYRequireJump(int a_PosY) { return ((a_PosY > POSY_TOINT) && (a_PosY == POSY_TOINT + 1)); } - /** A semi-temporary list to store the traversed coordinates during active pathfinding so we don't visit them again */ - std::vector<Vector3i> m_TraversedCoordinates; - /** Returns if coordinate is in the traversed list */ - bool IsCoordinateInTraversedList(Vector3i a_Coords); + /** Finds the next place to go by calculating a path and setting the m_NextWayPointPosition variable for the next block to head to + This is based on the ultimate, final destination and the current position, as well as the A* algorithm, and any environmental hazards + Returns if a path is ready, and therefore if the mob should move to m_NextWayPointPosition + */ + bool TickPathFinding(cChunk & a_Chunk); - /** Finds the next place to go - This is based on the ultimate, final destination and the current position, as well as the traversed coordinates, and any environmental hazards */ - void TickPathFinding(void); - /** Finishes a pathfinding task, be it due to failure or something else */ - inline void FinishPathFinding(void) - { - m_TraversedCoordinates.clear(); - m_bMovingToDestination = false; - } - /** Sets the body yaw and head yaw/pitch based on next/ultimate destinations */ - void SetPitchAndYawFromDestination(void); + /** Move in a straight line to the next waypoint in the path, will jump if needed. */ + void MoveToWayPoint(cChunk & a_Chunk); - /* =========================== */ - /* ========= FALLING ========= */ + /** Ensures the destination is not buried underground or under water. Also ensures the destination is not in the air. + Only the Y coordinate of m_FinalDestination might be changed. + 1. If m_FinalDestination is the position of a water block, m_FinalDestination's Y will be modified to point to the heighest water block in the pool in the current column. + 2. If m_FinalDestination is the position of a solid, m_FinalDestination's Y will be modified to point to the first airblock above the solid in the current column. + 3. If m_FinalDestination is the position of an air block, Y will keep decreasing until hitting either a solid or water. + Now either 1 or 2 is performed. */ + bool EnsureProperDestination(cChunk & a_Chunk); + + /** Resets a pathfinding task, be it due to failure or something else + Resets the pathfinder. If m_IsFollowingPath is true, TickPathFinding starts a brand new path. + Should only be called by the pathfinder, cMonster::Tick or StopMovingToPosition. */ + void ResetPathFinding(void); + + /** Stops pathfinding + Calls ResetPathFinding and sets m_IsFollowingPath to false */ + void StopMovingToPosition(); + + /** Sets the body yaw and head yaw / pitch based on next / ultimate destinations */ + void SetPitchAndYawFromDestination(void); virtual void HandleFalling(void); int m_LastGroundHeight; - - /* =========================== */ + int m_JumpCoolDown; std::chrono::milliseconds m_IdleInterval; std::chrono::milliseconds m_DestroyTimer; @@ -239,10 +260,11 @@ protected: float m_DropChanceLeggings; float m_DropChanceBoots; bool m_CanPickUpLoot; + int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player? - void HandleDaylightBurning(cChunk & a_Chunk); + void HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn); + bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); bool m_BurnsInDaylight; - double m_RelativeWalkSpeed; /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops*/ diff --git a/src/Mobs/Path.cpp b/src/Mobs/Path.cpp new file mode 100644 index 000000000..6f3d43305 --- /dev/null +++ b/src/Mobs/Path.cpp @@ -0,0 +1,491 @@ + +#include "Globals.h" + +#include <cmath> + +#include "Path.h" +#include "../Chunk.h" + +#define JUMP_G_COST 20 + +#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed. +#define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate. +#define CALCULATIONS_PER_STEP 10 // Higher means more CPU load but faster path calculations. +// The only version which guarantees the shortest path is 0, 0. + + + + + + + + +bool compareHeuristics::operator()(cPathCell * & a_Cell1, cPathCell * & a_Cell2) +{ + return a_Cell1->m_F > a_Cell2->m_F; +} + + + + + +/* cPath implementation */ +cPath::cPath( + cChunk & a_Chunk, + const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps, + double a_BoundingBoxWidth, double a_BoundingBoxHeight, + int a_MaxUp, int a_MaxDown +) : + + m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint + m_Chunk(&a_Chunk), + m_BadChunkFound(false) +{ + // TODO: if src not walkable OR dest not walkable, then abort. + // Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable + + a_BoundingBoxWidth = 1; // Until we improve physics, if ever. + + m_BoundingBoxWidth = ceil(a_BoundingBoxWidth); + m_BoundingBoxHeight = ceil(a_BoundingBoxHeight); + m_HalfWidth = a_BoundingBoxWidth / 2; + + int HalfWidthInt = a_BoundingBoxWidth / 2; + m_Source.x = floor(a_StartingPoint.x - HalfWidthInt); + m_Source.y = floor(a_StartingPoint.y); + m_Source.z = floor(a_StartingPoint.z - HalfWidthInt); + + m_Destination.x = floor(a_EndingPoint.x - HalfWidthInt); + m_Destination.y = floor(a_EndingPoint.y); + m_Destination.z = floor(a_EndingPoint.z - HalfWidthInt); + + if (GetCell(m_Source)->m_IsSolid || GetCell(m_Destination)->m_IsSolid) + { + m_Status = ePathFinderStatus::PATH_NOT_FOUND; + return; + } + + m_NearestPointToTarget = GetCell(m_Source); + m_Status = ePathFinderStatus::CALCULATING; + m_StepsLeft = a_MaxSteps; + + ProcessCell(GetCell(a_StartingPoint), nullptr, 0); + m_Chunk = nullptr; +} + + + + + +cPath::~cPath() +{ + if (m_Status == ePathFinderStatus::CALCULATING) + { + FinishCalculation(); + } +} + + + + + +ePathFinderStatus cPath::Step(cChunk & a_Chunk) +{ + m_Chunk = &a_Chunk; + if (m_Status != ePathFinderStatus::CALCULATING) + { + return m_Status; + } + + if (m_BadChunkFound) + { + FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND); + return m_Status; + } + + if (m_StepsLeft == 0) + { + AttemptToFindAlternative(); + } + else + { + --m_StepsLeft; + int i; + for (i = 0; i < CALCULATIONS_PER_STEP; ++i) + { + if (Step_Internal()) // Step_Internal returns true when no more calculation is needed. + { + break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND. + } + } + + m_Chunk = nullptr; + } + return m_Status; +} + + + + + +Vector3i cPath::AcceptNearbyPath() +{ + ASSERT(m_Status == ePathFinderStatus::NEARBY_FOUND); + m_Status = ePathFinderStatus::PATH_FOUND; + return m_Destination; +} + + + + + +bool cPath::IsSolid(const Vector3i & a_Location) +{ + ASSERT(m_Chunk != nullptr); + + auto Chunk = m_Chunk->GetNeighborChunk(a_Location.x, a_Location.z); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + m_BadChunkFound = true; + return true; + } + m_Chunk = Chunk; + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + int RelX = a_Location.x - m_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_Location.z - m_Chunk->GetPosZ() * cChunkDef::Width; + + m_Chunk->GetBlockTypeMeta(RelX, a_Location.y, RelZ, BlockType, BlockMeta); + if ((BlockType == E_BLOCK_FENCE) || (BlockType == E_BLOCK_FENCE_GATE)) + { + GetCell(a_Location + Vector3i(0, 1, 0))->m_IsSolid = true; // Mobs will always think that the fence is 2 blocks high and therefore won't jump over. + } + if (BlockType == E_BLOCK_STATIONARY_WATER) + { + GetCell(a_Location + Vector3i(0, -1, 0))->m_IsSolid = true; + } + + return cBlockInfo::IsSolid(BlockType); +} + + + + + +bool cPath::Step_Internal() +{ + cPathCell * CurrentCell = OpenListPop(); + + // Path not reachable. + if (CurrentCell == nullptr) + { + AttemptToFindAlternative(); + return true; + } + + // Path found. + if (CurrentCell->m_Location == m_Destination) + { + BuildPath(); + FinishCalculation(ePathFinderStatus::PATH_FOUND); + return true; + } + + // Calculation not finished yet. + // Check if we have a new NearestPoint. + // TODO I don't like this that much, there should be a smarter way. + if ((m_Destination - CurrentCell->m_Location).Length() < 5) + { + if (m_Rand.NextInt(4) == 0) + { + m_NearestPointToTarget = CurrentCell; + } + } + else if (CurrentCell->m_H < m_NearestPointToTarget->m_H) + { + m_NearestPointToTarget = CurrentCell; + } + // process a currentCell by inspecting all neighbors. + + + // Check North, South, East, West on our height. + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, 0), CurrentCell, 10); + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, 0), CurrentCell, 10); + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, 1), CurrentCell, 10); + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, -1), CurrentCell, 10); + + // Check diagonals on XY plane. + for (int x = -1; x <= 1; x += 2) + { + if (GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid) // If there's a solid our east / west. + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 1, 0), CurrentCell, JUMP_G_COST); // Check east / west-up. + } + else + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, -1, 0), CurrentCell, 14); // Else check east / west-down. + } + } + + // Check diagonals on the YZ plane. + for (int z = -1; z <= 1; z += 2) + { + if (GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) // If there's a solid our east / west. + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, z), CurrentCell, JUMP_G_COST); // Check east / west-up. + } + else + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, -1, z), CurrentCell, 14); // Else check east / west-down. + } + } + + // Check diagonals on the XZ plane. (Normal diagonals, this plane is special because of gravity, etc) + for (int x = -1; x <= 1; x += 2) + { + for (int z = -1; z <= 1; z += 2) + { + // This condition prevents diagonal corner cutting. + if (!GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid && !GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) + { + // This prevents falling of "sharp turns" e.g. a 1x1x20 rectangle in the air which breaks in a right angle suddenly. + if (GetCell(CurrentCell->m_Location + Vector3i(x, -1, 0))->m_IsSolid && GetCell(CurrentCell->m_Location + Vector3i(0, -1, z))->m_IsSolid) + { + ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 0, z), CurrentCell, 14); // 14 is a good enough approximation of sqrt(10 + 10). + } + } + } + } + + return false; +} + + + + + +void cPath::AttemptToFindAlternative() +{ + if (m_NearestPointToTarget == GetCell(m_Source)) + { + FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND); + } + else + { + m_Destination = m_NearestPointToTarget->m_Location; + BuildPath(); + FinishCalculation(ePathFinderStatus::NEARBY_FOUND); + } +} + + + + + +void cPath::BuildPath() +{ + cPathCell * CurrentCell = GetCell(m_Destination); + do + { + m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points. + CurrentCell = CurrentCell->m_Parent; + } while (CurrentCell != nullptr); +} + + + + + +void cPath::FinishCalculation() +{ + m_Map.clear(); + m_OpenList = std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics>{}; +} + + + + + +void cPath::FinishCalculation(ePathFinderStatus a_NewStatus) +{ + if (m_BadChunkFound) + { + a_NewStatus = ePathFinderStatus::PATH_NOT_FOUND; + } + m_Status = a_NewStatus; + FinishCalculation(); +} + + + + + +void cPath::OpenListAdd(cPathCell * a_Cell) +{ + a_Cell->m_Status = eCellStatus::OPENLIST; + m_OpenList.push(a_Cell); + #ifdef COMPILING_PATHFIND_DEBUGGER + si::setBlock(a_Cell->m_Location.x, a_Cell->m_Location.y, a_Cell->m_Location.z, debug_open, SetMini(a_Cell)); + #endif +} + + + + + +cPathCell * cPath::OpenListPop() // Popping from the open list also means adding to the closed list. +{ + if (m_OpenList.size() == 0) + { + return nullptr; // We've exhausted the search space and nothing was found, this will trigger a PATH_NOT_FOUND or NEARBY_FOUND status. + } + + cPathCell * Ret = m_OpenList.top(); + m_OpenList.pop(); + Ret->m_Status = eCellStatus::CLOSEDLIST; + #ifdef COMPILING_PATHFIND_DEBUGGER +si::setBlock((Ret)->m_Location.x, (Ret)->m_Location.y, (Ret)->m_Location.z, debug_closed, SetMini(Ret)); + #endif + return Ret; +} + + + + + +void cPath::ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Parent, int a_Cost) +{ + cPathCell * cell = GetCell(a_Location); + int x, y, z; + + // Make sure we fit in the position. + for (y = 0; y < m_BoundingBoxHeight; ++y) + { + for (x = 0; x < m_BoundingBoxWidth; ++x) + { + for (z = 0; z < m_BoundingBoxWidth; ++z) + { + if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid) + { + return; + } + } + } + } + + /*y =-1; + for (x = 0; x < m_BoundingBoxWidth; ++x) + { + for (z = 0; z < m_BoundingBoxWidth; ++z) + { + if (!GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid) + { + return; + } + } + } + ProcessCell(cell, a_Parent, a_Cost);*/ + + // Make sure there's at least 1 piece of solid below us. + + bool GroundFlag = false; + y =-1; + for (x = 0; x < m_BoundingBoxWidth; ++x) + { + for (z = 0; z < m_BoundingBoxWidth; ++z) + { + if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid) + { + GroundFlag = true; + break; + } + } + } + + if (GroundFlag) + { + ProcessCell(cell, a_Parent, a_Cost); + } +} + + + + + +void cPath::ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta) +{ + // Case 1: Cell is in the closed list, ignore it. + if (a_Cell->m_Status == eCellStatus::CLOSEDLIST) + { + return; + } + if (a_Cell->m_Status == eCellStatus::NOLIST) // Case 2: The cell is not in any list. + { + // Cell is walkable, add it to the open list. + // Note that non-walkable cells are filtered out in Step_internal(); + // Special case: Start cell goes here, gDelta is 0, caller is NULL. + a_Cell->m_Parent = a_Caller; + if (a_Caller != nullptr) + { + a_Cell->m_G = a_Caller->m_G + a_GDelta; + } + else + { + a_Cell->m_G = 0; + } + + // Calculate H. This is A*'s Heuristics value. + #if DISTANCE_MANHATTAN == 1 + // Manhattan distance. DeltaX + DeltaY + DeltaZ. + a_Cell->m_H = 10 * (abs(a_Cell->m_Location.x-m_Destination.x) + abs(a_Cell->m_Location.y-m_Destination.y) + abs(a_Cell->m_Location.z-m_Destination.z)); + #else + // Euclidian distance. sqrt(DeltaX^2 + DeltaY^2 + DeltaZ^2), more precise. + a_Cell->m_H = static_cast<decltype(a_Cell->m_H)>((a_Cell->m_Location - m_Destination).Length() * 10); + #endif + + #if HEURISTICS_ONLY == 1 + a_Cell->m_F = a_Cell->m_H; // Greedy search. https://en.wikipedia.org/wiki/Greedy_search + #else + a_Cell->m_F = a_Cell->m_H + a_Cell->m_G; // Regular A*. + #endif + + OpenListAdd(a_Cell); + return; + } + + // Case 3: Cell is in the open list, check if G and H need an update. + int NewG = a_Caller->m_G + a_GDelta; + if (NewG < a_Cell->m_G) + { + a_Cell->m_G = NewG; + a_Cell->m_H = a_Cell->m_F + a_Cell->m_G; + a_Cell->m_Parent = a_Caller; + } + +} + + + + + +cPathCell * cPath::GetCell(const Vector3i & a_Location) +{ + // Create the cell in the hash table if it's not already there. + if (m_Map.count(a_Location) == 0) // Case 1: Cell is not on any list. We've never checked this cell before. + { + m_Map[a_Location].m_Location = a_Location; + m_Map[a_Location].m_IsSolid = IsSolid(a_Location); + m_Map[a_Location].m_Status = eCellStatus::NOLIST; + #ifdef COMPILING_PATHFIND_DEBUGGER + #ifdef COMPILING_PATHFIND_DEBUGGER_MARK_UNCHECKED + si::setBlock(a_Location.x, a_Location.y, a_Location.z, debug_unchecked, Cell->m_IsSolid ? NORMAL : MINI); + #endif + #endif + return &m_Map[a_Location]; + } + else + { + return &m_Map[a_Location]; + } +} diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h new file mode 100644 index 000000000..3b9c0400e --- /dev/null +++ b/src/Mobs/Path.h @@ -0,0 +1,186 @@ + +#pragma once + +/* +// Needed Fwds: cPath +enum class ePathFinderStatus; +class cPath; +*/ + +#include "../FastRandom.h" +#ifdef COMPILING_PATHFIND_DEBUGGER + /* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug + this class outside of MCServer. This preprocessor flag is never set when compiling MCServer. */ + #include "PathFinderIrrlicht_Head.h" +#endif + +#include <unordered_map> + +//fwd: ../Chunk.h +class cChunk; + +/* Various little structs and classes */ +enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND}; +enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST}; +struct cPathCell +{ + Vector3i m_Location; // Location of the cell in the world. + int m_F, m_G, m_H; // F, G, H as defined in regular A*. + eCellStatus m_Status; // Which list is the cell in? Either non, open, or closed. + cPathCell * m_Parent; // Cell's parent, as defined in regular A*. + bool m_IsSolid; // Is the cell an air or a solid? Partial solids are currently considered solids. +}; + + + + + +class compareHeuristics +{ +public: + bool operator()(cPathCell * & a_V1, cPathCell * & a_V2); +}; + + + + + +class cPath +{ +public: + /** Creates a pathfinder instance. A Mob will probably need a single pathfinder instance for its entire life. + + Note that if you have a man-sized mob (1x1x2, zombies, etc), you are advised to call this function without parameters + because the declaration might change in later version of the pathFinder, and a parameter-less call always assumes a man-sized mob. + + If your mob is not man-sized, you are advised to use cPath(width, height), this would be compatible with future versions, + but please be aware that as of now those parameters will be ignored and your mob will be assumed to be man sized. + + @param a_BoundingBoxWidth the character's boundingbox width in blocks. Currently the parameter is ignored and 1 is assumed. + @param a_BoundingBoxHeight the character's boundingbox width in blocks. Currently the parameter is ignored and 2 is assumed. + @param a_MaxUp the character's max jump height in blocks. Currently the parameter is ignored and 1 is assumed. + @param a_MaxDown How far is the character willing to fall? Currently the parameter is ignored and 1 is assumed. */ + /** Attempts to find a path starting from source to destination. + After calling this, you are expected to call Step() once per tick or once per several ticks until it returns true. You should then call getPath() to obtain the path. + Calling this before a path is found resets the current path and starts another search. + @param a_StartingPoint The function expects this position to be the lowest block the mob is in, a rule of thumb: "The block where the Zombie's knees are at". + @param a_EndingPoint "The block where the Zombie's knees want to be". + @param a_MaxSteps The maximum steps before giving up. */ + cPath( + cChunk & a_Chunk, + const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps, + double a_BoundingBoxWidth, double a_BoundingBoxHeight, + int a_MaxUp = 1, int a_MaxDown = 1 + ); + + /** Destroys the path and frees its memory. */ + ~cPath(); + + /** Performs part of the path calculation and returns the appropriate status. + If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination + is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found, + and to make consequent calls to step return PATH_FOUND*/ + ePathFinderStatus Step(cChunk & a_Chunk); + + /** Called after the PathFinder's step returns NEARBY_FOUND. + Changes the PathFinder status from NEARBY_FOUND to PATH_FOUND, returns the nearby destination that + the PathFinder found a path to. */ + Vector3i AcceptNearbyPath(); + + /* Point retrieval functions, inlined for performance. */ + /** Returns the next point in the path. */ + inline Vector3d GetNextPoint() + { + ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); + Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - (++m_CurrentPoint)]; + return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth); + } + /** Checks whether this is the last point or not. Never call getnextPoint when this is true. */ + inline bool IsLastPoint() + { + ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); + return (m_CurrentPoint == m_PathPoints.size() - 1); + } + inline bool IsFirstPoint() + { + ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); + return (m_CurrentPoint == 0); + } + /** Get the point at a_index. Remark: Internally, the indexes are reversed. */ + inline Vector3d GetPoint(size_t a_index) + { + ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); + ASSERT(a_index < m_PathPoints.size()); + Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - a_index]; + return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth); + } + /** Returns the total number of points this path has. */ + inline int GetPointCount() + { + if (m_Status != ePathFinderStatus::PATH_FOUND) + { + return 0; + } + return m_PathPoints.size(); + } + + struct VectorHasher + { + std::size_t operator()(const Vector3i & a_Vector) const + { + // Guaranteed to have no hash collisions for any 128x128x128 area. Suitable for pathfinding. + int32_t t = 0; + t += (int8_t)a_Vector.x; + t = t << 8; + t += (int8_t)a_Vector.y; + t = t << 8; + t += (int8_t)a_Vector.z; + t = t << 8; + return (size_t)t; + } + }; +private: + + /* General */ + bool IsSolid(const Vector3i & a_Location); // Query our hosting world and ask it if there's a solid at a_location. + bool Step_Internal(); // The public version just calls this version * CALCULATIONS_PER_CALL times. + void FinishCalculation(); // Clears the memory used for calculating the path. + void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status. + void AttemptToFindAlternative(); + void BuildPath(); + + /* Openlist and closedlist management */ + void OpenListAdd(cPathCell * a_Cell); + cPathCell * OpenListPop(); + void ProcessIfWalkable(const Vector3i &a_Location, cPathCell * a_Parent, int a_Cost); + + /* Map management */ + void ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta); + cPathCell * GetCell(const Vector3i & a_location); + + /* Pathfinding fields */ + std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList; + std::unordered_map<Vector3i, cPathCell, VectorHasher> m_Map; + Vector3i m_Destination; + Vector3i m_Source; + int m_BoundingBoxWidth; + int m_BoundingBoxHeight; + double m_HalfWidth; + int m_StepsLeft; + cPathCell * m_NearestPointToTarget; + cFastRandom m_Rand; + + /* Control fields */ + ePathFinderStatus m_Status; + + /* Final path fields */ + size_t m_CurrentPoint; + std::vector<Vector3i> m_PathPoints; + + /* Interfacing with the world */ + cChunk * m_Chunk; // Only valid inside Step()! + bool m_BadChunkFound; + #ifdef COMPILING_PATHFIND_DEBUGGER + #include "../path_irrlicht.cpp" + #endif +}; diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index edd4d9de4..56d6abfd5 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -90,7 +90,6 @@ void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_Attachee->IsPlayer() && (m_Attachee->GetEquippedWeapon().m_ItemType == E_ITEM_CARROT_ON_STICK)) { MoveToPosition((m_Attachee->GetPosition()) + (m_Attachee->GetLookVector()*10)); - m_bMovingToDestination = true; } } } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index c0cdec035..ec24f167e 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -98,7 +98,7 @@ void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_TimeToStopEating > 0) { - m_bMovingToDestination = false; // The sheep should not move when he's eating + StopMovingToPosition(); m_TimeToStopEating--; if (m_TimeToStopEating == 0) diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index 331c8e8ad..f99404669 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -37,7 +37,7 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) else { AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW); - + } AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE); AddRandomArmorDropItem(a_Drops, LootingLevel); @@ -48,25 +48,6 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSkeleton::MoveToPosition(const Vector3d & a_Position) -{ - // If the destination is sufficiently skylight challenged AND the skeleton isn't on fire then block the movement - if ( - !IsOnFire() && - (m_World->GetBlockSkyLight((int)floor(a_Position.x), (int)floor(a_Position.y), (int)floor(a_Position.z)) - m_World->GetSkyDarkness() > 8) - ) - { - m_bMovingToDestination = false; - return; - } - - super::MoveToPosition(a_Position); -} - - - - - void cSkeleton::Attack(std::chrono::milliseconds a_Dt) { m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate; diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h index 9c49c52fb..1b6ce4bf2 100644 --- a/src/Mobs/Skeleton.h +++ b/src/Mobs/Skeleton.h @@ -11,19 +11,18 @@ class cSkeleton : public cAggressiveMonster { typedef cAggressiveMonster super; - + public: cSkeleton(bool IsWither); CLASS_PROTODEF(cSkeleton) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void MoveToPosition(const Vector3d & a_Position) override; virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual bool IsUndead(void) override { return true; } - + bool IsWither(void) const { return m_bIsWither; } private: diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index e42501e47..7fc4821d8 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -89,7 +89,7 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI) -const AString cSlime::GetSizeName(int a_Size) const +AString cSlime::GetSizeName(int a_Size) { if (a_Size > 1) { diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h index 29605992d..40131b101 100644 --- a/src/Mobs/Slime.h +++ b/src/Mobs/Slime.h @@ -27,7 +27,7 @@ public: /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds. Returns either "big" or "small". */ - const AString GetSizeName(int a_Size) const; + static AString GetSizeName(int a_Size); protected: diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 6f647ac18..e4953d546 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -156,7 +156,7 @@ void cVillager::HandleFarmerPrepareFarmCrops() void cVillager::HandleFarmerTryHarvestCrops() { // Harvest the crops if the villager isn't moving and if the crops are closer then 2 blocks. - if (!m_bMovingToDestination && (GetPosition() - m_CropsPos).Length() < 2) + if (!m_IsFollowingPath && (GetPosition() - m_CropsPos).Length() < 2) { // Check if the blocks didn't change while the villager was walking to the coordinates. BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z); diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index b3eefdf79..3c2ec1520 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -5,6 +5,7 @@ #include "../World.h" #include "../Entities/Player.h" #include "../Items/ItemHandler.h" +#include "Broadcaster.h" @@ -83,13 +84,13 @@ void cWolf::OnRightClicked(cPlayer & a_Player) SetIsTame(true); SetOwner(a_Player.GetName(), a_Player.GetUUID()); m_World->BroadcastEntityStatus(*this, esWolfTamed); - m_World->BroadcastParticleEffect("heart", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5); + m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); } else { // Taming failed m_World->BroadcastEntityStatus(*this, esWolfTaming); - m_World->BroadcastParticleEffect("smoke", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5); + m_World->GetBroadcaster().BroadcastParticleEffect("smoke", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); } } } @@ -137,7 +138,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player) } } } - + m_World->BroadcastEntityMetadata(*this); } @@ -203,7 +204,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } else if (IsSitting()) { - m_bMovingToDestination = false; + StopMovingToPosition(); } } diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 73ffb55c2..5de83acd8 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -25,8 +25,8 @@ public: virtual void Attack(std::chrono::milliseconds a_Dt) override; // Get functions - bool IsSitting (void) const { return m_IsSitting; } - bool IsTame (void) const { return m_IsTame; } + bool IsSitting (void) const override { return m_IsSitting; } + bool IsTame (void) const override { return m_IsTame; } bool IsBegging (void) const { return m_IsBegging; } bool IsAngry (void) const { return m_IsAngry; } AString GetOwnerName (void) const { return m_OwnerName; } diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp index 63042e252..fa4ac855d 100644 --- a/src/Mobs/Zombie.cpp +++ b/src/Mobs/Zombie.cpp @@ -37,26 +37,3 @@ void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) AddRandomArmorDropItem(a_Drops, LootingLevel); AddRandomWeaponDropItem(a_Drops, LootingLevel); } - - - - - -void cZombie::MoveToPosition(const Vector3d & a_Position) -{ - // If the destination is sufficiently skylight challenged AND the skeleton isn't on fire then block the movement - if ( - !IsOnFire() && - (m_World->GetBlockSkyLight((int)floor(a_Position.x), (int)floor(a_Position.y), (int)floor(a_Position.z)) - m_World->GetSkyDarkness() > 8) - ) - { - m_bMovingToDestination = false; - return; - } - - super::MoveToPosition(a_Position); -} - - - - diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h index 809c2a6fe..47a9f1904 100644 --- a/src/Mobs/Zombie.h +++ b/src/Mobs/Zombie.h @@ -10,17 +10,15 @@ class cZombie : public cAggressiveMonster { typedef cAggressiveMonster super; - + public: cZombie(bool a_IsVillagerZombie); CLASS_PROTODEF(cZombie) - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void MoveToPosition(const Vector3d & a_Position) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool IsUndead(void) override { return true; } - + bool IsVillagerZombie(void) const { return m_IsVillagerZombie; } bool IsConverting (void) const { return m_IsConverting; } diff --git a/src/MonsterConfig.h b/src/MonsterConfig.h index 371d324c2..50979c44a 100644 --- a/src/MonsterConfig.h +++ b/src/MonsterConfig.h @@ -23,7 +23,7 @@ public: private: struct sAttributesStruct; struct sMonsterConfigState; - sMonsterConfigState* m_pState; + sMonsterConfigState * m_pState; void Initialize(); } ; diff --git a/src/Noise/Noise.cpp b/src/Noise/Noise.cpp index 0249ab6c1..d11c47bc2 100644 --- a/src/Noise/Noise.cpp +++ b/src/Noise/Noise.cpp @@ -633,10 +633,10 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS }; const NOISE_DATATYPE FracX = (a_X) - BaseX; - const NOISE_DATATYPE x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX); - const NOISE_DATATYPE x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX); - const NOISE_DATATYPE x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX); - const NOISE_DATATYPE x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX); + const NOISE_DATATYPE x1interp1 = CubicInterpolate(points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX); + const NOISE_DATATYPE x1interp2 = CubicInterpolate(points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX); + const NOISE_DATATYPE x1interp3 = CubicInterpolate(points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX); + const NOISE_DATATYPE x1interp4 = CubicInterpolate(points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX); const NOISE_DATATYPE points2[4][4] = { @@ -646,45 +646,45 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS { IntNoise3D(BaseX - 1, BaseY + 2, BaseZ), IntNoise3D(BaseX, BaseY + 2, BaseZ), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ), }, }; - const NOISE_DATATYPE x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX); - const NOISE_DATATYPE x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX); - const NOISE_DATATYPE x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX); - const NOISE_DATATYPE x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX); + const NOISE_DATATYPE x2interp1 = CubicInterpolate(points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX); + const NOISE_DATATYPE x2interp2 = CubicInterpolate(points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX); + const NOISE_DATATYPE x2interp3 = CubicInterpolate(points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX); + const NOISE_DATATYPE x2interp4 = CubicInterpolate(points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX); const NOISE_DATATYPE points3[4][4] = { - { IntNoise3D( BaseX-1, BaseY-1, BaseZ+1), IntNoise3D( BaseX, BaseY-1, BaseZ+1), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1), IntNoise3D( BaseX+2, BaseY-1, BaseZ + 1), }, - { IntNoise3D( BaseX-1, BaseY, BaseZ+1), IntNoise3D( BaseX, BaseY, BaseZ+1), IntNoise3D( BaseX+1, BaseY, BaseZ+1), IntNoise3D( BaseX+2, BaseY, BaseZ + 1), }, - { IntNoise3D( BaseX-1, BaseY+1, BaseZ+1), IntNoise3D( BaseX, BaseY+1, BaseZ+1), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1), IntNoise3D( BaseX+2, BaseY+1, BaseZ + 1), }, - { IntNoise3D( BaseX-1, BaseY+2, BaseZ+1), IntNoise3D( BaseX, BaseY+2, BaseZ+1), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1), IntNoise3D( BaseX+2, BaseY+2, BaseZ + 1), }, + { IntNoise3D(BaseX - 1, BaseY - 1, BaseZ + 1), IntNoise3D(BaseX, BaseY - 1, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ + 1), }, + { IntNoise3D(BaseX - 1, BaseY, BaseZ + 1), IntNoise3D(BaseX, BaseY, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY, BaseZ + 1), }, + { IntNoise3D(BaseX - 1, BaseY + 1, BaseZ + 1), IntNoise3D(BaseX, BaseY + 1, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ + 1), }, + { IntNoise3D(BaseX - 1, BaseY + 2, BaseZ + 1), IntNoise3D(BaseX, BaseY + 2, BaseZ + 1), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ + 1), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ + 1), }, }; - const NOISE_DATATYPE x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX); - const NOISE_DATATYPE x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX); - const NOISE_DATATYPE x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX); - const NOISE_DATATYPE x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX); + const NOISE_DATATYPE x3interp1 = CubicInterpolate(points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX); + const NOISE_DATATYPE x3interp2 = CubicInterpolate(points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX); + const NOISE_DATATYPE x3interp3 = CubicInterpolate(points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX); + const NOISE_DATATYPE x3interp4 = CubicInterpolate(points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX); const NOISE_DATATYPE points4[4][4] = { - { IntNoise3D( BaseX-1, BaseY-1, BaseZ+2), IntNoise3D( BaseX, BaseY-1, BaseZ+2), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2), }, - { IntNoise3D( BaseX-1, BaseY, BaseZ+2), IntNoise3D( BaseX, BaseY, BaseZ+2), IntNoise3D( BaseX+1, BaseY, BaseZ+2), IntNoise3D( BaseX+2, BaseY, BaseZ+2), }, - { IntNoise3D( BaseX-1, BaseY+1, BaseZ+2), IntNoise3D( BaseX, BaseY+1, BaseZ+2), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2), }, - { IntNoise3D( BaseX-1, BaseY+2, BaseZ+2), IntNoise3D( BaseX, BaseY+2, BaseZ+2), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2), }, + { IntNoise3D(BaseX - 1, BaseY - 1, BaseZ + 2), IntNoise3D(BaseX, BaseY - 1, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ + 2), }, + { IntNoise3D(BaseX - 1, BaseY, BaseZ + 2), IntNoise3D(BaseX, BaseY, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY, BaseZ + 2), }, + { IntNoise3D(BaseX - 1, BaseY + 1, BaseZ + 2), IntNoise3D(BaseX, BaseY + 1, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ + 2), }, + { IntNoise3D(BaseX - 1, BaseY + 2, BaseZ + 2), IntNoise3D(BaseX, BaseY + 2, BaseZ + 2), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ + 2), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ + 2), }, }; - const NOISE_DATATYPE x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX); - const NOISE_DATATYPE x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX); - const NOISE_DATATYPE x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX); - const NOISE_DATATYPE x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX); + const NOISE_DATATYPE x4interp1 = CubicInterpolate(points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX); + const NOISE_DATATYPE x4interp2 = CubicInterpolate(points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX); + const NOISE_DATATYPE x4interp3 = CubicInterpolate(points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX); + const NOISE_DATATYPE x4interp4 = CubicInterpolate(points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX); const NOISE_DATATYPE FracY = (a_Y) - BaseY; - const NOISE_DATATYPE yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY); - const NOISE_DATATYPE yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY); - const NOISE_DATATYPE yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY); - const NOISE_DATATYPE yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY); + const NOISE_DATATYPE yinterp1 = CubicInterpolate(x1interp1, x1interp2, x1interp3, x1interp4, FracY); + const NOISE_DATATYPE yinterp2 = CubicInterpolate(x2interp1, x2interp2, x2interp3, x2interp4, FracY); + const NOISE_DATATYPE yinterp3 = CubicInterpolate(x3interp1, x3interp2, x3interp3, x3interp4, FracY); + const NOISE_DATATYPE yinterp4 = CubicInterpolate(x4interp1, x4interp2, x4interp3, x4interp4, FracY); const NOISE_DATATYPE FracZ = (a_Z) - BaseZ; - return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ); + return CubicInterpolate(yinterp1, yinterp2, yinterp3, yinterp4, FracZ); } diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h index d52f049d2..a95a9a0cd 100644 --- a/src/OSSupport/CriticalSection.h +++ b/src/OSSupport/CriticalSection.h @@ -13,7 +13,7 @@ public: void Lock(void); void Unlock(void); - // IsLocked/IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined + // IsLocked / IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined // The fake versions (in Release) will not effect the program in any way #ifdef _DEBUG cCriticalSection(void); diff --git a/src/OSSupport/Errors.cpp b/src/OSSupport/Errors.cpp index a5361e1a6..407799495 100644 --- a/src/OSSupport/Errors.cpp +++ b/src/OSSupport/Errors.cpp @@ -22,7 +22,7 @@ AString GetOSErrorString( int a_ErrNo) // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r(): - #if !defined(__APPLE__) && ( _GNU_SOURCE) && !defined(ANDROID_NDK) // GNU version of strerror_r() + #if !defined(__APPLE__) && defined( _GNU_SOURCE) && !defined(ANDROID_NDK) // GNU version of strerror_r() char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer)); if (res != nullptr) diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp index 8957dfaef..43105b230 100644 --- a/src/OSSupport/File.cpp +++ b/src/OSSupport/File.cpp @@ -453,6 +453,108 @@ AString cFile::ReadWholeFile(const AString & a_FileName) +AString cFile::ChangeFileExt(const AString & a_FileName, const AString & a_NewExt) +{ + auto res = a_FileName; + + // If the path separator is the last character of the string, return the string unmodified (refers to a folder): + #if defined(_MSC_VER) + // Find either path separator - MSVC CRT accepts slashes as separators, too + auto LastPathSep = res.find_last_of("/\\"); + #elif defined(_WIN32) + // Windows with different CRTs support only the backslash separator + auto LastPathSep = res.rfind('\\'); + #else + // Linux supports only the slash separator + auto LastPathSep = res.rfind('/'); + #endif + if ((LastPathSep != AString::npos) && (LastPathSep + 1 == res.size())) + { + return res; + } + + // Append or replace the extension: + auto DotPos = res.rfind('.'); + if ( + (DotPos == AString::npos) || // No dot found + ((LastPathSep != AString::npos) && (LastPathSep > DotPos)) // Last dot is before the last path separator (-> in folder name) + ) + { + // No extension, just append the new one: + if (!a_NewExt.empty() && (a_NewExt[0] != '.')) + { + // a_NewExt doesn't start with a dot, insert one: + res.push_back('.'); + } + res.append(a_NewExt); + } + else + { + // Replace existing extension: + if (!a_NewExt.empty() && (a_NewExt[0] != '.')) + { + // a_NewExt doesn't start with a dot, keep the current one: + res.erase(DotPos + 1, AString::npos); + } + else + { + res.erase(DotPos, AString::npos); + } + res.append(a_NewExt); + } + return res; +} + + + + + +unsigned cFile::GetLastModificationTime(const AString & a_FileName) +{ + struct stat st; + if (stat(a_FileName.c_str(), &st) < 0) + { + return 0; + } + #ifdef _WIN32 + // Windows returns times in local time already + return static_cast<unsigned>(st.st_mtime); + #else + // Linux returns UTC time, convert to local timezone: + return static_cast<unsigned>(mktime(localtime(&st.st_mtime))); + #endif +} + + + + + +AString cFile::GetPathSeparator(void) +{ + #ifdef _WIN32 + return "\\"; + #else + return "/"; + #endif +} + + + + + +AString cFile::GetExecutableExt(void) +{ + #ifdef _WIN32 + return ".exe"; + #else + return ""; + #endif +} + + + + + int cFile::Printf(const char * a_Fmt, ...) { AString buf; diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h index ac6d1ab21..1b5e71a17 100644 --- a/src/OSSupport/File.h +++ b/src/OSSupport/File.h @@ -62,7 +62,7 @@ public: { fmRead, // Read-only. If the file doesn't exist, object will not be valid fmWrite, // Write-only. If the file already exists, it will be overwritten - fmReadWrite, // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning + fmReadWrite, // Read / write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning fmAppend // Write-only. If the file already exists cursor will be moved to the end of the file } ; @@ -124,9 +124,30 @@ public: /** Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute */ static bool CreateFolder(const AString & a_FolderPath); - /** Returns the entire contents of the specified file as a string. Returns empty string on error. */ + // tolua_end + + /** Returns the entire contents of the specified file as a string. Returns empty string on error. + Exported manually in ManualBindings.cpp due to #1914 - ToLua code doesn't work well with binary files. */ static AString ReadWholeFile(const AString & a_FileName); + // tolua_begin + + /** Returns a_FileName with its extension changed to a_NewExt. + a_FileName may contain path specification. */ + static AString ChangeFileExt(const AString & a_FileName, const AString & a_NewExt); + + /** Returns the last modification time (in current timezone) of the specified file. + The value returned is in the same units as the value returned by time() function. + If the file is not found / accessible, zero is returned. */ + static unsigned GetLastModificationTime(const AString & a_FileName); + + /** Returns the path separator used by the current platform. + Note that the platform / CRT may support additional path separators (such as slashes on Windows), these don't get reported. */ + static AString GetPathSeparator(void); + + /** Returns the customary executable extension used by the current platform. */ + static AString GetExecutableExt(void); + // tolua_end /** Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). */ diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 95a935bbe..1162d7fc6 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -162,7 +162,7 @@ public: /** Returns the local port to which the underlying socket is bound. */ virtual UInt16 GetPort(void) const = 0; - /** Sends the specified payload in a single UDP datagram to the specified host+port combination. + /** Sends the specified payload in a single UDP datagram to the specified host + port combination. Note that in order to send to a broadcast address, you need to call EnableBroadcasts() first. */ virtual bool Send(const AString & a_Payload, const AString & a_Host, UInt16 a_Port) = 0; diff --git a/src/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h index adc531ed8..57fa4bdb2 100644 --- a/src/OSSupport/Semaphore.h +++ b/src/OSSupport/Semaphore.h @@ -9,7 +9,7 @@ public: void Wait(); void Signal(); private: - void* m_Handle; // HANDLE pointer + void * m_Handle; // HANDLE pointer #ifndef _WIN32 bool m_bNamed; diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index c6f1978ad..ae6ba04f1 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -23,7 +23,6 @@ cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): m_RemotePort(0), m_ShouldShutdown(false) { - LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); } @@ -38,8 +37,6 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L m_RemotePort(0), m_ShouldShutdown(false) { - LOGD("Created new cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); - // Update the endpoint addresses: UpdateLocalAddress(); UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); @@ -51,7 +48,6 @@ cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_L cTCPLinkImpl::~cTCPLinkImpl() { - LOGD("Deleting cTCPLinkImpl at %p with BufferEvent at %p", this, m_BufferEvent); bufferevent_free(m_BufferEvent); } @@ -216,8 +212,6 @@ void cTCPLinkImpl::WriteCallback(bufferevent * a_BufferEvent, void * a_Self) void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) { - LOGD("cTCPLink event callback for link %p, BEV %p; what = 0x%02x", a_Self, a_BufferEvent, a_What); - ASSERT(a_Self != nullptr); cTCPLinkImplPtr Self = static_cast<cTCPLinkImpl *>(a_Self)->m_Self; diff --git a/src/OSSupport/UDPEndpointImpl.cpp b/src/OSSupport/UDPEndpointImpl.cpp index 31ca107ce..68117e5a0 100644 --- a/src/OSSupport/UDPEndpointImpl.cpp +++ b/src/OSSupport/UDPEndpointImpl.cpp @@ -384,7 +384,7 @@ void cUDPEndpointImpl::Open(UInt16 a_Port) // Failed to create IPv6 socket, create an IPv4 one instead: m_IsMainSockIPv6 = false; err = EVUTIL_SOCKET_ERROR(); - LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); + LOGD("UDP: Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); m_MainSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (!IsValidSocket(m_MainSock)) { diff --git a/src/OverridesSettingsRepository.cpp b/src/OverridesSettingsRepository.cpp new file mode 100644 index 000000000..e63f2c44c --- /dev/null +++ b/src/OverridesSettingsRepository.cpp @@ -0,0 +1,273 @@ + +#include "Globals.h" +#include "OverridesSettingsRepository.h" + +cOverridesSettingsRepository::cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides) : + m_Main(std::move(a_Main)), + m_Overrides(std::move(a_Overrides)) +{ +} + + + + + +bool cOverridesSettingsRepository::KeyExists(const AString a_keyName) const +{ + return m_Overrides->KeyExists(a_keyName) || m_Main->KeyExists(a_keyName); +} + + + + +bool cOverridesSettingsRepository::HasValue(const AString & a_KeyName, const AString & a_ValueName) const +{ + return m_Overrides->HasValue(a_KeyName, a_ValueName) || m_Main->HasValue(a_KeyName, a_ValueName); +} + + + + + +int cOverridesSettingsRepository::AddKeyName(const AString & a_keyname) +{ + + if (m_Overrides->KeyExists(a_keyname)) + { + m_Overrides->AddKeyName(a_keyname); + return 0; + } + + return m_Main->AddKeyName(a_keyname); +} + + + + + +bool cOverridesSettingsRepository::AddKeyComment(const AString & a_keyname, const AString & a_comment) +{ + if (m_Overrides->KeyExists(a_keyname)) + { + return m_Overrides->AddKeyComment(a_keyname, a_comment); + } + + return m_Main->AddKeyComment(a_keyname, a_comment); +} + + + + + +AString cOverridesSettingsRepository::GetKeyComment(const AString & a_keyname, const int a_commentID) const +{ + + if (m_Overrides->KeyExists(a_keyname)) + { + return m_Overrides->GetKeyComment(a_keyname, a_commentID); + } + + return m_Main->GetKeyComment(a_keyname, a_commentID); +} + + + + + +bool cOverridesSettingsRepository::DeleteKeyComment(const AString & a_keyname, const int a_commentID) +{ + if (m_Overrides->KeyExists(a_keyname)) + { + return m_Overrides->DeleteKeyComment(a_keyname, a_commentID); + } + + return m_Main->DeleteKeyComment(a_keyname, a_commentID); +} + + + + + +void cOverridesSettingsRepository::AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + m_Overrides->AddValue(a_KeyName, a_ValueName, a_Value); + } + else + { + m_Main->AddValue(a_KeyName, a_ValueName, a_Value); + } +} + + + + + +std::vector<std::pair<AString, AString>> cOverridesSettingsRepository::GetValues(AString a_keyName) +{ + auto overrides = m_Overrides->GetValues(a_keyName); + auto main = m_Main->GetValues(a_keyName); + std::sort(overrides.begin(), overrides.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;}); + std::sort(main.begin(), main.end(), [](std::pair<AString, AString> a, std::pair<AString, AString> b) -> bool { return a < b ;}); + + std::vector<std::pair<AString, AString>> ret; + + + size_t overridesIndex = 0; + for (auto pair : main) + { + if (overridesIndex >= overrides.size()) + { + ret.push_back(pair); + continue; + } + if (pair.first == overrides[overridesIndex].first) + { + continue; + } + while (pair.first > overrides[overridesIndex].first) + { + ret.push_back(overrides[overridesIndex]); + overridesIndex++; + } + ret.push_back(pair); + } + return ret; +} + + + + + +AString cOverridesSettingsRepository::GetValue(const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) const +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValue(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValue(a_KeyName, a_ValueName, defValue); + } +} + + + + + +AString cOverridesSettingsRepository::GetValueSet (const AString & a_KeyName, const AString & a_ValueName, const AString & defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSet(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSet(a_KeyName, a_ValueName, defValue); + } +} + + + + + +int cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const int defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue); + } +} + + + + + +Int64 cOverridesSettingsRepository::GetValueSetI(const AString & a_KeyName, const AString & a_ValueName, const Int64 defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSetI(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSetI(a_KeyName, a_ValueName, defValue); + } +} + + + + + +bool cOverridesSettingsRepository::GetValueSetB(const AString & a_KeyName, const AString & a_ValueName, const bool defValue) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->GetValueSetB(a_KeyName, a_ValueName, defValue); + } + else + { + return m_Main->GetValueSetB(a_KeyName, a_ValueName, defValue); + } +} + + + + + +bool cOverridesSettingsRepository::SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } + else + { + return m_Main->SetValue(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } +} + + + + + +bool cOverridesSettingsRepository::SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } + else + { + return m_Main->SetValueI(a_KeyName, a_ValueName, a_Value, a_CreateIfNotExists); + } +} + + + + + +bool cOverridesSettingsRepository::DeleteValue(const AString & a_KeyName, const AString & a_ValueName) +{ + if (m_Overrides->HasValue(a_KeyName, a_ValueName)) + { + return m_Overrides->DeleteValue(a_KeyName, a_ValueName); + } + else + { + return m_Main->DeleteValue(a_KeyName, a_ValueName); + } +} + + + +bool cOverridesSettingsRepository::Flush() +{ + return m_Overrides->Flush() && m_Main->Flush(); +} + diff --git a/src/OverridesSettingsRepository.h b/src/OverridesSettingsRepository.h new file mode 100644 index 000000000..04a53997f --- /dev/null +++ b/src/OverridesSettingsRepository.h @@ -0,0 +1,52 @@ + +#pragma once + +#include "SettingsRepositoryInterface.h" + +#include <unordered_map> + +class cOverridesSettingsRepository : public cSettingsRepositoryInterface +{ + +public: + cOverridesSettingsRepository(std::unique_ptr<cSettingsRepositoryInterface> a_Main, std::unique_ptr<cSettingsRepositoryInterface> a_Overrides); + + virtual ~cOverridesSettingsRepository() = default; + + virtual bool KeyExists(const AString keyname) const override; + + virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const override; + + virtual int AddKeyName(const AString & keyname) override; + + virtual bool AddKeyComment(const AString & keyname, const AString & comment) override; + + virtual AString GetKeyComment(const AString & keyname, const int commentID) const override; + + virtual bool DeleteKeyComment(const AString & keyname, const int commentID) override; + + virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) override; + + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) override; + + virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const override; + + virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") override; + virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) override; + virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) override; + virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) override; + + virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) override; + virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) override; + + virtual bool DeleteValue(const AString & keyname, const AString & valuename) override; + + virtual bool Flush() override; + +private: + + std::unique_ptr<cSettingsRepositoryInterface> m_Main; + std::unique_ptr<cSettingsRepositoryInterface> m_Overrides; + +}; + diff --git a/src/PolarSSL++/BlockingSslClientSocket.cpp b/src/PolarSSL++/BlockingSslClientSocket.cpp index 821125b31..f5ad2f08c 100644 --- a/src/PolarSSL++/BlockingSslClientSocket.cpp +++ b/src/PolarSSL++/BlockingSslClientSocket.cpp @@ -54,19 +54,19 @@ class cBlockingSslClientSocketLinkCallbacks: } - virtual void OnReceivedData(const char * a_Data, size_t a_Length) + virtual void OnReceivedData(const char * a_Data, size_t a_Length) override { m_Socket.OnReceivedData(a_Data, a_Length); } - virtual void OnRemoteClosed(void) + virtual void OnRemoteClosed(void) override { m_Socket.OnDisconnected(); } - virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override { m_Socket.OnDisconnected(); } diff --git a/src/PolarSSL++/BlockingSslClientSocket.h b/src/PolarSSL++/BlockingSslClientSocket.h index 319e82bf2..462ee95a7 100644 --- a/src/PolarSSL++/BlockingSslClientSocket.h +++ b/src/PolarSSL++/BlockingSslClientSocket.h @@ -21,6 +21,11 @@ class cBlockingSslClientSocket : { public: cBlockingSslClientSocket(void); + + ~cBlockingSslClientSocket(void) + { + Disconnect(); + } /** Connects to the specified server and performs SSL handshake. Returns true if successful, false on failure. Sets internal error text on failure. */ diff --git a/src/PolarSSL++/CMakeLists.txt b/src/PolarSSL++/CMakeLists.txt index 39d41292d..b11d16e33 100644 --- a/src/PolarSSL++/CMakeLists.txt +++ b/src/PolarSSL++/CMakeLists.txt @@ -37,6 +37,6 @@ if(NOT MSVC) add_library(PolarSSL++ ${SRCS} ${HDRS}) if (UNIX) - target_link_libraries(PolarSSL++ polarssl) + target_link_libraries(PolarSSL++ mbedtls) endif() endif() diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp index c34c75982..ec3a4d85f 100644 --- a/src/ProbabDistrib.cpp +++ b/src/ProbabDistrib.cpp @@ -133,6 +133,7 @@ int cProbabDistrib::MapValue(int a_OrigValue) const // Linearly interpolate between Lo and Hi: int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability; + ProbDif = (ProbDif != 0) ? ProbDif : 1; int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value; return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif; } diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index c9e4296a2..bfbe5028d 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -19,6 +19,10 @@ #define DEFAULT_AUTH_SERVER "sessionserver.mojang.com" #define DEFAULT_AUTH_ADDRESS "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%" + + + + cAuthenticator::cAuthenticator(void) : super("cAuthenticator"), m_Server(DEFAULT_AUTH_SERVER), @@ -40,11 +44,11 @@ cAuthenticator::~cAuthenticator() -void cAuthenticator::ReadINI(cIniFile & IniFile) +void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings) { - m_Server = IniFile.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER); - m_Address = IniFile.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS); - m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); + m_Server = a_Settings.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER); + m_Address = a_Settings.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS); + m_ShouldAuthenticate = a_Settings.GetValueSetB("Authentication", "Authenticate", true); } @@ -69,9 +73,9 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co -void cAuthenticator::Start(cIniFile & IniFile) +void cAuthenticator::Start(cSettingsRepositoryInterface & a_Settings) { - ReadINI(IniFile); + ReadSettings(a_Settings); m_ShouldTerminate = false; super::Start(); } @@ -267,3 +271,7 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a return true; } */ + + + + diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h index 853eff535..02b349256 100644 --- a/src/Protocol/Authenticator.h +++ b/src/Protocol/Authenticator.h @@ -14,7 +14,7 @@ #include "../OSSupport/IsThread.h" - +class cSettingsRepositoryInterface; @@ -40,13 +40,13 @@ public: ~cAuthenticator(); /** (Re-)read server and address from INI: */ - void ReadINI(cIniFile & IniFile); + void ReadSettings(cSettingsRepositoryInterface & a_Settings); /** Queues a request for authenticating a user. If the auth fails, the user will be kicked */ void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); /** Starts the authenticator thread. The thread may be started and stopped repeatedly */ - void Start(cIniFile & IniFile); + void Start(cSettingsRepositoryInterface & a_Settings); /** Stops the authenticator thread. The thread may be started and stopped repeatedly */ void Stop(void); diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index 60fd5f935..2b9c06779 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -68,7 +68,7 @@ const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int void cChunkDataSerializer::Serialize29(AString & a_Data) { - // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib can stream) const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; const int MetadataOffset = sizeof(m_BlockTypes); @@ -126,7 +126,7 @@ void cChunkDataSerializer::Serialize29(AString & a_Data) void cChunkDataSerializer::Serialize39(AString & a_Data) { - // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib can stream) const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; const int MetadataOffset = sizeof(m_BlockTypes); diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 570754204..110590359 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -38,12 +38,36 @@ const int MAX_PER_QUERY = 100; -/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: -Downloaded from http://certs.starfieldtech.com/repository/ */ -static const AString & StarfieldCACert(void) +/** Returns the CA certificates that should be trusted for Mojang-related connections. */ +static const AString & GetCACerts(void) { static const AString Cert( - // G2 cert + // Equifax root CA cert + // Currently used for signing *.mojang.com's cert + // Exported from Mozilla Firefox's built-in CA repository + "-----BEGIN CERTIFICATE-----\n" + "MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV\n" + "UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy\n" + "dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1\n" + "MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx\n" + "dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B\n" + "AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f\n" + "BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A\n" + "cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC\n" + "AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ\n" + "MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm\n" + "aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw\n" + "ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj\n" + "IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF\n" + "MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA\n" + "A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y\n" + "7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh\n" + "1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4\n" + "-----END CERTIFICATE-----\n\n" + + // Starfield G2 cert + // This is the data of the root certs for Starfield Technologies, the CA that used to sign sessionserver.mojang.com's cert + // Downloaded from http://certs.starfieldtech.com/repository/ "-----BEGIN CERTIFICATE-----\n" "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" @@ -67,7 +91,8 @@ static const AString & StarfieldCACert(void) "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" "-----END CERTIFICATE-----\n\n" - // Original (G1) cert: + + // Starfield original (G1) cert: "-----BEGIN CERTIFICATE-----\n" "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" @@ -226,12 +251,12 @@ cMojangAPI::~cMojangAPI() -void cMojangAPI::Start(cIniFile & a_SettingsIni, bool a_ShouldAuth) +void cMojangAPI::Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth) { - m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); - m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); - m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); - m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); + m_NameToUUIDServer = a_Settings.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); + m_NameToUUIDAddress = a_Settings.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + m_UUIDToProfileServer = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); + m_UUIDToProfileAddress = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); LoadCachesFromDisk(); if (a_ShouldAuth) { @@ -390,7 +415,7 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R { // Connect the socket: cBlockingSslClientSocket Socket; - Socket.SetTrustedRootCertsFromString(StarfieldCACert(), a_ServerName); + Socket.SetTrustedRootCertsFromString(GetCACerts(), a_ServerName); if (!Socket.Connect(a_ServerName, 443)) { LOGWARNING("%s: Can't connect to %s: %s", __FUNCTION__, a_ServerName.c_str(), Socket.GetLastErrorText().c_str()); @@ -434,7 +459,6 @@ bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_R a_Response.append((const char *)buf, (size_t)ret); } - Socket.Disconnect(); return true; } @@ -659,7 +683,7 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery) a_NamesToQuery.erase(a_NamesToQuery.begin(), itr); Json::FastWriter Writer; AString RequestBody = Writer.write(root); - + // Create the HTTP request: AString Request; Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding @@ -667,7 +691,7 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery) Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; Request += "Content-Type: application/json\r\n"; - Request += Printf("Content-Length: %u\r\n", (unsigned)RequestBody.length()); + Request += Printf("Content-Length: %u\r\n", static_cast<unsigned>(RequestBody.length())); Request += "\r\n"; Request += RequestBody; diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h index 0dc2617b6..bea950740 100644 --- a/src/Protocol/MojangAPI.h +++ b/src/Protocol/MojangAPI.h @@ -25,7 +25,7 @@ namespace Json - +class cSettingsRepositoryInterface; // tolua_begin class cMojangAPI @@ -38,7 +38,7 @@ public: /** Initializes the API; reads the settings from the specified ini file. Loads cached results from disk. */ - void Start(cIniFile & a_SettingsIni, bool a_ShouldAuth); + void Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth); /** Connects to the specified server using SSL, sends the given request and receives the response. Checks Mojang certificates using the hard-coded Starfield root CA certificate. diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 95adc23e9..7be72014a 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -16,6 +16,8 @@ #include "../Map.h" #include "../ByteBuffer.h" +#include <array> + @@ -99,6 +101,7 @@ public: virtual void SendPlayerAbilities (void) = 0; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0; virtual void SendParticleEffect (const AString & a_SoundName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) = 0; + virtual void SendParticleEffect (const AString & a_SoundName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) = 0; virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) = 0; virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) = 0; virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) = 0; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index e1e48e43f..0bd219fb1 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -492,7 +492,7 @@ void cProtocol172::SendEntityVelocity(const cEntity & a_Entity) cPacketizer Pkt(*this, 0x12); // Entity Velocity packet Pkt.WriteBEUInt32(a_Entity.GetUniqueID()); - // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick + // 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick Pkt.WriteBEInt16(static_cast<short>(a_Entity.GetSpeedX() * 400)); Pkt.WriteBEInt16(static_cast<short>(a_Entity.GetSpeedY() * 400)); Pkt.WriteBEInt16(static_cast<short>(a_Entity.GetSpeedZ() * 400)); @@ -813,6 +813,16 @@ void cProtocol172::SendParticleEffect(const AString & a_ParticleName, float a_Sr +void cProtocol172::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) +{ + // 1.72 doesn't support extra data + this->SendParticleEffect(a_ParticleName, a_Src.x, a_Src.y, a_Src.z, a_Offset.x, a_Offset.y, a_Offset.z, a_ParticleData, a_ParticleAmount); +} + + + + + void cProtocol172::SendPlayerListAddPlayer(const cPlayer & a_Player) { ASSERT(m_State == 3); // In game mode? @@ -1791,7 +1801,7 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; - Version["name"] = "1.7.2"; + Version["name"] = "MCServer 1.7.2"; Version["protocol"] = 4; // Players: @@ -2567,7 +2577,7 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings { - AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent/backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;) + AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent / backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;) } a_Item.m_Lore = Lore; @@ -3233,7 +3243,7 @@ void cProtocol176::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; - Version["name"] = "1.7.6"; + Version["name"] = "MCServer 1.7.6"; Version["protocol"] = 5; // Players: diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 553856eaa..ead2935b0 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -100,6 +100,7 @@ public: virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override; + virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override; diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 20b55f461..d06022ce0 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -479,7 +479,7 @@ void cProtocol180::SendEntityVelocity(const cEntity & a_Entity) cPacketizer Pkt(*this, 0x12); // Entity Velocity packet Pkt.WriteVarInt32(a_Entity.GetUniqueID()); - // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick + // 400 = 8000 / 20 ... Conversion from our speed in m / s to 8000 m / tick Pkt.WriteBEInt16((short)(a_Entity.GetSpeedX() * 400)); Pkt.WriteBEInt16((short)(a_Entity.GetSpeedY() * 400)); Pkt.WriteBEInt16((short)(a_Entity.GetSpeedZ() * 400)); @@ -814,6 +814,50 @@ void cProtocol180::SendParticleEffect(const AString & a_ParticleName, float a_Sr +void cProtocol180::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) +{ + ASSERT(m_State == 3); // In game mode? + int ParticleID = GetParticleID(a_ParticleName); + + cPacketizer Pkt(*this, 0x2A); + Pkt.WriteBEInt32(ParticleID); + Pkt.WriteBool(false); + Pkt.WriteBEFloat(a_Src.x); + Pkt.WriteBEFloat(a_Src.y); + Pkt.WriteBEFloat(a_Src.z); + Pkt.WriteBEFloat(a_Offset.x); + Pkt.WriteBEFloat(a_Offset.y); + Pkt.WriteBEFloat(a_Offset.z); + Pkt.WriteBEFloat(a_ParticleData); + Pkt.WriteBEInt32(a_ParticleAmount); + switch (ParticleID) + { + // iconcrack + case 36: + { + Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[0])); + Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[1])); + break; + } + // blockcrack + // blockdust + case 37: + case 38: + { + Pkt.WriteVarInt32(static_cast<UInt32>(a_Data[0])); + break; + } + default: + { + break; + } + } +} + + + + + void cProtocol180::SendPlayerListAddPlayer(const cPlayer & a_Player) { ASSERT(m_State == 3); // In game mode? @@ -2056,7 +2100,7 @@ void cProtocol180::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) // Version: Json::Value Version; - Version["name"] = "1.8"; + Version["name"] = "MCServer 1.8"; Version["protocol"] = 47; // Players: @@ -2192,7 +2236,7 @@ void cProtocol180::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer) void cProtocol180::HandlePacketAnimation(cByteBuffer & a_ByteBuffer) { - m_Client->HandleAnimation(1); // Packet exists solely for arm-swing notification + m_Client->HandleAnimation(0); // Packet exists solely for arm-swing notification } @@ -2307,7 +2351,7 @@ void cProtocol180::HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffe { return; } - m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftClickOutside : caLeftClick); + m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == -1) ? caLeftClickOutside : caLeftClick); } @@ -2396,7 +2440,7 @@ void cProtocol180::HandlePacketPlayerPos(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosY); HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosZ); HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround); - m_Client->HandlePlayerPos(PosX, PosY, PosZ, PosY + 1.62, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, PosY + (m_Client->GetPlayer()->IsCrouched() ? 1.54 : 1.62), IsOnGround); } @@ -2761,7 +2805,7 @@ void cProtocol180::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) if (!NBT.IsValid()) { AString HexDump; - CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + CreateHexDump(HexDump, a_Metadata.data(), std::max<size_t>(a_Metadata.size(), 1024), 16); LOGWARNING("Cannot parse NBT item metadata: (" SIZE_T_FMT " bytes)\n%s", a_Metadata.size(), HexDump.c_str()); return; } @@ -2796,7 +2840,7 @@ void cProtocol180::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings { - AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent/backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;) + AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent / backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;) } a_Item.m_Lore = Lore; diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h index 06a57b216..6143e8b4e 100644 --- a/src/Protocol/Protocol18x.h +++ b/src/Protocol/Protocol18x.h @@ -98,6 +98,7 @@ public: virtual void SendPlayerAbilities (void) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override; + virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override; virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override; virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override; virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 6c599dedc..477f2d71e 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -449,6 +449,17 @@ void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, flo + +void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) +{ + ASSERT(m_Protocol != nullptr); + m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data); +} + + + + + void cProtocolRecognizer::SendPaintingSpawn(const cPainting & a_Painting) { m_Protocol->SendPaintingSpawn(a_Painting); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 8454549e3..956b5dcc0 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -18,7 +18,7 @@ // Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.7.x, 1.8" +#define MCS_CLIENT_VERSIONS "1.7.x, 1.8.x" #define MCS_PROTOCOL_VERSIONS "4, 5, 47" @@ -82,6 +82,7 @@ public: virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) override; virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override; + virtual void SendParticleEffect (const AString & a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data) override; virtual void SendPaintingSpawn (const cPainting & a_Painting) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index 685bd92f5..c5dc9b69b 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -134,15 +134,15 @@ cRCONServer::~cRCONServer() -void cRCONServer::Initialize(cIniFile & a_IniFile) +void cRCONServer::Initialize(cSettingsRepositoryInterface & a_Settings) { - if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) + if (!a_Settings.GetValueSetB("RCON", "Enabled", false)) { return; } // Read the password, don't allow an empty one: - m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); + m_Password = a_Settings.GetValueSet("RCON", "Password", ""); if (m_Password.empty()) { LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); @@ -150,7 +150,7 @@ void cRCONServer::Initialize(cIniFile & a_IniFile) } // Read the listening ports for RCON from config: - AStringVector Ports = ReadUpgradeIniPorts(a_IniFile, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575"); + AStringVector Ports = ReadUpgradeIniPorts(a_Settings, "RCON", "Ports", "PortsIPv4", "PortsIPv6", "25575"); // Start listening on each specified port: for (auto port: Ports) diff --git a/src/RCONServer.h b/src/RCONServer.h index 352fa7b50..ecd936eeb 100644 --- a/src/RCONServer.h +++ b/src/RCONServer.h @@ -17,7 +17,7 @@ // fwd: class cServer; -class cIniFile; +class cSettingsRepositoryInterface; @@ -29,7 +29,7 @@ public: cRCONServer(cServer & a_Server); virtual ~cRCONServer(); - void Initialize(cIniFile & a_IniFile); + void Initialize(cSettingsRepositoryInterface & a_Settings); protected: friend class cRCONCommandOutput; @@ -61,7 +61,7 @@ protected: // cTCPLink::cCallbacks overrides: - virtual void OnLinkCreated(cTCPLinkPtr a_Link); + virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; virtual void OnReceivedData(const char * a_Data, size_t a_Length) override; virtual void OnRemoteClosed(void) override; virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override; diff --git a/src/RankManager.cpp b/src/RankManager.cpp index 451de88e7..ee39026ea 100644 --- a/src/RankManager.cpp +++ b/src/RankManager.cpp @@ -414,6 +414,7 @@ void cRankManager::Initialize(cMojangAPI & a_MojangAPI) m_DB.exec("CREATE TABLE IF NOT EXISTS PermGroup (PermGroupID INTEGER PRIMARY KEY, Name)"); m_DB.exec("CREATE TABLE IF NOT EXISTS RankPermGroup (RankID INTEGER, PermGroupID INTEGER)"); m_DB.exec("CREATE TABLE IF NOT EXISTS PermissionItem (PermGroupID INTEGER, Permission)"); + m_DB.exec("CREATE TABLE IF NOT EXISTS RestrictionItem (PermGroupID INTEGER, Permission)"); m_DB.exec("CREATE TABLE IF NOT EXISTS DefaultRank (RankID INTEGER)"); m_IsInitialized = true; @@ -571,6 +572,20 @@ AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID) +AStringVector cRankManager::GetPlayerRestrictions(const AString & a_PlayerUUID) +{ + AString Rank = GetPlayerRankName(a_PlayerUUID); + if (Rank.empty()) + { + Rank = m_DefaultRank; + } + return GetRankRestrictions(Rank); +} + + + + + AStringVector cRankManager::GetRankGroups(const AString & a_RankName) { ASSERT(m_IsInitialized); @@ -632,6 +647,36 @@ AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName) +AStringVector cRankManager::GetGroupRestrictions(const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, + "SELECT RestrictionItem.Permission FROM RestrictionItem " + "LEFT JOIN PermGroup ON PermGroup.PermGroupID = RestrictionItem.PermGroupID " + "WHERE PermGroup.Name = ?" + ); + stmt.bind(1, a_GroupName); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get group restrictions from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + AStringVector cRankManager::GetRankPermissions(const AString & a_RankName) { ASSERT(m_IsInitialized); @@ -663,6 +708,37 @@ AStringVector cRankManager::GetRankPermissions(const AString & a_RankName) +AStringVector cRankManager::GetRankRestrictions(const AString & a_RankName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, + "SELECT RestrictionItem.Permission FROM RestrictionItem " + "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = RestrictionItem.PermGroupID " + "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID " + "WHERE Rank.Name = ?" + ); + stmt.bind(1, a_RankName); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get rank restrictions from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + AStringVector cRankManager::GetAllPlayerUUIDs(void) { ASSERT(m_IsInitialized); @@ -764,6 +840,46 @@ AStringVector cRankManager::GetAllPermissions(void) +AStringVector cRankManager::GetAllRestrictions(void) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + AStringVector res; + try + { + SQLite::Statement stmt(m_DB, "SELECT DISTINCT(Permission) FROM RestrictionItem"); + while (stmt.executeStep()) + { + res.push_back(stmt.getColumn(0).getText()); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to get restrictions from DB: %s", __FUNCTION__, ex.what()); + } + return res; +} + + + + + +AStringVector cRankManager::GetAllPermissionsRestrictions(void) +{ + AStringVector Permissions = GetAllPermissions(); + AStringVector Restrictions = GetAllRestrictions(); + for (auto & restriction: Restrictions) + { + Permissions.push_back(restriction); + } + return Permissions; +} + + + + + bool cRankManager::GetPlayerMsgVisuals( const AString & a_PlayerUUID, AString & a_MsgPrefix, @@ -1063,6 +1179,73 @@ bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AStr +bool cRankManager::AddRestrictionToGroup(const AString & a_Restriction, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the group's ID: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str()); + return false; + } + GroupID = stmt.getColumn(0).getInt(); + } + + // Check if the restriction is already present: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM RestrictionItem WHERE PermGroupID = ? AND Permission = ?"); + stmt.bind(1, GroupID); + stmt.bind(2, a_Restriction); + if (!stmt.executeStep()) + { + LOGWARNING("%s: Failed to check binding between restriction %s and group %s, aborting.", __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str()); + return false; + } + if (stmt.getColumn(0).getInt() > 0) + { + LOGD("%s: Restriction %s is already present in group %s, skipping and returning success.", + __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str() + ); + return true; + } + } + + // Add the restriction: + { + SQLite::Statement stmt(m_DB, "INSERT INTO RestrictionItem (Permission, PermGroupID) VALUES (?, ?)"); + stmt.bind(1, a_Restriction); + stmt.bind(2, GroupID); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add restriction %s to group %s, aborting.", __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str()); + return false; + } + } + + // Adding succeeded: + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add restriction %s to group %s: %s", + __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str(), ex.what() + ); + } + return false; +} + + + + + bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName) { ASSERT(m_IsInitialized); @@ -1133,6 +1316,76 @@ bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, co +bool cRankManager::AddRestrictionsToGroup(const AStringVector & a_Restrictions, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the group's ID: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str()); + return false; + } + GroupID = stmt.getColumn(0).getInt(); + } + + for (auto itr = a_Restrictions.cbegin(), end = a_Restrictions.cend(); itr != end; ++itr) + { + // Check if the restriction is already present: + { + SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM RestrictionItem WHERE PermGroupID = ? AND Permission = ?"); + stmt.bind(1, GroupID); + stmt.bind(2, *itr); + if (!stmt.executeStep()) + { + LOGWARNING("%s: Failed to check binding between restriction %s and group %s, aborting.", __FUNCTION__, itr->c_str(), a_GroupName.c_str()); + return false; + } + if (stmt.getColumn(0).getInt() > 0) + { + LOGD("%s: Restriction %s is already present in group %s, skipping and returning success.", + __FUNCTION__, itr->c_str(), a_GroupName.c_str() + ); + continue; + } + } + + // Add the permission: + { + SQLite::Statement stmt(m_DB, "INSERT INTO RestrictionItem (Permission, PermGroupID) VALUES (?, ?)"); + stmt.bind(1, *itr); + stmt.bind(2, GroupID); + if (stmt.exec() <= 0) + { + LOGWARNING("%s: Failed to add restriction %s to group %s, skipping.", __FUNCTION__, itr->c_str(), a_GroupName.c_str()); + continue; + } + } + } // for itr - a_Restrictions[] + + // Adding succeeded: + return true; + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to add restrictions to group %s: %s", + __FUNCTION__, a_GroupName.c_str(), ex.what() + ); + } + return false; +} + + + + + void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName) { ASSERT(m_IsInitialized); @@ -1362,6 +1615,46 @@ void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const +void cRankManager::RemoveRestrictionFromGroup(const AString & a_Restriction, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + // Get the ID of the group: + int GroupID; + { + SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?"); + stmt.bind(1, a_GroupName); + if (!stmt.executeStep()) + { + LOGINFO("%s: Group %s was not found, skipping.", __FUNCTION__, a_GroupName.c_str()); + return; + } + GroupID = stmt.getColumn(0).getInt(); + } + + // Remove the permission from the group: + { + SQLite::Statement stmt(m_DB, "DELETE FROM RestrictionItem WHERE PermGroupID = ? AND Permission = ?"); + stmt.bind(1, GroupID); + stmt.bind(2, a_Restriction); + stmt.exec(); + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to remove restriction %s from group %s in DB: %s", + __FUNCTION__, a_Restriction.c_str(), a_GroupName.c_str(), ex.what() + ); + } +} + + + + + bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName) { ASSERT(m_IsInitialized); @@ -1744,6 +2037,37 @@ bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AStri +bool cRankManager::IsRestrictionInGroup(const AString & a_Restriction, const AString & a_GroupName) +{ + ASSERT(m_IsInitialized); + cCSLock Lock(m_CS); + + try + { + SQLite::Statement stmt(m_DB, + "SELECT * FROM RestrictionItem " + "LEFT JOIN PermGroup ON PermGroup.PermGroupID = RestrictionItem.PermGroupID " + "WHERE RestrictionItem.Permission = ? AND PermGroup.Name = ?" + ); + stmt.bind(1, a_Restriction); + stmt.bind(2, a_GroupName); + if (stmt.executeStep()) + { + // The restriction is in the group + return true; + } + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what()); + } + return false; +} + + + + + void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID) { ASSERT(m_IsInitialized); @@ -1829,7 +2153,7 @@ void cRankManager::ClearPlayerRanks(void) } catch (SQLite::Exception & ex) { - LOGWARNING("%s: Failed to remove/clear all players: %s", __FUNCTION__, ex.what()); + LOGWARNING("%s: Failed to remove / clear all players: %s", __FUNCTION__, ex.what()); } } @@ -1936,3 +2260,58 @@ void cRankManager::CreateDefaults(void) +bool cRankManager::DoesColumnExist(const char * a_TableName, const char * a_ColumnName) +{ + try + { + SQLite::Statement stmt(m_DB, Printf("PRAGMA table_info(%s)", a_TableName)); + while (stmt.executeStep()) // Iterate over all table's columns + { + int NumColumns = stmt.getColumnCount(); + for (int i = 0; i < NumColumns; i++) // Iterate over all reply's columns (table column's metadata) + { + auto column = stmt.getColumn(i); + if (strcmp(column.getName(), "name") == 0) + { + if (NoCaseCompare(column.getText(), a_ColumnName) == 0) + { + // Colun found + return true; + } + } + } // for i - stmt.getColumns() + } // while (stmt.executeStep()) + } + catch (const SQLite::Exception & ex) + { + LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what()); + } + return false; +} + + + + + +void cRankManager::CreateColumnIfNotExists(const char * a_TableName, const char * a_ColumnName, const char * a_ColumnType) +{ + // If the column already exists, bail out: + if (DoesColumnExist(a_TableName, a_ColumnName)) + { + return; + } + + // Add the column: + try + { + m_DB.exec(Printf("ALTER TABLE %s ADD COLUMN %s %s", a_TableName, a_ColumnName, a_ColumnType)); + } + catch (const SQLite::Exception & exc) + { + LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, exc.what()); + } +} + + + + diff --git a/src/RankManager.h b/src/RankManager.h index 5dff634b5..b3431b7d1 100644 --- a/src/RankManager.h +++ b/src/RankManager.h @@ -71,6 +71,10 @@ public: If the player has no rank assigned to them, returns the default rank's permissions. */ AStringVector GetPlayerPermissions(const AString & a_PlayerUUID); + /** Returns the restrictions that the specified player has assigned to them. + If the player has no rank assigned to them, returns the default rank's restrictions. */ + AStringVector GetPlayerRestrictions(const AString & a_PlayerUUID); + /** Returns the names of groups that the specified rank has assigned to it. Returns an empty vector if the rank doesn't exist. */ AStringVector GetRankGroups(const AString & a_RankName); @@ -79,10 +83,18 @@ public: Returns an empty vector if the group doesn't exist. */ AStringVector GetGroupPermissions(const AString & a_GroupName); + /** Returns the restrictions that the specified group has assigned to it. + Returns an empty vector if the group doesn't exist. */ + AStringVector GetGroupRestrictions(const AString & a_GroupName); + /** Returns all permissions that the specified rank has assigned to it, through all its groups. Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */ AStringVector GetRankPermissions(const AString & a_RankName); + /** Returns all restrictions that the specified rank has assigned to it, through all its groups. + Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */ + AStringVector GetRankRestrictions(const AString & a_RankName); + /** Returns the short uuids of all defined players. The returned players are ordered by their name (NOT their UUIDs). */ AStringVector GetAllPlayerUUIDs(void); @@ -95,6 +107,12 @@ public: /** Returns all the distinct permissions that are stored in the DB. */ AStringVector GetAllPermissions(void); + /** Returns all the distinct restrictions that are stored in the DB. */ + AStringVector GetAllRestrictions(void); + + /** Returns all the distinct permissions and restrictions that are stored in the DB. */ + AStringVector GetAllPermissionsRestrictions(void); + /** Returns the message visuals (prefix, postfix, color) for the specified player. Returns true if the visuals were read from the DB, false if not (player not found etc). */ bool GetPlayerMsgVisuals( @@ -128,17 +146,27 @@ public: Returns true if successful, false on error. */ bool AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName); + /** Adds the specified restriction to the specified group. + Fails if the group name is not found. + Returns true if successful, false on error. */ + bool AddRestrictionToGroup(const AString & a_Restriction, const AString & a_GroupName); + /** Adds the specified permissions to the specified permission group. Fails if the permission group name is not found. Returns true if successful, false on error. */ bool AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName); + /** Adds the specified restrictions to the specified group. + Fails if the group name is not found. + Returns true if successful, false on error. */ + bool AddRestrictionsToGroup(const AStringVector & a_Restrictions, const AString & a_GroupName); + /** Removes the specified rank. All players assigned to that rank will be re-assigned to a_ReplacementRankName. If a_ReplacementRankName is empty or not a valid rank, the player will be removed from the DB, which means they will receive the default rank the next time they are queried. If the rank being removed is the default rank, the default will be changed to the replacement - rank; the operation fails if there's no replacement. */ + rank; the operation fails silently if there's no replacement. */ void RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName); /** Removes the specified group completely. @@ -152,6 +180,9 @@ public: /** Removes the specified permission from the specified group. */ void RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName); + /** Removes the specified restriction from the specified group. */ + void RemoveRestrictionFromGroup(const AString & a_Restriction, const AString & a_GroupName); + /** Renames the specified rank. No action if the rank name is not found. Fails if the new name is already used. Updates the cached m_DefaultRank if the default rank is being renamed. @@ -208,6 +239,9 @@ public: /** Returns true iff the specified group contains the specified permission. */ bool IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName); + /** Returns true iff the specified group contains the specified restriction. */ + bool IsRestrictionInGroup(const AString & a_Restriction, const AString & a_GroupName); + /** Called by cMojangAPI whenever the playername-uuid pairing is discovered. Updates the DB. */ void NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID); @@ -253,6 +287,13 @@ protected: /** Creates a default set of ranks / groups / permissions. */ void CreateDefaults(void); + + /** Returns true if the specified column exists in the specified table. */ + bool DoesColumnExist(const char * a_TableName, const char * a_ColumnName); + + /** If the specified table doesn't contain the specified column, it is added to the table. + The column type is used only when creating the column, it is not used when checking for existence. */ + void CreateColumnIfNotExists(const char * a_TableName, const char * a_ColumnName, const char * a_ColumnType = ""); } ; diff --git a/src/Root.cpp b/src/Root.cpp index 87bc29627..b28e7c894 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -19,6 +19,8 @@ #include "LoggerListeners.h" #include "BuildInfo.h" #include "IniFile.h" +#include "SettingsRepositoryInterface.h" +#include "OverridesSettingsRepository.h" #ifdef _WIN32 #include <conio.h> @@ -33,7 +35,8 @@ -cRoot* cRoot::s_Root = nullptr; +cRoot * cRoot::s_Root = nullptr; +bool cRoot::m_ShouldStop = false; @@ -48,7 +51,6 @@ cRoot::cRoot(void) : m_WebAdmin(nullptr), m_PluginManager(nullptr), m_MojangAPI(nullptr), - m_bStop(false), m_bRestart(false) { s_Root = this; @@ -70,8 +72,8 @@ cRoot::~cRoot() void cRoot::InputThread(cRoot & a_Params) { cLogCommandOutputCallback Output; - - while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good()) + + while (!cRoot::m_ShouldStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good()) { AString Command; std::getline(std::cin, Command); @@ -83,10 +85,11 @@ void cRoot::InputThread(cRoot & a_Params) if (m_TerminateEventRaised || !std::cin.good()) { - // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server: - if (m_RunAsService) // HACK: Dont kill if running as a service + // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running + // Stop the server: + if (!m_RunAsService) // Dont kill if running as a service { - a_Params.m_bStop = true; + a_Params.m_ShouldStop = true; } } } @@ -95,19 +98,19 @@ void cRoot::InputThread(cRoot & a_Params) -void cRoot::Start(void) +void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) { #ifdef _WIN32 HWND hwnd = GetConsoleWindow(); HMENU hmenu = GetSystemMenu(hwnd, FALSE); EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling #endif - + cLogger::cListener * consoleLogListener = MakeConsoleListener(); cLogger::cListener * fileLogListener = new cFileListener(); cLogger::GetInstance().AttachListener(consoleLogListener); cLogger::GetInstance().AttachListener(fileLogListener); - + LOG("--- Started Log ---\n"); #ifdef BUILD_ID @@ -117,8 +120,8 @@ void cRoot::Start(void) cDeadlockDetect dd; - m_bStop = false; - while (!m_bStop) + m_ShouldStop = false; + while (!m_ShouldStop) { auto BeginTime = std::chrono::steady_clock::now(); m_bRestart = false; @@ -129,22 +132,24 @@ void cRoot::Start(void) m_Server = new cServer(); LOG("Reading server config..."); - cIniFile IniFile; - if (!IniFile.ReadFile("settings.ini")) + + auto IniFile = cpp14::make_unique<cIniFile>(); + if (!IniFile->ReadFile("settings.ini")) { LOGWARN("Regenerating settings.ini, all settings will be reset"); - IniFile.AddHeaderComment(" This is the main server configuration"); - IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); - IniFile.AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); + IniFile->AddHeaderComment(" This is the main server configuration"); + IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); + IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); } + auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo)); LOG("Starting server..."); m_MojangAPI = new cMojangAPI; - bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); - m_MojangAPI->Start(IniFile, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init - if (!m_Server->InitServer(IniFile, ShouldAuthenticate)) + bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true); + m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init + if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate)) { - IniFile.WriteFile("settings.ini"); + settingsRepo->Flush(); LOGERROR("Failure starting server, aborting..."); return; } @@ -157,31 +162,31 @@ void cRoot::Start(void) m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); - + LOGD("Loading worlds..."); - LoadWorlds(IniFile); + LoadWorlds(*settingsRepo); LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); - m_PluginManager->ReloadPluginsNow(IniFile); - + m_PluginManager->ReloadPluginsNow(*settingsRepo); + LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion LOGD("Starting Authenticator..."); - m_Authenticator.Start(IniFile); - + m_Authenticator.Start(*settingsRepo); + LOGD("Starting worlds..."); StartWorlds(); - - if (IniFile.GetValueSetB("DeadlockDetect", "Enabled", true)) + + if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true)) { LOGD("Starting deadlock detector..."); - dd.Start(IniFile.GetValueSetI("DeadlockDetect", "IntervalSec", 20)); + dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20)); } - - IniFile.WriteFile("settings.ini"); + + settingsRepo->Flush(); LOGD("Finalising startup..."); if (m_Server->Start()) @@ -202,18 +207,22 @@ void cRoot::Start(void) #endif LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); + + // Save the current time + m_StartTime = std::chrono::steady_clock::now(); + #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif - while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads + while (!m_ShouldStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads { std::this_thread::sleep_for(std::chrono::seconds(1)); } if (m_TerminateEventRaised) { - m_bStop = true; + m_ShouldStop = true; } // Stop the server: @@ -224,7 +233,7 @@ void cRoot::Start(void) } // if (m_Server->Start()) else { - m_bStop = true; + m_ShouldStop = true; } delete m_MojangAPI; m_MojangAPI = nullptr; @@ -248,7 +257,7 @@ void cRoot::Start(void) LOGD("Unloading worlds..."); UnloadWorlds(); - + LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = nullptr; @@ -259,9 +268,9 @@ void cRoot::Start(void) LOG("Shutdown successful!"); } - + LOG("--- Stopped Log ---"); - + cLogger::GetInstance().DetachListener(consoleLogListener); delete consoleLogListener; cLogger::GetInstance().DetachListener(fileLogListener); @@ -271,13 +280,6 @@ void cRoot::Start(void) -void cRoot::SetStopping(bool a_Stopping) -{ - m_bStop = a_Stopping; -} - - - void cRoot::LoadGlobalSettings() { @@ -288,45 +290,44 @@ void cRoot::LoadGlobalSettings() -void cRoot::LoadWorlds(cIniFile & IniFile) +void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings) { // First get the default world - AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); + AString DefaultWorldName = a_Settings.GetValueSet("Worlds", "DefaultWorld", "world"); m_pDefaultWorld = new cWorld(DefaultWorldName.c_str()); m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; // Then load the other worlds - int KeyNum = IniFile.FindKey("Worlds"); - int NumWorlds = IniFile.GetNumValues(KeyNum); - if (NumWorlds <= 0) + auto Worlds = a_Settings.GetValues("Worlds"); + if (Worlds.size() <= 0) { return; } - + bool FoundAdditionalWorlds = false; - for (int i = 0; i < NumWorlds; i++) + for (auto WorldNameValue : Worlds) { - AString ValueName = IniFile.GetValueName(KeyNum, i); + AString ValueName = WorldNameValue.first; if (ValueName.compare("World") != 0) { continue; } - AString WorldName = IniFile.GetValue(KeyNum, i); + AString WorldName = WorldNameValue.second; if (WorldName.empty()) { continue; } FoundAdditionalWorlds = true; - cWorld* NewWorld = new cWorld( WorldName.c_str()); - m_WorldsByName[ WorldName ] = NewWorld; + cWorld * NewWorld = new cWorld(WorldName.c_str()); + m_WorldsByName[WorldName] = NewWorld; } // for i - Worlds if (!FoundAdditionalWorlds) { - if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld") + if (a_Settings.GetKeyComment("Worlds", 0) != " World=secondworld") { - IniFile.DeleteKeyComment("Worlds", 0); - IniFile.AddKeyComment("Worlds", " World=secondworld"); + a_Settings.DeleteKeyComment("Worlds", 0); + a_Settings.AddKeyComment("Worlds", " World=secondworld"); } } } @@ -466,7 +467,7 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCall // Some commands are built-in: if (a_Cmd == "stop") { - m_bStop = true; + m_ShouldStop = true; } else if (a_Cmd == "restart") { @@ -498,7 +499,7 @@ void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback // cRoot handles stopping and restarting due to our access to controlling variables if (a_Cmd == "stop") { - m_bStop = true; + m_ShouldStop = true; return; } else if (a_Cmd == "restart") @@ -613,7 +614,7 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac size_t Rating = RateCompareString (m_PlayerName, a_pPlayer->GetName()); if ((Rating > 0) && (Rating >= m_BestRating)) { - m_BestMatch = a_pPlayer; + m_BestMatch = a_pPlayer->GetName(); if (Rating > m_BestRating) { m_NumMatches = 0; @@ -633,18 +634,18 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac m_BestRating(0), m_NameLength(a_PlayerName.length()), m_PlayerName(a_PlayerName), - m_BestMatch(nullptr), + m_BestMatch(), m_NumMatches(0) {} - cPlayer * m_BestMatch; + AString m_BestMatch; unsigned m_NumMatches; } Callback (a_PlayerName); ForEachPlayer(Callback); if (Callback.m_NumMatches == 1) { - return a_Callback.Item(Callback.m_BestMatch); + return DoWithPlayer(Callback.m_BestMatch, a_Callback); } return false; } @@ -853,7 +854,3 @@ int cRoot::GetFurnaceFuelBurnTime(const cItem & a_Fuel) cFurnaceRecipe * FR = Get()->GetFurnaceRecipe(); return FR->GetBurnTime(a_Fuel); } - - - - diff --git a/src/Root.h b/src/Root.h index d552466dc..ab820427f 100644 --- a/src/Root.h +++ b/src/Root.h @@ -24,6 +24,7 @@ class cWorld; class cPlayer; class cCommandOutputCallback; class cCompositeChat; +class cSettingsRepositoryInterface; typedef cItemCallback<cPlayer> cPlayerListCallback; typedef cItemCallback<cWorld> cWorldListCallback; @@ -47,15 +48,13 @@ public: static bool m_TerminateEventRaised; static bool m_RunAsService; + static bool m_ShouldStop; cRoot(void); ~cRoot(); - void Start(void); - - // Added so the service handler can request a stop - void SetStopping(bool a_Stopping); + void Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo); // tolua_begin cServer * GetServer(void) { return m_Server; } @@ -71,22 +70,31 @@ public: a_OverworldName should be set for non-overworld dimensions if one wishes that world to link back to an overworld via portals */ cWorld * CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = ""); + + /** 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 << - + /// Writes chunkstats, for each world and totals, to the output callback void LogChunkStats(cCommandOutputCallback & a_Output); - + cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature - + /// 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 - + + /** 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; } @@ -99,32 +107,32 @@ public: "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 - + /// Executes a console command through the cServer class; does special handling for "stop" and "restart". void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); - + /// Kicks the user, no matter in what world they are. Used from cAuthenticator void KickUser(int a_ClientID, const AString & a_Reason); - + /// Called by cAuthenticator to auth the specified user void AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties); - + /// Executes commands queued in the command queue void TickCommands(void); /// Returns the number of chunks loaded int GetTotalChunkCount(void); // tolua_export - + /// Saves all chunks in all worlds void SaveAllChunks(void); // tolua_export - + /// Calls the callback for each player in all worlds bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << @@ -133,12 +141,12 @@ public: /** Finds the player over his uuid and calls the callback */ bool DoWithPlayerByUUID(const AString & 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); - + // 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); @@ -150,18 +158,18 @@ public: 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 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); - + // tolua_end - + private: class cCommand { @@ -171,17 +179,17 @@ private: m_Output(a_Output) { } - + AString m_Command; cCommandOutputCallback * m_Output; } ; - + typedef std::map<AString, cWorld *> WorldMap; typedef std::vector<cCommand> cCommandQueue; cWorld * m_pDefaultWorld; WorldMap m_WorldsByName; - + cCriticalSection m_CSPendingCommands; cCommandQueue m_PendingCommands; @@ -201,31 +209,26 @@ private: cHTTPServer m_HTTPServer; - bool m_bStop; bool m_bRestart; void LoadGlobalSettings(); /// Loads the worlds from settings.ini, creates the worldmap - void LoadWorlds(cIniFile & IniFile); - + void LoadWorlds(cSettingsRepositoryInterface & a_Settings); + /// Starts each world's life void StartWorlds(void); - + /// Stops each world's threads, so that it's safe to unload them void StopWorlds(void); - + /// Unloads all worlds from memory void UnloadWorlds(void); - + /// Does the actual work of executing a command void DoExecuteConsoleCommand(const AString & a_Cmd); - - static cRoot* s_Root; + + static cRoot * s_Root; static void InputThread(cRoot & a_Params); }; // tolua_export - - - - diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index adce8b549..a4a4c3391 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -286,7 +286,7 @@ cScoreboard::cScoreboard(cWorld * a_World) : m_World(a_World) -cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) +cObjective * cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type) { cObjective Objective(a_Name, a_DisplayName, a_Type, m_World); @@ -471,7 +471,7 @@ cObjective * cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot) -bool cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback) +bool cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback & a_Callback) { cCSLock Lock(m_CSObjectives); @@ -493,7 +493,7 @@ bool cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallb -bool cScoreboard::ForEachObjective(cObjectiveCallback& a_Callback) +bool cScoreboard::ForEachObjective(cObjectiveCallback & a_Callback) { cCSLock Lock(m_CSObjectives); @@ -512,7 +512,7 @@ bool cScoreboard::ForEachObjective(cObjectiveCallback& a_Callback) -bool cScoreboard::ForEachTeam(cTeamCallback& a_Callback) +bool cScoreboard::ForEachTeam(cTeamCallback & a_Callback) { cCSLock Lock(m_CSTeams); diff --git a/src/Scoreboard.h b/src/Scoreboard.h index dd4073c89..ab10b20aa 100644 --- a/src/Scoreboard.h +++ b/src/Scoreboard.h @@ -262,19 +262,16 @@ public: cTeam * QueryPlayerTeam(const AString & a_Name); // WARNING: O(n logn) /** Execute callback for each objective with the specified type - Returns true if all objectives processed, false if the callback aborted by returning true. - */ - bool ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback); + Returns true if all objectives processed, false if the callback aborted by returning true. */ + bool ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback & a_Callback); /** Execute callback for each objective. - Returns true if all objectives have been processed, false if the callback aborted by returning true. - */ - bool ForEachObjective(cObjectiveCallback& a_Callback); // Exported in ManualBindings.cpp + Returns true if all objectives have been processed, false if the callback aborted by returning true. */ + bool ForEachObjective(cObjectiveCallback & a_Callback); // Exported in ManualBindings.cpp /** Execute callback for each team. - Returns true if all teams have been processed, false if the callback aborted by returning true. - */ - bool ForEachTeam(cTeamCallback& a_Callback); // Exported in ManualBindings.cpp + Returns true if all teams have been processed, false if the callback aborted by returning true. */ + bool ForEachTeam(cTeamCallback & a_Callback); // Exported in ManualBindings.cpp void SetDisplay(cObjective * a_Objective, eDisplaySlot a_Slot); diff --git a/src/Server.cpp b/src/Server.cpp index df2c7deef..fd3188b18 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -72,7 +72,7 @@ class cServerListenCallbacks: virtual void OnAccepted(cTCPLink & a_Link) override {} - virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) + 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()); } @@ -113,7 +113,7 @@ void cServer::cTickThread::Execute(void) auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count(); m_ShouldTerminate = !m_Server.Tick(static_cast<float>(msec)); auto TickTime = std::chrono::steady_clock::now() - NowTime; - + if (TickTime < msPerTick) { // Stretch tick time until it's at least msPerTick @@ -185,12 +185,12 @@ void cServer::PlayerDestroying(const cPlayer * a_Player) -bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) +bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth) { - m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!"); - m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100); - m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false); - m_bAllowMultiLogin = a_SettingsIni.GetValueSetB("Server", "AllowMultiLogin", false); + m_Description = a_Settings.GetValueSet("Server", "Description", "MCServer - in C++!"); + m_MaxPlayers = a_Settings.GetValueSetI("Server", "MaxPlayers", 100); + m_bIsHardcore = a_Settings.GetValueSetB("Server", "HardcoreEnabled", false); + m_bAllowMultiLogin = a_Settings.GetValueSetB("Server", "AllowMultiLogin", false); m_PlayerCount = 0; m_PlayerCountDiff = 0; @@ -205,9 +205,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); - m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565"); - - m_RCONServer.Initialize(a_SettingsIni); + m_Ports = ReadUpgradeIniPorts(a_Settings, "Server", "Ports", "Port", "PortsIPv6", "25565"); + + m_RCONServer.Initialize(a_Settings); m_bIsConnected = true; @@ -226,16 +226,16 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) } // Check if both BungeeCord and online mode are on, if so, warn the admin: - m_ShouldAllowBungeeCord = a_SettingsIni.GetValueSetB("Authentication", "AllowBungeeCord", false); + m_ShouldAllowBungeeCord = a_Settings.GetValueSetB("Authentication", "AllowBungeeCord", false); 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."); } - - m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false); - m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true); - - m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); + + m_ShouldLoadOfflinePlayerData = a_Settings.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false); + m_ShouldLoadNamedPlayerData = a_Settings.GetValueSetB("PlayerData", "LoadNamedPlayerData", true); + + m_ClientViewDistance = a_Settings.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) { m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE; @@ -246,9 +246,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE; LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance); } - + PrepareKeys(); - + return true; } @@ -320,13 +320,13 @@ bool cServer::Tick(float a_Dt) cCSLock Lock(m_CSPlayerCount); m_PlayerCount += PlayerCountDiff; } - + // Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102): cPluginManager::Get()->Tick(a_Dt); - + // Let the Root process all the queued commands: cRoot::Get()->TickCommands(); - + // Tick all clients not yet assigned to a world: TickClients(a_Dt); @@ -351,7 +351,7 @@ void cServer::TickClients(float a_Dt) cClientHandlePtrs RemoveClients; { 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) { @@ -365,7 +365,7 @@ 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 for (auto itr = m_Clients.begin(); itr != m_Clients.end();) { @@ -380,7 +380,7 @@ void cServer::TickClients(float a_Dt) ++itr; } // for itr - m_Clients[] } - + // Delete the clients that have been destroyed RemoveClients.clear(); } @@ -439,7 +439,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac } // "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 MCS, so that they work no matter what if (split[0] == "help") { @@ -464,22 +464,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac { if (split.size() > 1) { - cPluginManager::PluginMap map = cPluginManager::Get()->GetAllPlugins(); - - for (auto plugin_entry : map) - { - if (plugin_entry.first == split[1]) - { - a_Output.Out("Error! Plugin is already loaded!"); - a_Output.Finished(); - return; - } - } + 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 { - a_Output.Out("Usage: load <pluginname>"); + a_Output.Out("Usage: load <PluginFolder>"); } a_Output.Finished(); return; @@ -488,12 +478,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac { if (split.size() > 1) { - cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1])); - a_Output.Out("Plugin unloaded"); + cPluginManager::Get()->UnloadPlugin(split[1]); + a_Output.Out("Plugin unload scheduled"); } else { - a_Output.Out("Usage: unload <pluginname>"); + a_Output.Out("Usage: unload <PluginFolder>"); } a_Output.Finished(); return; @@ -539,7 +529,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac DumpUsedMemory(&Output); return; } - + else if (split[0].compare("killmem") == 0) { for (;;) @@ -554,7 +544,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac a_Output.Finished(); return; } - + a_Output.Out("Unknown command, type 'help' for all commands."); a_Output.Finished(); } @@ -568,13 +558,13 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & UNUSED(a_Split); typedef std::pair<AString, AString> AStringPair; typedef std::vector<AStringPair> AStringPairs; - + class cCallback : public cPluginManager::cCommandEnumCallback { 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 { UNUSED(a_Plugin); @@ -589,7 +579,7 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & } return false; } - + AStringPairs m_Commands; size_t m_MaxLen; } Callback; @@ -635,7 +625,7 @@ void cServer::Shutdown(void) srv->Close(); } m_ServerHandles.clear(); - + // Notify the tick thread and wait for it to terminate: m_bRestarting = true; m_RestartEvent.Wait(); diff --git a/src/Server.h b/src/Server.h index 1f30295b7..4d0bc1c18 100644 --- a/src/Server.h +++ b/src/Server.h @@ -38,8 +38,8 @@ class cClientHandle; typedef SharedPtr<cClientHandle> cClientHandlePtr; typedef std::list<cClientHandlePtr> cClientHandlePtrs; typedef std::list<cClientHandle *> cClientHandles; -class cIniFile; class cCommandOutputCallback; +class cSettingsRepositoryInterface; namespace Json @@ -58,7 +58,7 @@ public: // tolua_end virtual ~cServer() {} - bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth); + bool InitServer(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth); // tolua_begin diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp index 5a0bea980..c0ae31fd3 100644 --- a/src/SetChunkData.cpp +++ b/src/SetChunkData.cpp @@ -33,8 +33,8 @@ cSetChunkData::cSetChunkData( const NIBBLETYPE * a_SkyLight, const cChunkDef::HeightMap * a_HeightMap, const cChunkDef::BiomeMap * a_Biomes, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, + cEntityList && a_Entities, + cBlockEntityList && a_BlockEntities, bool a_ShouldMarkDirty ) : m_ChunkX(a_ChunkX), @@ -84,8 +84,8 @@ cSetChunkData::cSetChunkData( } // Move entities and blockentities: - std::swap(m_Entities, a_Entities); - std::swap(m_BlockEntities, a_BlockEntities); + m_Entities = std::move(a_Entities); + m_BlockEntities = std::move(a_BlockEntities); } @@ -103,7 +103,7 @@ void cSetChunkData::CalculateHeightMap(void) int index = cChunkDef::MakeIndexNoCheck(x, y, z); if (m_BlockTypes[index] != E_BLOCK_AIR) { - m_HeightMap[x + z * cChunkDef::Width] = (HEIGHTTYPE)y; + m_HeightMap[x + z * cChunkDef::Width] = static_cast<HEIGHTTYPE>(y); break; } } // for y diff --git a/src/SetChunkData.h b/src/SetChunkData.h index 1eeb75ca9..bf5283569 100644 --- a/src/SetChunkData.h +++ b/src/SetChunkData.h @@ -24,8 +24,10 @@ public: /** Constructs a new instance based on data existing elsewhere, will copy all the memory. Prefer to use the other constructor as much as possible. - Will move the entity and blockentity lists into the internal storage, and empty the a_Entities and - a_BlockEntities lists. + Will move the entity and blockentity lists into the internal storage, and invalidate a_Entities and + a_BlockEntities. + When passing an lvalue, a_Entities and a_BlockEntities must be explicitly converted to an rvalue beforehand + with std::move(). a_BlockTypes and a_BlockMetas must always be valid. If either of the light arrays are nullptr, the chunk data will be marked as not having any light at all and will be scheduled for re-lighting once it is set into the chunkmap. @@ -41,8 +43,8 @@ public: const NIBBLETYPE * a_SkyLight, const cChunkDef::HeightMap * a_HeightMap, const cChunkDef::BiomeMap * a_Biomes, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, + cEntityList && a_Entities, + cBlockEntityList && a_BlockEntities, bool a_ShouldMarkDirty ); diff --git a/src/SettingsRepositoryInterface.h b/src/SettingsRepositoryInterface.h new file mode 100644 index 000000000..443b90ff5 --- /dev/null +++ b/src/SettingsRepositoryInterface.h @@ -0,0 +1,61 @@ + +#pragma once + +class cSettingsRepositoryInterface +{ +public: + + enum errors + { + noID = -1, + }; + + virtual ~cSettingsRepositoryInterface() = default; + + /** Returns true iff the specified key exists */ + virtual bool KeyExists(const AString keyname) const = 0; + + /** Returns true iff the specified value exists. */ + virtual bool HasValue(const AString & a_KeyName, const AString & a_ValueName) const = 0; + + /** Add a key name. Return value is not required to mean anything **/ + virtual int AddKeyName(const AString & keyname) = 0; + + /** Add a key comment, will always fail if the repository does not support comments **/ + virtual bool AddKeyComment(const AString & keyname, const AString & comment) = 0; + + /** Return a key comment, returns "" for repositories that do not return comments **/ + virtual AString GetKeyComment(const AString & keyname, const int commentID) const = 0; + + /** Delete a key comment, will always fail if the repository does not support comments **/ + virtual bool DeleteKeyComment(const AString & keyname, const int commentID) = 0; + + /** Adds a new value to the specified key. + If a value of the same name already exists, creates another one **/ + virtual void AddValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value) = 0; + + /** returns a vector containing a name, value pair for each value under the key **/ + virtual std::vector<std::pair<AString, AString>> GetValues(AString a_keyName) = 0; + + /** Get the value at the specified key and value, returns defValue on failure **/ + virtual AString GetValue (const AString & keyname, const AString & valuename, const AString & defValue = "") const = 0; + + /** Gets the value; if not found, write the default to the repository **/ + virtual AString GetValueSet (const AString & keyname, const AString & valuename, const AString & defValue = "") = 0; + virtual int GetValueSetI(const AString & keyname, const AString & valuename, const int defValue = 0) = 0; + virtual Int64 GetValueSetI(const AString & keyname, const AString & valuename, const Int64 defValue = 0) = 0; + virtual bool GetValueSetB(const AString & keyname, const AString & valuename, const bool defValue = false) = 0; + + /** Overwrites the value of the key, value pair + Specify the optional parameter as false if you do not want the value created if it doesn't exist. + Returns true if value set, false otherwise. **/ + virtual bool SetValue (const AString & a_KeyName, const AString & a_ValueName, const AString & a_Value, const bool a_CreateIfNotExists = true) = 0; + virtual bool SetValueI(const AString & a_KeyName, const AString & a_ValueName, const int a_Value, const bool a_CreateIfNotExists = true) = 0; + + /** Deletes the specified key, value pair **/ + virtual bool DeleteValue(const AString & keyname, const AString & valuename) = 0; + + + /** Writes the changes to the backing store, if the repository has one **/ + virtual bool Flush() = 0; +}; diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h index 8e1be5e6b..0eac61a03 100644 --- a/src/Simulator/FloodyFluidSimulator.h +++ b/src/Simulator/FloodyFluidSimulator.h @@ -47,7 +47,7 @@ protected: /** Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so. */ bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); - /** Checks if the specified block should harden (Water/Lava interaction) and if so, converts it to a suitable block. + /** Checks if the specified block should harden (Water / Lava interaction) and if so, converts it to a suitable block. Returns whether the block was changed or not. */ bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta); diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 40be9c582..7f320af0d 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -366,7 +366,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_R if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last) { if ( - IsMechanism(Type) && // Is it a mechanism? Not block/other torch etc. + IsMechanism(Type) && // Is it a mechanism? Not block / other torch etc. (!Vector3i(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on ) { @@ -1600,7 +1600,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB if ((OtherRepeaterDir & 0x03) == 0x3) { return true; - } // If so, I am latched/locked + } // If so, I am latched / locked } // Check if western(left) neighbor is a powered on repeater who is facing us @@ -1612,7 +1612,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB if ((OtherRepeaterDir & 0x03) == 0x1) { return true; - } // If so, I am latched/locked + } // If so, I am latched / locked } break; @@ -1634,7 +1634,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB if ((OtherRepeaterDir & 0x30) == 0x00) { return true; - } // If so, am latched/locked + } // If so, I am latched / locked } // Check if northern(up) neighbor is a powered on repeater who is facing us @@ -1646,7 +1646,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB if ((OtherRepeaterDir & 0x03) == 0x02) { return true; - } // If so, I am latched/locked + } // If so, I am latched / locked } break; diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 4adc6a0a0..12bd3ada1 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -150,6 +150,13 @@ AStringVector StringSplitWithQuotes(const AString & str, const AString & delim) while ((cutAt = str.find_first_of(delim, Prev)) != str.npos) { + if (cutAt == Prev) + { + // Empty string due to multiple whitespace / whitespace at the beginning of the input + // Just skip it + Prev = Prev + 1; + continue; + } AString current = str.substr(Prev, cutAt - Prev); if ((current.front() == '"') || (current.front() == '\'')) { diff --git a/src/StringUtils.h b/src/StringUtils.h index 785197763..b5fc58a2d 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -75,7 +75,7 @@ extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_expo /** Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()]. */ extern size_t RateCompareString(const AString & s1, const AString & s2); -/** Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith */ +/** Replaces each occurence of iNeedle in iHayStack with iReplaceWith */ extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); // tolua_export /** Converts a stream of BE shorts into UTF-8 string; returns a_UTF8. */ diff --git a/src/Tracer.cpp b/src/Tracer.cpp index e604f4a5b..5fdaff15d 100644 --- a/src/Tracer.cpp +++ b/src/Tracer.cpp @@ -12,17 +12,32 @@ +const float FLOAT_EPSILON = 0.0001f; // TODO: Stash this in some header where it can be reused + + +const std::array<const Vector3f, 6>& cTracer::m_NormalTable(void) +{ + static std::array<const Vector3f, 6>* table = + new std::array<const Vector3f, 6> + { + { + Vector3f(-1, 0, 0), // 1: -x + Vector3f( 0, 0, -1), // 2: -z + Vector3f( 1, 0, 0), // 3: +x + Vector3f( 0, 0, 1), // 4: +z + Vector3f( 0, 1, 0), // 5: +y + Vector3f( 0, -1, 0) // 6: -y + } + }; + + return *table; +}; + cTracer::cTracer(cWorld * a_World): m_World(a_World) { - m_NormalTable[0].Set(-1, 0, 0); - m_NormalTable[1].Set( 0, 0, -1); - m_NormalTable[2].Set( 1, 0, 0); - m_NormalTable[3].Set( 0, 0, 1); - m_NormalTable[4].Set( 0, 1, 0); - m_NormalTable[5].Set( 0, -1, 0); } @@ -37,17 +52,17 @@ cTracer::~cTracer() -float cTracer::SigNum(float a_Num) +int cTracer::SigNum(float a_Num) { if (a_Num < 0.f) { - return -1.f; + return -1; } if (a_Num > 0.f) { - return 1.f; + return 1; } - return 0.f; + return 0; } @@ -56,26 +71,28 @@ float cTracer::SigNum(float a_Num) void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) { + // Since this method should only be called by Trace, zero length vectors should already have been taken care of + ASSERT(a_Direction.HasNonZeroLength()); + // calculate the direction of the ray (linear algebra) dir = a_Direction; // decide which direction to start walking in - step.x = (int) SigNum(dir.x); - step.y = (int) SigNum(dir.y); - step.z = (int) SigNum(dir.z); + step.x = SigNum(dir.x); + step.y = SigNum(dir.y); + step.z = SigNum(dir.z); + // normalize the direction vector - if (dir.SqrLength() > 0.f) - { - dir.Normalize(); - } + dir.Normalize(); + // how far we must move in the ray direction before // we encounter a new voxel in x-direction // same but y-direction if (dir.x != 0.f) { - tDelta.x = 1 / fabs(dir.x); + tDelta.x = 1 / std::abs(dir.x); } else { @@ -83,7 +100,7 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) } if (dir.y != 0.f) { - tDelta.y = 1 / fabs(dir.y); + tDelta.y = 1 / std::abs(dir.y); } else { @@ -91,44 +108,45 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) } if (dir.z != 0.f) { - tDelta.z = 1 / fabs(dir.z); + tDelta.z = 1 / std::abs(dir.z); } else { tDelta.z = 0; } + // start voxel coordinates - pos.x = (int)floorf(a_Start.x); - pos.y = (int)floorf(a_Start.y); - pos.z = (int)floorf(a_Start.z); + pos.x = static_cast<int>(floorf(a_Start.x)); + pos.y = static_cast<int>(floorf(a_Start.y)); + pos.z = static_cast<int>(floorf(a_Start.z)); // calculate distance to first intersection in the voxel we start from if (dir.x < 0) { - tMax.x = ((float)pos.x - a_Start.x) / dir.x; + tMax.x = (static_cast<float>(pos.x) - a_Start.x) / dir.x; } else { - tMax.x = (((float)pos.x + 1) - a_Start.x) / dir.x; + tMax.x = (static_cast<float>(pos.x + 1) - a_Start.x) / dir.x; // TODO: Possible division by zero } if (dir.y < 0) { - tMax.y = ((float)pos.y - a_Start.y) / dir.y; + tMax.y = (static_cast<float>(pos.y) - a_Start.y) / dir.y; } else { - tMax.y = (((float)pos.y + 1) - a_Start.y) / dir.y; + tMax.y = (static_cast<float>(pos.y + 1) - a_Start.y) / dir.y; // TODO: Possible division by zero } if (dir.z < 0) { - tMax.z = ((float)pos.z - a_Start.z) / dir.z; + tMax.z = (static_cast<float>(pos.z) - a_Start.z) / dir.z; } else { - tMax.z = (((float)pos.z + 1) - a_Start.z) / dir.z; + tMax.z = (static_cast<float>(pos.z + 1) - a_Start.z) / dir.z; // TODO: Possible division by zero } } @@ -138,6 +156,11 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight) { + if (!a_Direction.HasNonZeroLength()) + { + return false; + } + if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height)) { LOGD("%s: Start Y is outside the world (%.2f), not tracing.", __FUNCTION__, a_Start.y); @@ -146,18 +169,18 @@ bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int SetValues(a_Start, a_Direction); - Vector3f End = a_Start + (dir * (float)a_Distance); + Vector3f End = a_Start + (dir * static_cast<float>(a_Distance)); if (End.y < 0) { - float dist = -a_Start.y / dir.y; + float dist = -a_Start.y / dir.y; // No division by 0 possible End = a_Start + (dir * dist); } // end voxel coordinates - end1.x = (int)floorf(End.x); - end1.y = (int)floorf(End.y); - end1.z = (int)floorf(End.z); + end1.x = static_cast<int>(floorf(End.x)); + end1.y = static_cast<int>(floorf(End.y)); + end1.z = static_cast<int>(floorf(End.z)); // check if first is occupied if (pos.Equals(end1)) @@ -241,7 +264,7 @@ bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int int Normal = GetHitNormal(a_Start, End, pos); if (Normal > 0) { - HitNormal = m_NormalTable[Normal-1]; + HitNormal = m_NormalTable()[Normal - 1]; } return true; } @@ -295,8 +318,7 @@ int cTracer::intersect3D_SegmentPlane(const Vector3f & a_Origin, const Vector3f float D = a_PlaneNormal.Dot(u); // dot(Pn.n, u); float N = -(a_PlaneNormal.Dot(w)); // -dot(a_Plane.n, w); - const float EPSILON = 0.0001f; - if (fabs(D) < EPSILON) + if (std::abs(D) < FLOAT_EPSILON) { // segment is parallel to plane if (N == 0.0) diff --git a/src/Tracer.h b/src/Tracer.h index ec87d449e..55c21546b 100644 --- a/src/Tracer.h +++ b/src/Tracer.h @@ -3,6 +3,8 @@ #include "Vector3.h" +#include <array> + @@ -61,10 +63,12 @@ private: /// Return 1 through 6 for the following block faces, repectively: -x, -z, x, z, y, -y int GetHitNormal( const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos); - float SigNum( float a_Num); - cWorld* m_World; + /// Signum function + int SigNum(float a_Num); + + cWorld * m_World; - Vector3f m_NormalTable[6]; + static const std::array<const Vector3f, 6> & m_NormalTable(void); Vector3f dir; Vector3f tDelta; diff --git a/src/UI/DropSpenserWindow.cpp b/src/UI/DropSpenserWindow.cpp index aeb7c64b7..121836e40 100644 --- a/src/UI/DropSpenserWindow.cpp +++ b/src/UI/DropSpenserWindow.cpp @@ -1,7 +1,7 @@ // DropSpenserWindow.cpp -// Representing the UI window for the dropper/dispenser block +// Representing the UI window for the dropper / dispenser block #include "Globals.h" #include "DropSpenserWindow.h" diff --git a/src/UI/DropSpenserWindow.h b/src/UI/DropSpenserWindow.h index edff936e5..cfc040493 100644 --- a/src/UI/DropSpenserWindow.h +++ b/src/UI/DropSpenserWindow.h @@ -1,7 +1,7 @@ // DropSpenserWindow.h -// Representing the UI window for the dropper/dispenser block +// Representing the UI window for the dropper / dispenser block diff --git a/src/UI/FurnaceWindow.cpp b/src/UI/FurnaceWindow.cpp index 132439ff3..a4e852fd8 100644 --- a/src/UI/FurnaceWindow.cpp +++ b/src/UI/FurnaceWindow.cpp @@ -41,7 +41,7 @@ void cFurnaceWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & } else { - // Furnace Input/Fuel Slot + // Furnace Input / Fuel Slot AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); diff --git a/src/UI/MinecartWithChestWindow.h b/src/UI/MinecartWithChestWindow.h index a2b5283a6..87e8f6137 100644 --- a/src/UI/MinecartWithChestWindow.h +++ b/src/UI/MinecartWithChestWindow.h @@ -33,7 +33,7 @@ public: a_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestopen", a_ChestCart->GetPosX(), a_ChestCart->GetPosY(), a_ChestCart->GetPosZ(), 1, 1); } - virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea* a_ClickedArea, bool a_ShouldApply) override + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override { cSlotAreas AreasInOrder; diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 0e7ba2a50..664c6502c 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -239,7 +239,7 @@ public: // cSlotAreaTemporary overrides: virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void DblClicked (cPlayer & a_Player, int a_SlotNum); + virtual void DblClicked (cPlayer & a_Player, int a_SlotNum) override; virtual void OnPlayerRemoved(cPlayer & a_Player) override; virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; @@ -306,7 +306,7 @@ public: void UpdateResult(cPlayer & a_Player); protected: - /** The maximum cost of repairing/renaming in the anvil. */ + /** The maximum cost of repairing / renaming in the anvil. */ int m_MaximumCost; /** The stack size of the second item where was used for repair */ diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index bb2e2a807..d1c08acec 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -21,19 +21,23 @@ -char cWindow::m_WindowIDCounter = 1; +Byte cWindow::m_WindowIDCounter = 0; cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) : - m_WindowID((++m_WindowIDCounter) % 127), + m_WindowID(static_cast<char>((++m_WindowIDCounter) % 127)), m_WindowType(a_WindowType), m_WindowTitle(a_WindowTitle), m_IsDestroyed(false), m_Owner(nullptr) { + // The window ID is signed in protocol 1.7, unsigned in protocol 1.8. Keep out of trouble by using only 7 bits: + // Ref.: http://forum.mc-server.org/showthread.php?tid=1876 + ASSERT((m_WindowID >= 0) && (m_WindowID < 127)); + if (a_WindowType == wtInventory) { m_WindowID = 0; diff --git a/src/UI/Window.h b/src/UI/Window.h index 9821aade1..156028465 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -185,7 +185,7 @@ protected: cWindowOwner * m_Owner; - static char m_WindowIDCounter; + static Byte m_WindowIDCounter; /// Sets the internal flag as "destroyed"; notifies the owner that the window is destroying virtual void Destroy(void); diff --git a/src/Vector3.h b/src/Vector3.h index 1f3f6b955..346bc1bbb 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -78,6 +78,20 @@ public: ); } + inline bool HasNonZeroLength(void) const + { + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wfloat-equal" + #endif + + return ((x != 0) || (y != 0) || (z != 0)); + + #ifdef __clang__ + #pragma clang diagnostic pop + #endif + } + inline double Length(void) const { return sqrt(static_cast<double>(x * x + y * y + z * z)); @@ -119,13 +133,19 @@ public: inline bool Equals(const Vector3<T> & a_Rhs) const { - // Perform a bitwise comparison of the contents - we want to know whether this object is exactly equal + // Perform a strict comparison of the contents - we want to know whether this object is exactly equal // To perform EPS-based comparison, use the EqualsEps() function - return ( - (memcmp(&x, &a_Rhs.x, sizeof(x)) == 0) && - (memcmp(&y, &a_Rhs.y, sizeof(y)) == 0) && - (memcmp(&z, &a_Rhs.z, sizeof(z)) == 0) - ); + + #ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wfloat-equal" + #endif + + return !((x != a_Rhs.x) || (y != a_Rhs.y) || (z != a_Rhs.z)); + + #ifdef __clang__ + #pragma clang diagnostic pop + #endif } inline bool EqualsEps(const Vector3<T> & a_Rhs, T a_Eps) const @@ -244,6 +264,15 @@ public: ); } + inline Vector3<T> operator / (const Vector3<T> & a_Rhs) + { + return Vector3<T>( + x / a_Rhs.x, + y / a_Rhs.y, + z / a_Rhs.z + ); + } + inline Vector3<T> operator * (T a_v) const { return Vector3<T>( @@ -345,6 +374,7 @@ protected: + template <> inline Vector3<int> Vector3<int>::Floor(void) const { return *this; diff --git a/src/VoronoiMap.h b/src/VoronoiMap.h index 56022849e..a04a613d5 100644 --- a/src/VoronoiMap.h +++ b/src/VoronoiMap.h @@ -59,7 +59,7 @@ protected: cNoise m_Noise2; cNoise m_Noise3; - /** Size of the Voronoi cells (avg X/Y distance between the seeds). Expected to be at least 2. */ + /** Size of the Voronoi cells (avg X / Y distance between the seeds). Expected to be at least 2. */ int m_CellSize; /** The amount that the cell seeds may be offset from the grid. diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index 13cf3cc41..1cb4463e1 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -234,7 +234,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~')); // Retrieve the request data: - cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData()); + cWebadminRequestData * Data = reinterpret_cast<cWebadminRequestData *>(a_Request.GetUserData()); if (Data == nullptr) { a_Connection.SendStatusAndReason(500, "Bad UserData"); @@ -244,6 +244,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque // Wrap it all up for the Lua call: AString Template; HTTPTemplateRequest TemplateRequest; + TemplateRequest.Request.URL = a_Request.GetURL(); TemplateRequest.Request.Username = a_Request.GetAuthUsername(); TemplateRequest.Request.Method = a_Request.GetMethod(); TemplateRequest.Request.Path = BareURL.substr(1); @@ -464,10 +465,10 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { if ((*itr)->GetWebTitle() == Split[1]) { - Page.Content = (*itr)->HandleWebRequest(&a_Request); + Page.Content = (*itr)->HandleWebRequest(a_Request); cWebPlugin * WebPlugin = *itr; FoundPlugin = WebPlugin->GetWebTitle(); - AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first; + AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first; Page.PluginName = FoundPlugin; Page.TabName = TabName; break; @@ -489,20 +490,32 @@ AString cWebAdmin::GetDefaultPage(void) Content += "<h4>Server Name:</h4>"; Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>"; + // Display a list of all plugins: Content += "<h4>Plugins:</h4><ul>"; - cPluginManager * PM = cPluginManager::Get(); - const cPluginManager::PluginMap & List = PM->GetAllPlugins(); - for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr) + struct cPluginCallback: + public cPluginManager::cPluginCallback { - if (itr->second == nullptr) + AString & m_Content; + + cPluginCallback(AString & a_Content): + m_Content(a_Content) { - continue; } - AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion()); - } + + virtual bool Item(cPlugin * a_Plugin) override + { + if (a_Plugin->IsLoaded()) + { + AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion()); + } + return false; + } + } Callback(Content); + cPluginManager::Get()->ForEachPlugin(Callback); Content += "</ul>"; - Content += "<h4>Players:</h4><ul>"; + // Display a list of all players: + Content += "<h4>Players:</h4><ul>"; cPlayerAccum PlayerAccum; cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players if (World != nullptr) @@ -518,7 +531,7 @@ AString cWebAdmin::GetDefaultPage(void) -AString cWebAdmin::GetBaseURL( const AString& a_URL) +AString cWebAdmin::GetBaseURL(const AString & a_URL) { return GetBaseURL(StringSplit(a_URL, "/")); } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 1e1a9bfa9..4dbcc57a6 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -50,9 +50,18 @@ struct HTTPRequest typedef std::map< std::string, std::string > StringStringMap; typedef std::map< std::string, HTTPFormData > FormDataMap; + /** The entire URL presented to the HTTP server. */ + AString URL; + + /** HTTP method used for the request ("GET", "POST" etc.) */ AString Method; + + /** The Path part of the request's URL (excluding GET params). */ AString Path; + + /** Name of the logged-in user. Empty if not logged in. */ AString Username; + // tolua_end /** Parameters given in the URL, after the questionmark */ diff --git a/src/World.cpp b/src/World.cpp index 4480013c3..3c39de317 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -57,7 +57,7 @@ #include <stdlib.h> #endif - +#include "Broadcaster.h" @@ -150,7 +150,7 @@ protected: int m_LastReportChunkCount; // cChunkCoordCallback override: - virtual void Call(int a_ChunkX, int a_ChunkZ) + virtual void Call(int a_ChunkX, int a_ChunkZ) override { // Check if this was the last chunk: m_NumPrepared += 1; @@ -176,7 +176,7 @@ protected: { float PercentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx; float ChunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast<std::chrono::milliseconds>(Now - m_LastReportTime).count(); - LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks/s)", + LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks / sec)", m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed ); m_LastReportTime = Now; @@ -362,6 +362,7 @@ cWorld::~cWorld() void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) { BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); + BroadcastSoundEffect("ambient.weather.thunder", a_BlockX, a_BlockY, a_BlockZ, 50, 1); } @@ -621,18 +622,18 @@ void cWorld::Start(void) InitialiseAndLoadMobSpawningValues(IniFile); SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); - m_ChunkMap = make_unique<cChunkMap>(this); + m_ChunkMap = cpp14::make_unique<cChunkMap>(this); // preallocate some memory for ticking blocks so we don't need to allocate that often m_BlockTickQueue.reserve(1000); m_BlockTickQueueCopy.reserve(1000); // Simulators: - m_SimulatorManager = make_unique<cSimulatorManager>(*this); + m_SimulatorManager = cpp14::make_unique<cSimulatorManager>(*this); m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); - m_SandSimulator = make_unique<cSandSimulator>(*this, IniFile); - m_FireSimulator = make_unique<cFireSimulator>(*this, IniFile); + m_SandSimulator = cpp14::make_unique<cSandSimulator>(*this, IniFile); + m_FireSimulator = cpp14::make_unique<cFireSimulator>(*this, IniFile); m_RedstoneSimulator = InitializeRedstoneSimulator(IniFile); // Water, Lava and Redstone simulators get registered in their initialize function. @@ -767,7 +768,7 @@ eWeather cWorld::ChooseNewWeather() case eWeather_Rain: { - // 1/8 chance of turning into a thunderstorm + // 1 / 8 chance of turning into a thunderstorm return ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny; } } @@ -800,7 +801,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile) a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown"); a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D"); a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal"); - a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals"); + a_IniFile.GetValueSet("Generator", "Finishers", "RoughRavines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, TallGrass, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals"); break; } case dimNether: @@ -812,7 +813,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile) a_IniFile.GetValueSet("Generator", "HeightGen", "Flat"); a_IniFile.GetValueSet("Generator", "FlatHeight", "128"); a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether"); - a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, PreSimulator"); + a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, GlowStone, PreSimulator"); a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30"); break; } @@ -833,7 +834,7 @@ void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile) AString DefaultMonsters; switch (m_Dimension) { - case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, guardian, horse, mooshroom, ocelot, pig, rabbit, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; case dimEnd: DefaultMonsters = "enderman"; break; case dimNotSet: ASSERT(!"Dimension not set"); break; @@ -1019,7 +1020,7 @@ void cWorld::TickWeather(float a_Dt) // 0.5% chance per tick of thunderbolt if (m_TickRand.randInt() % 199 == 0) { - CastThunderbolt(0, 0, 0); // TODO: find random possitions near players to cast thunderbolts. + CastThunderbolt(0, 0, 0); // TODO: find random positions near players to cast thunderbolts. } } } @@ -1119,7 +1120,7 @@ void cWorld::TickScheduledTasks(void) auto WorldAge = m_WorldAge; // Move all the due tasks from m_ScheduledTasks into Tasks: - for (auto itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end();) // Cannot use range-basd for, we're modifying the container + for (auto itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end();) // Cannot use range-based for, we're modifying the container { if ((*itr)->m_TargetTick < std::chrono::duration_cast<cTickTimeLong>(WorldAge).count()) { @@ -1351,7 +1352,7 @@ bool cWorld::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBloc -bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback& a_Callback) +bool cWorld::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconCallback & a_Callback) { return m_ChunkMap->DoWithBeaconAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); } @@ -1459,6 +1460,15 @@ bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback +bool cWorld::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback) +{ + return m_ChunkMap->DoWithChunkAt(a_BlockPos, a_Callback); +} + + + + + void cWorld::GrowTree(int a_X, int a_Y, int a_Z) { if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) @@ -2241,14 +2251,6 @@ void cWorld::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation -void cWorld::BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount, a_Exclude); -} - - - - void cWorld::BroadcastPlayerListAddPlayer(const cPlayer & a_Player, const cClientHandle * a_Exclude) { @@ -2679,7 +2681,7 @@ void cWorld::UnloadUnusedChunks(void) void cWorld::QueueUnloadUnusedChunks(void) { - QueueTask(make_unique<cWorld::cTaskUnloadUnusedChunks>()); + QueueTask(cpp14::make_unique<cWorld::cTaskUnloadUnusedChunks>()); } @@ -3712,7 +3714,7 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc a_ChunkDesc.GetBlockTypes(), BlockMetas, nullptr, nullptr, // We don't have lighting, chunk will be lighted when needed &a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(), - a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(), + std::move(a_ChunkDesc.GetEntities()), std::move(a_ChunkDesc.GetBlockEntities()), true )); SetChunkData->RemoveInvalidBlockEntities(); @@ -3770,5 +3772,10 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch +cBroadcaster cWorld::GetBroadcaster() +{ + return cBroadcaster(this); +} + diff --git a/src/World.h b/src/World.h index 1de241f60..2ecdd519c 100644 --- a/src/World.h +++ b/src/World.h @@ -55,6 +55,7 @@ class cMobHeadEntity; class cCompositeChat; class cCuboid; class cSetChunkData; +class cBroadcaster; typedef std::list< cPlayer * > cPlayerList; @@ -151,10 +152,10 @@ public: int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; } - /** Is the daylight cyclus enabled? */ + /** Is the daylight cycle enabled? */ virtual bool IsDaylightCycleEnabled(void) const { return m_IsDaylightCycleEnabled; } - /** Sets the daylight cyclus to true/false. */ + /** Sets the daylight cycle to true / false. */ virtual void SetDaylightCycleEnabled(bool a_IsDaylightCycleEnabled) { m_IsDaylightCycleEnabled = a_IsDaylightCycleEnabled; @@ -202,10 +203,10 @@ public: bool VillagersShouldHarvestCrops(void) const { return m_VillagersShouldHarvestCrops; } - virtual eDimension GetDimension(void) const { return m_Dimension; } + virtual eDimension GetDimension(void) const override { return m_Dimension; } /** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */ - virtual int GetHeight(int a_BlockX, int a_BlockZ); + virtual int GetHeight(int a_BlockX, int a_BlockZ) override; // tolua_end @@ -243,7 +244,6 @@ public: void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = nullptr); void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr); virtual void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = nullptr) override; // tolua_export - void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr); // tolua_export void BroadcastPlayerListAddPlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr); void BroadcastPlayerListRemovePlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr); void BroadcastPlayerListUpdateGameMode (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr); @@ -253,7 +253,7 @@ public: void BroadcastScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); void BroadcastDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display); - void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr); // tolua_export + void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr) override; // tolua_export void BroadcastSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = nullptr); // tolua_export void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr); void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr); @@ -610,6 +610,9 @@ public: /** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */ bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); + /** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/ + bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback); + void GrowTreeImage(const sSetBlockVector & a_Blocks); // tolua_begin @@ -785,7 +788,7 @@ public: bool IsWeatherWet(void) const { return !IsWeatherSunny(); } /** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */ - virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) + virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) override { return (IsWeatherWet() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ))); } @@ -828,6 +831,8 @@ public: This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long as at least one requests is active the chunk will be ticked). */ void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked = true); // tolua_export + + cBroadcaster GetBroadcaster(); private: @@ -1062,7 +1067,7 @@ private: /** Handles the weather in each tick */ void TickWeather(float a_Dt); - /** Handles the mob spawning/moving/destroying each tick */ + /** Handles the mob spawning / moving / destroying each tick */ void TickMobs(std::chrono::milliseconds a_Dt); /** Executes all tasks queued onto the tick thread */ diff --git a/src/WorldStorage/EnchantmentSerializer.cpp b/src/WorldStorage/EnchantmentSerializer.cpp index 56072207f..a6e562956 100644 --- a/src/WorldStorage/EnchantmentSerializer.cpp +++ b/src/WorldStorage/EnchantmentSerializer.cpp @@ -5,7 +5,7 @@ #include "Enchantments.h" #include "FastNBT.h" -void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName) +void EnchantmentSerializer::WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName) { // Write the enchantments into the specified NBT writer // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments") @@ -25,7 +25,7 @@ void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantmen -void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx) +void EnchantmentSerializer::ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx) { // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments) diff --git a/src/WorldStorage/EnchantmentSerializer.h b/src/WorldStorage/EnchantmentSerializer.h index 9ed362900..7d6546aad 100644 --- a/src/WorldStorage/EnchantmentSerializer.h +++ b/src/WorldStorage/EnchantmentSerializer.h @@ -9,9 +9,9 @@ namespace EnchantmentSerializer { /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") - void WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName); + void WriteToNBTCompound(const cEnchantments & a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName); /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) - void ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx); + void ParseFromNBT(cEnchantments & a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx); }; diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h index c6225eacf..35e47c8e4 100644 --- a/src/WorldStorage/FastNBT.h +++ b/src/WorldStorage/FastNBT.h @@ -164,7 +164,7 @@ public: /** Returns the direct child tag of the specified name, or -1 if no such tag. */ int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const; - /** Returns the child tag of the specified path (Name1/Name2/Name3...), or -1 if no such tag. */ + /** Returns the child tag of the specified path (Name1 / Name2 / Name3...), or -1 if no such tag. */ int FindTagByPath(int a_Tag, const AString & a_Path) const; eTagType GetType(int a_Tag) const { return m_Tags[(size_t)a_Tag].m_Type; } diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp index f2d35b318..da7b9d929 100644 --- a/src/WorldStorage/MapSerializer.cpp +++ b/src/WorldStorage/MapSerializer.cpp @@ -15,13 +15,13 @@ -cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map) - : m_Map(a_Map) +cMapSerializer::cMapSerializer(const AString & a_WorldName, cMap * a_Map): + m_Map(a_Map) { AString DataPath; - Printf(DataPath, "%s/data", a_WorldName.c_str()); + Printf(DataPath, "%s%cdata", a_WorldName.c_str(), cFile::PathSeparator); - Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID()); + Printf(m_Path, "%s%cmap_%i.dat", DataPath.c_str(), cFile::PathSeparator, a_Map->GetID()); cFile::CreateFolder(FILE_IO_PREFIX + DataPath); } @@ -203,9 +203,9 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0) { AString DataPath; - Printf(DataPath, "%s/data", a_WorldName.c_str()); + Printf(DataPath, "%s%cdata", a_WorldName.c_str(), cFile::PathSeparator); - Printf(m_Path, "%s/idcounts.dat", DataPath.c_str()); + Printf(m_Path, "%s%cidcounts.dat", DataPath.c_str(), cFile::PathSeparator); cFile::CreateFolder(FILE_IO_PREFIX + DataPath); } diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h index e13a75c8f..007f1347b 100644 --- a/src/WorldStorage/MapSerializer.h +++ b/src/WorldStorage/MapSerializer.h @@ -26,7 +26,7 @@ class cMapSerializer { public: - cMapSerializer(const AString& a_WorldName, cMap * a_Map); + cMapSerializer(const AString & a_WorldName, cMap * a_Map); /** Try to load the map */ bool Load(void); diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp index 404604382..01e56dbe1 100644 --- a/src/WorldStorage/ScoreboardSerializer.cpp +++ b/src/WorldStorage/ScoreboardSerializer.cpp @@ -14,13 +14,13 @@ -cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard) - : m_ScoreBoard(a_ScoreBoard) +cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard * a_ScoreBoard): + m_ScoreBoard(a_ScoreBoard) { AString DataPath; - Printf(DataPath, "%s/data", a_WorldName.c_str()); + Printf(DataPath, "%s%cdata", a_WorldName.c_str(), cFile::PathSeparator); - m_Path = DataPath + "/scoreboard.dat"; + m_Path = DataPath + cFile::PathSeparator + "scoreboard.dat"; cFile::CreateFolder(FILE_IO_PREFIX + DataPath); } diff --git a/src/WorldStorage/ScoreboardSerializer.h b/src/WorldStorage/ScoreboardSerializer.h index 048fa3ab4..f9065b35f 100644 --- a/src/WorldStorage/ScoreboardSerializer.h +++ b/src/WorldStorage/ScoreboardSerializer.h @@ -25,7 +25,7 @@ class cScoreboardSerializer { public: - cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard); + cScoreboardSerializer(const AString & a_WorldName, cScoreboard * a_ScoreBoard); /// Try to load the scoreboard bool Load(void); @@ -40,7 +40,7 @@ private: bool LoadScoreboardFromNBT(const cParsedNBT & a_NBT); - cScoreboard* m_ScoreBoard; + cScoreboard * m_ScoreBoard; AString m_Path; diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp index 74113941c..99a702c39 100644 --- a/src/WorldStorage/StatSerializer.cpp +++ b/src/WorldStorage/StatSerializer.cpp @@ -18,7 +18,7 @@ cStatSerializer::cStatSerializer(const AString & a_WorldName, const AString & a_ // inside the folder of the default world. AString StatsPath; - Printf(StatsPath, "%s/stats", a_WorldName.c_str()); + Printf(StatsPath, "%s%cstats", a_WorldName.c_str(), cFile::PathSeparator); m_Path = StatsPath + "/" + a_PlayerName + ".json"; diff --git a/src/WorldStorage/StatSerializer.h b/src/WorldStorage/StatSerializer.h index 72f8d74f1..6b7efddbb 100644 --- a/src/WorldStorage/StatSerializer.h +++ b/src/WorldStorage/StatSerializer.h @@ -43,7 +43,7 @@ protected: private: - cStatManager* m_Manager; + cStatManager * m_Manager; AString m_Path; diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 7244bcb73..392b9bf83 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -95,7 +95,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) : { // Create a level.dat file for mapping tools, if it doesn't already exist: AString fnam; - Printf(fnam, "%s/level.dat", a_World->GetName().c_str()); + Printf(fnam, "%s%clevel.dat", a_World->GetName().c_str(), cFile::PathSeparator); if (!cFile::Exists(fnam)) { cFastNBTWriter Writer; @@ -251,7 +251,7 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) // Load it anew: AString FileName; - Printf(FileName, "%s/region", m_World->GetName().c_str()); + Printf(FileName, "%s%cregion", m_World->GetName().c_str(), cFile::PathSeparator); cFile::CreateFolder(FILE_IO_PREFIX + FileName); AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); @@ -323,7 +323,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT) { - // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data) + // The data arrays, in MCA-native y / z / x ordering (will be reordered for the final chunk data) cChunkDef::BlockTypes BlockTypes; cChunkDef::BlockNibbles MetaData; cChunkDef::BlockNibbles BlockLight; @@ -431,7 +431,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT IsLightValid ? BlockLight : nullptr, IsLightValid ? SkyLight : nullptr, nullptr, Biomes, - Entities, BlockEntities, + std::move(Entities), std::move(BlockEntities), false )); m_World->QueueSetChunkData(SetChunkData); @@ -879,7 +879,7 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI Beacon->SetSecondaryEffect((cEntityEffect::eType)a_NBT.GetInt(CurrentLine)); } - // We are better than mojang, we load/save the beacon inventory! + // We are better than mojang, we load / save the beacon inventory! int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); if ((Items >= 0) && (a_NBT.GetType(Items) == TAG_List)) { @@ -3148,7 +3148,7 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri } // Store the header: - ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number + ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size up to nearest 4KB sector, make it a sector number if (ChunkSize > 255) { LOGWARNING("Cannot save chunk [%d, %d], the data is too large (%u KiB, maximum is 1024 KiB). Remove some entities and retry.", diff --git a/src/XMLParser.h b/src/XMLParser.h index ea341bce6..90e499785 100644 --- a/src/XMLParser.h +++ b/src/XMLParser.h @@ -168,9 +168,9 @@ public: protected: - // Parser callback enable/disable methods: + // Parser callback enable / disable methods: - // @cmember Enable/Disable the start element handler + // @cmember Enable / Disable the start element handler void EnableStartElementHandler (bool fEnable = true) { @@ -178,7 +178,7 @@ protected: XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : nullptr); } - // @cmember Enable/Disable the end element handler + // @cmember Enable / Disable the end element handler void EnableEndElementHandler (bool fEnable = true) { @@ -186,7 +186,7 @@ protected: XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : nullptr); } - // @cmember Enable/Disable the element handlers + // @cmember Enable / Disable the element handlers void EnableElementHandler (bool fEnable = true) { @@ -195,7 +195,7 @@ protected: EnableEndElementHandler (fEnable); } - // @cmember Enable/Disable the character data handler + // @cmember Enable / Disable the character data handler void EnableCharacterDataHandler (bool fEnable = true) { @@ -203,7 +203,7 @@ protected: XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : nullptr); } - // @cmember Enable/Disable the processing instruction handler + // @cmember Enable / Disable the processing instruction handler void EnableProcessingInstructionHandler (bool fEnable = true) { @@ -211,7 +211,7 @@ protected: XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : nullptr); } - // @cmember Enable/Disable the comment handler + // @cmember Enable / Disable the comment handler void EnableCommentHandler (bool fEnable = true) { @@ -219,7 +219,7 @@ protected: XML_SetCommentHandler (m_p, fEnable ? CommentHandler : nullptr); } - // @cmember Enable/Disable the start CDATA section handler + // @cmember Enable / Disable the start CDATA section handler void EnableStartCdataSectionHandler (bool fEnable = true) { @@ -227,7 +227,7 @@ protected: XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : nullptr); } - // @cmember Enable/Disable the end CDATA section handler + // @cmember Enable / Disable the end CDATA section handler void EnableEndCdataSectionHandler (bool fEnable = true) { @@ -235,7 +235,7 @@ protected: XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : nullptr); } - // @cmember Enable/Disable the CDATA section handlers + // @cmember Enable / Disable the CDATA section handlers void EnableCdataSectionHandler (bool fEnable = true) { @@ -244,7 +244,7 @@ protected: EnableEndCdataSectionHandler (fEnable); } - // @cmember Enable/Disable default handler + // @cmember Enable / Disable default handler void EnableDefaultHandler (bool fEnable = true, bool fExpand = true) { @@ -259,7 +259,7 @@ protected: } } - // @cmember Enable/Disable external entity ref handler + // @cmember Enable / Disable external entity ref handler void EnableExternalEntityRefHandler (bool fEnable = true) { @@ -267,7 +267,7 @@ protected: XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : nullptr); } - // @cmember Enable/Disable unknown encoding handler + // @cmember Enable / Disable unknown encoding handler void EnableUnknownEncodingHandler (bool fEnable = true) { @@ -275,7 +275,7 @@ protected: XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : nullptr); } - // @cmember Enable/Disable start namespace handler + // @cmember Enable / Disable start namespace handler void EnableStartNamespaceDeclHandler (bool fEnable = true) { @@ -283,7 +283,7 @@ protected: XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : nullptr); } - // @cmember Enable/Disable end namespace handler + // @cmember Enable / Disable end namespace handler void EnableEndNamespaceDeclHandler (bool fEnable = true) { @@ -291,7 +291,7 @@ protected: XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : nullptr); } - // @cmember Enable/Disable namespace handlers + // @cmember Enable / Disable namespace handlers void EnableNamespaceDeclHandler (bool fEnable = true) { @@ -299,7 +299,7 @@ protected: EnableEndNamespaceDeclHandler (fEnable); } - // @cmember Enable/Disable the XML declaration handler + // @cmember Enable / Disable the XML declaration handler void EnableXmlDeclHandler (bool fEnable = true) { @@ -307,7 +307,7 @@ protected: XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : nullptr); } - // @cmember Enable/Disable the start DOCTYPE declaration handler + // @cmember Enable / Disable the start DOCTYPE declaration handler void EnableStartDoctypeDeclHandler (bool fEnable = true) { @@ -315,7 +315,7 @@ protected: XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : nullptr); } - // @cmember Enable/Disable the end DOCTYPE declaration handler + // @cmember Enable / Disable the end DOCTYPE declaration handler void EnableEndDoctypeDeclHandler (bool fEnable = true) { @@ -324,7 +324,7 @@ protected: fEnable ? EndDoctypeDeclHandler : nullptr); } - // @cmember Enable/Disable the DOCTYPE declaration handler + // @cmember Enable / Disable the DOCTYPE declaration handler void EnableDoctypeDeclHandler (bool fEnable = true) { diff --git a/src/main.cpp b/src/main.cpp index da8eb75d4..5cd057278 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Root.h" +#include "tclap/CmdLine.h" #include <exception> #include <csignal> @@ -12,12 +13,11 @@ #endif // _MSC_VER #include "OSSupport/NetworkSingleton.h" +#include "BuildInfo.h" +#include "MemorySettingsRepository.h" -/** Make the Root instance global, so it can be terminated from the worker threads */ -cRoot Root; - /** If something has told the server to stop; checked periodically in cRoot */ bool cRoot::m_TerminateEventRaised = false; @@ -35,14 +35,20 @@ bool g_ShouldLogCommOut; bool cRoot::m_RunAsService = false; + + + #if defined(_WIN32) -SERVICE_STATUS_HANDLE g_StatusHandle = NULL; -HANDLE g_ServiceThread = INVALID_HANDLE_VALUE; -#define SERVICE_NAME "MCServerService" + SERVICE_STATUS_HANDLE g_StatusHandle = NULL; + HANDLE g_ServiceThread = INVALID_HANDLE_VALUE; + #define SERVICE_NAME "MCServerService" #endif -/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window + + + +/** If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window */ // _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013 // and we haven't had a memory leak for over a year anyway. // #define ENABLE_LEAK_FINDER @@ -74,6 +80,10 @@ void NonCtrlHandler(int a_Signal) std::signal(SIGSEGV, SIG_DFL); LOGERROR(" D: | MCServer has encountered an error and needs to close"); LOGERROR("Details | SIGSEGV: Segmentation fault"); + #ifdef BUILD_ID + LOGERROR("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID); + LOGERROR("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME); + #endif PrintStackTrace(); abort(); } @@ -85,6 +95,10 @@ void NonCtrlHandler(int a_Signal) std::signal(a_Signal, SIG_DFL); LOGERROR(" D: | MCServer has encountered an error and needs to close"); LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault"); + #ifdef BUILD_ID + LOGERROR("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID); + LOGERROR("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME); + #endif PrintStackTrace(); abort(); } @@ -169,6 +183,7 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except + #ifdef _WIN32 // Handle CTRL events in windows, including console window close BOOL CtrlHandler(DWORD fdwCtrlType) @@ -188,10 +203,11 @@ BOOL CtrlHandler(DWORD fdwCtrlType) + //////////////////////////////////////////////////////////////////////////////// // universalMain - Main startup logic for both standard running and as a service -void universalMain() +void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo) { #ifdef _WIN32 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE)) @@ -210,7 +226,8 @@ void universalMain() try #endif { - Root.Start(); + cRoot Root; + Root.Start(std::move(overridesRepo)); } #if !defined(ANDROID_NDK) catch (std::exception & e) @@ -241,7 +258,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam) UNREFERENCED_PARAMETER(lpParam); // Do the normal startup - universalMain(); + universalMain(cpp14::make_unique<cMemorySettingsRepository>()); return ERROR_SUCCESS; } @@ -282,7 +299,7 @@ void WINAPI serviceCtrlHandler(DWORD CtrlCode) { case SERVICE_CONTROL_STOP: { - Root.SetStopping(true); + cRoot::m_ShouldStop = true; serviceSetState(0, SERVICE_STOP_PENDING, 0); break; } @@ -309,10 +326,10 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[]) char applicationFilename[MAX_PATH]; char applicationDirectory[MAX_PATH]; - GetModuleFileName(NULL, applicationFilename, MAX_PATH); // This binaries fill path. + GetModuleFileName(NULL, applicationFilename, sizeof(applicationFilename)); // This binary's file path. - // GetModuleFileName() returns the path and filename. Strip off the filename. - strncpy(applicationDirectory, applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename)); + // Strip off the filename, keep only the path: + strncpy_s(applicationDirectory, sizeof(applicationDirectory), applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename)); applicationDirectory[strlen(applicationDirectory)] = '\0'; // Make sure new path is null terminated // Services are run by the SCM, and inherit its working directory - usually System32. @@ -323,7 +340,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[]) if (g_StatusHandle == NULL) { - OutputDebugString("RegisterServiceCtrlHandler() failed\n"); + OutputDebugStringA("RegisterServiceCtrlHandler() failed\n"); serviceSetState(0, SERVICE_STOPPED, GetLastError()); return; } @@ -333,7 +350,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[]) g_ServiceThread = CreateThread(NULL, 0, serviceWorkerThread, NULL, 0, NULL); if (g_ServiceThread == NULL) { - OutputDebugString("CreateThread() failed\n"); + OutputDebugStringA("CreateThread() failed\n"); serviceSetState(0, SERVICE_STOPPED, GetLastError()); return; } @@ -347,14 +364,79 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[]) +std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv) +{ + try + { + TCLAP::CmdLine cmd("MCServer"); + + TCLAP::ValueArg<int> slotsArg("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd); + + TCLAP::MultiArg<int> portsArg("p", "port", "The port number the server should listen to", false, "port", cmd); + + TCLAP::SwitchArg commLogArg("", "log-comm", "Log server client communications to file", cmd); + + TCLAP::SwitchArg commLogInArg("", "log-comm-in", "Log inbound server client communications to file", cmd); + + TCLAP::SwitchArg commLogOutArg("", "log-comm-out", "Log outbound server client communications to file", cmd); + + TCLAP::SwitchArg noBufArg("", "no-output-buffering", "Disable output buffering", cmd); + + cmd.parse(argc, argv); + + auto repo = cpp14::make_unique<cMemorySettingsRepository>(); + + if (slotsArg.isSet()) + { + + int slots = slotsArg.getValue(); + + repo->AddValue("Server", "MaxPlayers", static_cast<Int64>(slots)); + + } + + if (portsArg.isSet()) + { + std::vector<int> ports = portsArg.getValue(); + for (auto port : ports) + { + repo->AddValue("Server", "Port", static_cast<Int64>(port)); + } + } + + if (commLogArg.getValue()) + { + g_ShouldLogCommIn = true; + g_ShouldLogCommOut = true; + } + else + { + g_ShouldLogCommIn = commLogInArg.getValue(); + g_ShouldLogCommOut = commLogOutArg.getValue(); + } + + if (noBufArg.getValue()) + { + setvbuf(stdout, nullptr, _IONBF, 0); + } + + repo->SetReadOnly(); + + return repo; + } + catch (TCLAP::ArgException &e) + { + printf("error reading command line %s for arg %s", e.error().c_str(), e.argId().c_str()); + return cpp14::make_unique<cMemorySettingsRepository>(); + } +} + //////////////////////////////////////////////////////////////////////////////// // main: -int main( int argc, char **argv) +int main(int argc, char **argv) { - UNUSED(argc); - UNUSED(argv); #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) InitLeakFinder(); @@ -409,39 +491,13 @@ int main( int argc, char **argv) // DEBUG: test the dumpfile creation: // *((int *)0) = 0; + auto argsRepo = parseArguments(argc, argv); + // Check if comm logging is to be enabled: for (int i = 0; i < argc; i++) { AString Arg(argv[i]); - if ( - (NoCaseCompare(Arg, "/commlog") == 0) || - (NoCaseCompare(Arg, "/logcomm") == 0) - ) - { - g_ShouldLogCommIn = true; - g_ShouldLogCommOut = true; - } - else if ( - (NoCaseCompare(Arg, "/commlogin") == 0) || - (NoCaseCompare(Arg, "/comminlog") == 0) || - (NoCaseCompare(Arg, "/logcommin") == 0) - ) - { - g_ShouldLogCommIn = true; - } - else if ( - (NoCaseCompare(Arg, "/commlogout") == 0) || - (NoCaseCompare(Arg, "/commoutlog") == 0) || - (NoCaseCompare(Arg, "/logcommout") == 0) - ) - { - g_ShouldLogCommOut = true; - } - else if (NoCaseCompare(Arg, "nooutbuf") == 0) - { - setvbuf(stdout, nullptr, _IONBF, 0); - } - else if (NoCaseCompare(Arg, "/service") == 0) + if (NoCaseCompare(Arg, "/service") == 0) { cRoot::m_RunAsService = true; } @@ -467,7 +523,7 @@ int main( int argc, char **argv) #endif { // Not running as a service, do normal startup - universalMain(); + universalMain(std::move(argsRepo)); } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) |