summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore12
-rwxr-xr-xCIbuild.sh15
-rw-r--r--Server/Plugins/APIDump/APIDesc.lua13
-rw-r--r--Server/Plugins/APIDump/main_APIDump.lua113
m---------Server/Plugins/Core0
-rwxr-xr-xcompile.sh6
-rw-r--r--src/Bindings/LuaState.cpp12
-rw-r--r--src/Bindings/LuaState.h1
-rw-r--r--src/Bindings/ManualBindings.cpp410
-rw-r--r--src/Bindings/ManualBindings_World.cpp55
-rw-r--r--src/Blocks/BlockPiston.cpp25
-rw-r--r--src/Mobs/Guardian.cpp2
-rw-r--r--src/Mobs/Monster.cpp45
-rw-r--r--src/Mobs/Monster.h15
-rw-r--r--src/Mobs/Squid.cpp2
-rw-r--r--src/OSSupport/File.cpp206
-rw-r--r--src/OSSupport/File.h75
-rw-r--r--src/Protocol/Protocol18x.cpp14
-rw-r--r--src/World.cpp216
-rw-r--r--src/World.h110
20 files changed, 914 insertions, 433 deletions
diff --git a/.gitignore b/.gitignore
index 1386548bb..6eb5419a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,7 +56,7 @@ players
world
world_nether
-#cmake stuff
+# cmake stuff
CMakeFiles/
cmake_install.cmake
CMakeCache.txt
@@ -79,7 +79,7 @@ src/Bindings/BindingDependencies.txt
Cuberite.dir/
src/AllFiles.lst
-#win32 cmake stuff
+# win32 cmake stuff
*.vcxproj
*.vcproj
*.vcxproj.filters
@@ -88,10 +88,16 @@ src/AllFiles.lst
*.sln
*.idb
-#cmake output folders
+# cmake output folders
ZERO_CHECK.dir/
Debug/
DebugProfile/
Release/
ReleaseProfile/
*.dir/
+
+# APIDump-generated status files:
+Server/cuberite_api.lua
+Server/official_undocumented.lua
+Server/NewlyUndocumented.lua
+
diff --git a/CIbuild.sh b/CIbuild.sh
index 8a91549ad..a6a4282a7 100755
--- a/CIbuild.sh
+++ b/CIbuild.sh
@@ -21,8 +21,19 @@ cd ..
echo "Building..."
make -j 2;
make -j 2 test ARGS="-V";
+
+echo "Testing..."
cd Server/;
if [ "$TRAVIS_CUBERITE_BUILD_TYPE" != "COVERAGE" ]; then
- echo restart | $CUBERITE_PATH;
- echo stop | $CUBERITE_PATH;
+ $CUBERITE_PATH << EOF
+load APIDump
+apicheck
+restart
+stop
+EOF
+ if [ -f ./NewlyUndocumented.lua ]; then
+ echo "ERROR: Newly undocumented API symbols found:"
+ cat ./NewlyUndocumented.lua
+ exit 1
+ fi
fi
diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua
index 187c72b9e..2fcd02a17 100644
--- a/Server/Plugins/APIDump/APIDesc.lua
+++ b/Server/Plugins/APIDump/APIDesc.lua
@@ -1008,16 +1008,20 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
Provides helper functions for manipulating and querying the filesystem. Most functions are static,
so they should be called directly on the cFile class itself:
<pre class="prettyprint lang-lua">
-cFile:Delete("/usr/bin/virus.exe");
+cFile:DeleteFile("/usr/bin/virus.exe");
</pre></p>
]],
Functions =
{
ChangeFileExt = { Params = "FileName, NewExt", Return = "string", Notes = "(STATIC) Returns FileName with its extension changed to NewExt. NewExt may begin with a dot, but needn't, the result is the same in both cases (the first dot, if present, is ignored). FileName may contain path elements, extension is recognized as the last dot after the last path separator in the string." },
Copy = { Params = "SrcFileName, DstFileName", Return = "bool", Notes = "(STATIC) Copies a single file to a new destination. Returns true if successful. Fails if the destination already exists." },
- CreateFolder = { Params = "FolderName", Return = "bool", Notes = "(STATIC) Creates a new folder. Returns true if successful." },
- Delete = { Params = "FileName", Return = "bool", Notes = "(STATIC) Deletes the specified file. Returns true if successful." },
- Exists = { Params = "FileName", Return = "bool", Notes = "(STATIC) Returns true if the specified file exists." },
+ CreateFolder = { Params = "FolderPath", Return = "bool", Notes = "(STATIC) Creates a new folder. Returns true if successful. Only a single level can be created at a time, use CreateFolderRecursive() to create multiple levels of folders at once." },
+ CreateFolderRecursive = { Params = "FolderPath", Return = "bool", Notes = "(STATIC) Creates a new folder, creating its parents if needed. Returns true if successful." },
+ Delete = { Params = "Path", Return = "bool", Notes = "(STATIC) Deletes the specified file or folder. Returns true if successful. Only deletes folders that are empty.<br/><b>NOTE</b>: If you already know if the object is a file or folder, use DeleteFile() or DeleteFolder() explicitly." },
+ DeleteFile = { Params = "FilePath", Return = "bool", Notes = "(STATIC) Deletes the specified file. Returns true if successful." },
+ DeleteFolder = { Params = "FolderPath", Return = "bool", Notes = "(STATIC) Deletes the specified file or folder. Returns true if successful. Only deletes folders that are empty." },
+ DeleteFolderContents = { Params = "FolderPath", Return = "bool", Notes = "(STATIC) Deletes everything from the specified folder, recursively. The specified folder stays intact. Returns true if successful." },
+ Exists = { Params = "Path", Return = "bool", Notes = "(STATIC) Returns true if the specified file or folder exists.<br/><b>OBSOLETE</b>, use IsFile() or IsFolder() instead" },
GetExecutableExt = { Params = "", Return = "string", Notes = "(STATIC) Returns the customary executable extension (including the dot) used by the current platform (\".exe\" on Windows, empty string on Linux). " },
GetFolderContents = { Params = "FolderName", Return = "array table of strings", Notes = "(STATIC) Returns the contents of the specified folder, as an array table of strings. Each filesystem object is listed. Use the IsFile() and IsFolder() functions to determine the object type." },
GetLastModificationTime = { Params = "Path", Return = "number", Notes = "(STATIC) Returns the last modification time (in current timezone) of the specified file or folder. Returns zero if file not found / not accessible. The returned value is in the same units as values returned by os.time()." },
@@ -3025,6 +3029,7 @@ end
"cFurnaceEntity.__cBlockEntityWindowOwner__",
"cHopperEntity.__cBlockEntityWindowOwner__",
"cLuaWindow.__cItemGrid__cListener__",
+ "Globals._CuberiteInternal_.*", -- Ignore all internal Cuberite constants
},
IgnoreVariables =
diff --git a/Server/Plugins/APIDump/main_APIDump.lua b/Server/Plugins/APIDump/main_APIDump.lua
index c0685087a..7cbd0d160 100644
--- a/Server/Plugins/APIDump/main_APIDump.lua
+++ b/Server/Plugins/APIDump/main_APIDump.lua
@@ -174,11 +174,9 @@ local function WriteArticles(f)
]]);
for _, extra in ipairs(g_APIDesc.ExtraPages) do
local SrcFileName = g_PluginFolder .. "/" .. extra.FileName;
- if (cFile:Exists(SrcFileName)) then
+ if (cFile:IsFile(SrcFileName)) then
local DstFileName = "API/" .. extra.FileName;
- if (cFile:Exists(DstFileName)) then
- cFile:Delete(DstFileName);
- end
+ cFile:Delete(DstFileName);
cFile:Copy(SrcFileName, DstFileName);
f:write("<li><a href=\"" .. extra.FileName .. "\">" .. extra.Title .. "</a></li>\n");
else
@@ -1002,7 +1000,7 @@ local function ListUndocumentedObjects(API, UndocumentedHooks)
if (HasFunctions or HasConstants or HasVariables) then
f:write("\t\t" .. cls.Name .. " =\n\t\t{\n");
if ((cls.Desc == nil) or (cls.Desc == "")) then
- f:write("\t\t\tDesc = \"\"\n");
+ f:write("\t\t\tDesc = \"\",\n");
end
end
@@ -1112,7 +1110,7 @@ local function ListMissingPages()
local NumLinks = 0;
for PageName, Referrers in pairs(g_TrackedPages) do
NumLinks = NumLinks + 1;
- if not(cFile:Exists("API/" .. PageName .. ".html")) then
+ if not(cFile:IsFile("API/" .. PageName .. ".html")) then
table.insert(MissingPages, {Name = PageName, Refs = Referrers} );
end
end;
@@ -1566,9 +1564,8 @@ end
-local function DumpApi()
- LOG("Dumping the API...")
-
+--- Prepares the API and Globals tables containing the documentation
+local function PrepareApi()
-- Load the API descriptions from the Classes and Hooks subfolders:
-- This needs to be done each time the command is invoked because the export modifies the tables' contents
dofile(g_PluginFolder .. "/APIDesc.lua")
@@ -1618,6 +1615,19 @@ local function DumpApi()
LOG("Reading descriptions...");
ReadDescriptions(API);
+ return API, Globals
+end
+
+
+
+
+
+local function DumpApi()
+ LOG("Dumping the API...")
+
+ -- Match the currently exported API with the available documentation:
+ local API, Globals = PrepareApi()
+
-- Check that the API lists the inheritance properly, report any problems to a file:
CheckAPIDescendants(API)
@@ -1668,6 +1678,86 @@ end
+local function HandleCmdApiCheck(a_Split, a_EntireCmd)
+ -- Download the official API stats on undocumented stuff:
+ -- (We need a blocking downloader, which is impossible with the current cNetwork API)
+ assert(os.execute("wget -O official_undocumented.lua http://apidocs.cuberite.org/_undocumented.lua"))
+ local OfficialStats = cFile:ReadWholeFile("official_undocumented.lua")
+ if (OfficialStats == "") then
+ return true, "Cannot load official stats"
+ end
+
+ -- Load the API stats as a Lua file, process into usable dictionary:
+ local Loaded, Msg = loadstring(OfficialStats)
+ if not(Loaded) then
+ return true, "Cannot load official stats: " .. (Msg or "<unknown error>")
+ end
+ local OfficialStatsDict = {}
+ setfenv(Loaded, OfficialStatsDict)
+ local IsSuccess, ErrMsg = pcall(Loaded)
+ if not(IsSuccess) then
+ return true, "Cannot parse official stats: " .. tostring(ErrMsg or "<unknown error>")
+ end
+ local Parsed = {}
+ for clsK, clsV in pairs(OfficialStatsDict.g_APIDesc.Classes) do
+ local cls =
+ {
+ Desc = not(clsV.Desc), -- set to true if the Desc was not documented in the official docs
+ Functions = {},
+ Constants = {}
+ }
+ for funK, _ in pairs(clsV.Functions or {}) do
+ cls.Functions[funK] = true
+ end
+ for conK, _ in pairs(clsV.Constants or {}) do
+ cls.Constants[conK] = true
+ end
+ Parsed[clsK] = cls
+ end
+
+ -- Get the current API's undocumented stats:
+ local API = PrepareApi()
+
+ -- Compare the two sets of undocumented stats, list anything extra in current:
+ local res = {}
+ local ins = table.insert
+ for _, cls in ipairs(API) do
+ local ParsedOfficial = Parsed[cls.Name] or {}
+ if (not(cls.Desc) and ParsedOfficial.Desc) then
+ ins(res, cls.Name .. ".Desc")
+ end
+ local ParsedOfficialFns = ParsedOfficial.Functions or {}
+ for _, funK in ipairs(cls.UndocumentedFunctions or {}) do
+ if not(ParsedOfficialFns[funK]) then
+ ins(res, cls.Name .. "." .. funK .. " (function)")
+ end
+ end
+ local ParsedOfficialCons = ParsedOfficial.Constants or {}
+ for _, conK in ipairs(cls.UndocumentedConstants or {}) do
+ if not(ParsedOfficialCons[conK]) then
+ ins(res, cls.Name .. "." .. conK .. " (constant)")
+ end
+ end
+ end
+ table.sort(res)
+
+ -- Bail out if no items found:
+ if not(res[1]) then
+ return true, "No new undocumented functions"
+ end
+
+ -- Save any found items to a file:
+ local f = io.open("NewlyUndocumented.lua", "w")
+ f:write(table.concat(res, "\n"))
+ f:close()
+
+ return true, "Newly undocumented items: " .. #res .. "\n" .. table.concat(res, "\n")
+end
+
+
+
+
+
function Initialize(Plugin)
g_Plugin = Plugin;
g_PluginFolder = Plugin:GetLocalFolder();
@@ -1675,8 +1765,9 @@ function Initialize(Plugin)
LOG("Initialising " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
-- Bind a console command to dump the API:
- cPluginManager:BindConsoleCommand("api", HandleCmdApi, "Dumps the Lua API docs into the API/ subfolder")
- cPluginManager:BindConsoleCommand("apishow", HandleCmdApiShow, "Runs the default browser to show the API docs")
+ cPluginManager:BindConsoleCommand("api", HandleCmdApi, "Dumps the Lua API docs into the API/ subfolder")
+ cPluginManager:BindConsoleCommand("apicheck", HandleCmdApiCheck, "Checks the Lua API documentation stats against the official stats")
+ cPluginManager:BindConsoleCommand("apishow", HandleCmdApiShow, "Runs the default browser to show the API docs")
-- Add a WebAdmin tab that has a Dump button
g_Plugin:AddWebTab("APIDump", HandleWebAdminDump)
diff --git a/Server/Plugins/Core b/Server/Plugins/Core
-Subproject ee01115ac848116cf58956fa6d031dbbadddba0
+Subproject b1e3f972ebf475bfbe1dfc8bccc0240977d11d3
diff --git a/compile.sh b/compile.sh
index fae149667..19d49b6b2 100755
--- a/compile.sh
+++ b/compile.sh
@@ -164,10 +164,9 @@ git submodule update --init
# Cmake.
echo " --- Running cmake..."
-popd
if [ ! -d build-cuberite ]; then mkdir build-cuberite; fi
pushd build-cuberite
-cmake ../cuberite/ -DCMAKE_BUILD_TYPE=$BUILDTYPE || error "cmake failed"
+cmake .. -DCMAKE_BUILD_TYPE=$BUILDTYPE || error "cmake failed"
# Make.
@@ -178,7 +177,7 @@ echo
# Echo: Compilation complete.
popd > /dev/null
-pushd cuberite/Server > /dev/null
+pushd Server > /dev/null
echo
echo "-----------------"
echo "Compilation done!"
@@ -192,6 +191,7 @@ fi
echo
echo "Enjoy :)"
popd > /dev/null
+popd > /dev/null
exit 0
:windows_detected
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 4c0dfa676..c9e7815ca 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -676,6 +676,18 @@ void cLuaState::Push(int a_Value)
+void cLuaState::Push(long a_Value)
+{
+ ASSERT(IsValid());
+
+ tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value));
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
void cLuaState::Push(UInt32 a_Value)
{
ASSERT(IsValid());
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 759fdc54f..269a10369 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -248,6 +248,7 @@ public:
void Push(cLuaUDPEndpoint * a_UDPEndpoint);
void Push(double a_Value);
void Push(int a_Value);
+ void Push(long a_Value);
void Push(const UInt32 a_Value);
void Push(void * a_Ptr);
void Push(std::chrono::milliseconds a_time);
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 3fc5b477c..7e6839fdf 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -482,8 +482,258 @@ cPluginLua * cManualBindings::GetLuaPlugin(lua_State * L)
+static int tolua_cFile_ChangeFileExt(lua_State * tolua_S)
+{
+ // API signature:
+ // ChangeFileExt(string, string) -> string
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2, 3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString FileName, NewExt;
+ ASSERT(L.GetStackValues(2, FileName, NewExt));
+ L.Push(cFile::ChangeFileExt(FileName, NewExt));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_Copy(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:Copy(string, string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2, 3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString SrcFile, DstFile;
+ ASSERT(L.GetStackValues(2, SrcFile, DstFile));
+ L.Push(cFile::Copy(SrcFile, DstFile));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_CreateFolder(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:CreateFolder(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString FolderPath;
+ ASSERT(L.GetStackValues(2, FolderPath));
+ L.Push(cFile::CreateFolder(FolderPath));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_CreateFolderRecursive(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:CreateFolderRecursive(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString FolderPath;
+ ASSERT(L.GetStackValues(2, FolderPath));
+ L.Push(cFile::CreateFolderRecursive(FolderPath));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_Delete(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:Delete(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::Delete(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_DeleteFile(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:DeleteFile(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::DeleteFile(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_DeleteFolder(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:DeleteFolder(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::DeleteFolder(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_DeleteFolderContents(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:DeleteFolderContents(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::DeleteFolderContents(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_Exists(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:Exists(string) -> bool
+
+ // Obsolete, use IsFile() or IsFolder() instead
+ cLuaState L(tolua_S);
+ LOGWARNING("cFile:Exists() is obsolete, use cFile:IsFolder() or cFile:IsFile() instead!");
+ L.LogStackTrace();
+
+ // Check params:
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::Exists(Path));
+ return 1;
+}
+
+
+
+
+
static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
{
+ // API signature:
+ // cFile:GetFolderContents(string) -> {string, string, ...}
+
// Check params:
cLuaState LuaState(tolua_S);
if (
@@ -497,7 +747,7 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
// Get params:
AString Folder;
- LuaState.GetStackValues(2, Folder);
+ ASSERT(LuaState.GetStackValues(2, Folder));
// Execute and push result:
LuaState.Push(cFile::GetFolderContents(Folder));
@@ -508,8 +758,119 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
+static int tolua_cFile_GetLastModificationTime(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:GetLastModificationTime(string) -> number
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::GetLastModificationTime(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_GetSize(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:GetSize(string) -> number
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::GetSize(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_IsFile(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:IsFile(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::IsFile(Path));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cFile_IsFolder(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:IsFolder(string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2) ||
+ !L.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString Path;
+ ASSERT(L.GetStackValues(2, Path));
+ L.Push(cFile::IsFolder(Path));
+ return 1;
+}
+
+
+
+
+
static int tolua_cFile_ReadWholeFile(lua_State * tolua_S)
{
+ // API signature:
+ // cFile:ReadWholeFile(string) -> string
+
// Check params:
cLuaState LuaState(tolua_S);
if (
@@ -523,7 +884,7 @@ static int tolua_cFile_ReadWholeFile(lua_State * tolua_S)
// Get params:
AString FileName;
- LuaState.GetStackValues(2, FileName);
+ ASSERT(LuaState.GetStackValues(2, FileName));
// Execute and push result:
LuaState.Push(cFile::ReadWholeFile(FileName));
@@ -534,6 +895,33 @@ static int tolua_cFile_ReadWholeFile(lua_State * tolua_S)
+static int tolua_cFile_Rename(lua_State * tolua_S)
+{
+ // API signature:
+ // cFile:Rename(string, string) -> bool
+
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cFile") ||
+ !L.CheckParamString(2, 3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Execute:
+ AString SrcPath, DstPath;
+ ASSERT(L.GetStackValues(2, SrcPath, DstPath));
+ L.Push(cFile::Rename(SrcPath, DstPath));
+ return 1;
+}
+
+
+
+
+
static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
{
// API function no longer available:
@@ -2846,8 +3234,22 @@ void cManualBindings::Bind(lua_State * tolua_S)
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_function(tolua_S, "ChangeFileExt", tolua_cFile_ChangeFileExt);
+ tolua_function(tolua_S, "Copy", tolua_cFile_Copy);
+ tolua_function(tolua_S, "CreateFolder", tolua_cFile_CreateFolder);
+ tolua_function(tolua_S, "CreateFolderRecursive", tolua_cFile_CreateFolderRecursive);
+ tolua_function(tolua_S, "Delete", tolua_cFile_Delete);
+ tolua_function(tolua_S, "DeleteFile", tolua_cFile_DeleteFile);
+ tolua_function(tolua_S, "DeleteFolder", tolua_cFile_DeleteFolder);
+ tolua_function(tolua_S, "DeleteFolderContents", tolua_cFile_DeleteFolderContents);
+ tolua_function(tolua_S, "Exists", tolua_cFile_Exists);
+ tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
+ tolua_function(tolua_S, "GetLastModificationTime", tolua_cFile_GetLastModificationTime);
+ tolua_function(tolua_S, "GetSize", tolua_cFile_GetSize);
+ tolua_function(tolua_S, "IsFile", tolua_cFile_IsFile);
+ tolua_function(tolua_S, "IsFolder", tolua_cFile_IsFolder);
+ tolua_function(tolua_S, "ReadWholeFile", tolua_cFile_ReadWholeFile);
+ tolua_function(tolua_S, "Rename", tolua_cFile_Rename);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cHopperEntity");
diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp
index 6f8499611..ba80d7130 100644
--- a/src/Bindings/ManualBindings_World.cpp
+++ b/src/Bindings/ManualBindings_World.cpp
@@ -374,7 +374,6 @@ static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
class cLuaWorldTask :
- public cWorld::cTask,
public cPluginLua::cResettable
{
public:
@@ -384,11 +383,7 @@ public:
{
}
-protected:
- int m_FnRef;
-
- // cWorld::cTask overrides:
- virtual void Run(cWorld & a_World) override
+ void Run(cWorld & a_World)
{
cCSLock Lock(m_CSPlugin);
if (m_Plugin != nullptr)
@@ -396,7 +391,10 @@ protected:
m_Plugin->Call(m_FnRef, &a_World);
}
}
-} ;
+
+protected:
+ int m_FnRef;
+};
@@ -433,9 +431,9 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S)
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);
+ auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
+ Plugin->AddResettable(ResettableTask);
+ self->QueueTask(std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1));
return 0;
}
@@ -483,35 +481,6 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
-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
@@ -548,11 +517,9 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
}
- int DelayTicks = static_cast<int>(tolua_tonumber(tolua_S, 2, 0));
-
- auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef);
- Plugin->AddResettable(task);
- World->ScheduleTask(DelayTicks, static_cast<cWorld::cTaskPtr>(task));
+ auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
+ Plugin->AddResettable(ResettableTask);
+ World->ScheduleTask(static_cast<int>(tolua_tonumber(tolua_S, 2, 0)), std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1));
return 0;
}
diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp
index 4e4814242..94782a7ed 100644
--- a/src/Blocks/BlockPiston.cpp
+++ b/src/Blocks/BlockPiston.cpp
@@ -27,7 +27,6 @@
} \
}
-#define PISTON_TICK_DELAY 1
#define PISTON_MAX_PUSH_DISTANCE 12
@@ -156,15 +155,12 @@ void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ,
// Push blocks, from the furthest to the nearest:
int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ;
NIBBLETYPE currBlockMeta;
- std::vector<Vector3i> ScheduledBlocks;
- ScheduledBlocks.reserve(PISTON_MAX_PUSH_DISTANCE);
for (int i = dist + 1; i > 1; i--)
{
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta);
- a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta, false);
- ScheduledBlocks.push_back(Vector3i(oldx, oldy, oldz));
+ a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta);
oldx = a_BlockX;
oldy = a_BlockY;
oldz = a_BlockZ;
@@ -173,13 +169,11 @@ void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ,
int extx = a_BlockX;
int exty = a_BlockY;
int extz = a_BlockZ;
- ScheduledBlocks.push_back(Vector3i(extx, exty, extz));
AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1);
// "a_Block" now at piston body, "ext" at future extension
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8);
- a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false);
- a_World->ScheduleTask(PISTON_TICK_DELAY, static_cast<cWorld::cTaskPtr>(std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks)));
+ a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0));
}
@@ -223,23 +217,14 @@ void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ
if (CanPull(tempBlock, tempMeta))
{
// Pull the block
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, tempBlock, tempMeta, false);
- a_World->SetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, false);
-
- std::vector<Vector3i> ScheduledBlocks;
- ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
- ScheduledBlocks.push_back(Vector3i(tempx, tempy, tempz));
- a_World->ScheduleTask(PISTON_TICK_DELAY + 1, static_cast<cWorld::cTaskPtr>(std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks)));
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, tempBlock, tempMeta);
+ a_World->SetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0);
return;
}
}
// Retract without pulling
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0, false);
-
- std::vector<Vector3i> ScheduledBlocks;
- ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
- a_World->ScheduleTask(PISTON_TICK_DELAY + 1, static_cast<cWorld::cTaskPtr>(std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks)));
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
}
diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp
index 1429e2b13..1bee8fdfb 100644
--- a/src/Mobs/Guardian.cpp
+++ b/src/Mobs/Guardian.cpp
@@ -37,7 +37,7 @@ void cGuardian::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cGuardian::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- m_IsFollowingPath = false; // Disable Pathfinding until it's fixed. TODO
+ m_PathfinderActivated = false; // Disable Pathfinding until it's fixed. TODO
// We must first process current location, and only then tick, otherwise we risk processing a location in a chunk
// that is not where the entity currently resides (FS #411)
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index b28e94ec1..4d9b9ca06 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -78,6 +78,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_IsFollowingPath(false)
, m_PathfinderActivated(false)
, m_GiveUpCounter(0)
+ , m_TicksSinceLastPathReset(1000)
, m_LastGroundHeight(POSY_TOINT)
, m_JumpCoolDown(0)
, m_IdleInterval(0)
@@ -128,6 +129,11 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
{
return false;
}
+ if (m_TicksSinceLastPathReset < 1000)
+ {
+ // No need to count beyond 1000. 1000 is arbitary here.
+ ++m_TicksSinceLastPathReset;
+ }
if (ReachedFinalDestination())
{
@@ -135,6 +141,26 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
return false;
}
+ 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 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())))
+ )
+ {
+ /* 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();
+ }
+ }
+ }
+
if (m_Path == nullptr)
{
if (!EnsureProperDestination(a_Chunk))
@@ -143,16 +169,21 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
return false;
}
m_GiveUpCounter = 40;
- m_Path = new cPath(a_Chunk, GetPosition(), m_FinalDestination, 20, GetWidth(), GetHeight());
+ m_NoPathToTarget = false;
+ m_NoMoreWayPoints = false;
+ m_PathFinderDestination = m_FinalDestination;
+ m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight());
}
switch (m_Path->Step(a_Chunk))
{
case ePathFinderStatus::NEARBY_FOUND:
{
- m_FinalDestination = m_Path->AcceptNearbyPath();
+ m_NoPathToTarget = true;
+ m_PathFinderDestination = m_Path->AcceptNearbyPath();
break;
}
+
case ePathFinderStatus::PATH_NOT_FOUND:
{
StopMovingToPosition(); // Try to calculate a path again.
@@ -166,10 +197,9 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
}
case ePathFinderStatus::PATH_FOUND:
{
- if ((--m_GiveUpCounter) == 0)
+ if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
{
- // Failed to reach a waypoint - that's a failure condition whichever point we're at
- if (m_EMState == CHASING)
+ if (m_EMState == ATTACKING)
{
ResetPathFinding(); // Try to calculate a path again.
// This results in mobs hanging around an unreachable target (player).
@@ -188,6 +218,10 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
}
}
+ else
+ {
+ m_NoMoreWayPoints = true;
+ }
m_IsFollowingPath = true;
return true;
@@ -379,6 +413,7 @@ void cMonster::StopMovingToPosition()
void cMonster::ResetPathFinding(void)
{
+ m_TicksSinceLastPathReset = 0;
m_IsFollowingPath = false;
if (m_Path != nullptr)
{
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index f9b271bf9..22280110c 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -120,8 +120,8 @@ public:
char GetAge (void) const { return m_Age; }
void SetAge(char a_Age) { m_Age = a_Age; }
// tolua_end
-
-
+
+
// tolua_begin
/** Returns true if the monster has a custom name. */
@@ -178,6 +178,7 @@ protected:
/* 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_NextWayPointPosition;
@@ -185,6 +186,16 @@ protected:
/** Coordinates for the ultimate, final destination. */
Vector3d m_FinalDestination;
+ /** 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
If current Y is solid, goes up to find first nonsolid block, and returns that.
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
index 30fbfa1ff..1b0163e30 100644
--- a/src/Mobs/Squid.cpp
+++ b/src/Mobs/Squid.cpp
@@ -35,7 +35,7 @@ void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cSquid::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- m_IsFollowingPath = false; // Disable Pathfinding until it's fixed. TODO
+ m_PathfinderActivated = false; // Disable Pathfinding until it's fixed. TODO
// We must first process current location, and only then tick, otherwise we risk processing a location in a chunk
// that is not where the entity currently resides (FS #411)
diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp
index 09f6147b2..dec873ccd 100644
--- a/src/OSSupport/File.cpp
+++ b/src/OSSupport/File.cpp
@@ -16,11 +16,7 @@
cFile::cFile(void) :
- #ifdef USE_STDIO_FILE
m_File(nullptr)
- #else
- m_File(INVALID_HANDLE_VALUE)
- #endif // USE_STDIO_FILE
{
// Nothing needed yet
}
@@ -30,11 +26,7 @@ cFile::cFile(void) :
cFile::cFile(const AString & iFileName, eMode iMode) :
- #ifdef USE_STDIO_FILE
m_File(nullptr)
- #else
- m_File(INVALID_HANDLE_VALUE)
- #endif // USE_STDIO_FILE
{
Open(iFileName, iMode);
}
@@ -78,11 +70,11 @@ bool cFile::Open(const AString & iFileName, eMode iMode)
return false;
}
-#ifdef _WIN32
- m_File = _fsopen((FILE_IO_PREFIX + iFileName).c_str(), Mode, _SH_DENYWR);
-#else
- m_File = fopen((FILE_IO_PREFIX + iFileName).c_str(), Mode);
-#endif // _WIN32
+ #ifdef _WIN32
+ m_File = _fsopen((FILE_IO_PREFIX + iFileName).c_str(), Mode, _SH_DENYWR);
+ #else
+ m_File = fopen((FILE_IO_PREFIX + iFileName).c_str(), Mode);
+ #endif // _WIN32
if ((m_File == nullptr) && (iMode == fmReadWrite))
{
@@ -91,11 +83,11 @@ bool cFile::Open(const AString & iFileName, eMode iMode)
// So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents.
// Simply re-open for read-writing, erasing existing contents:
-#ifdef _WIN32
- m_File = _fsopen((FILE_IO_PREFIX + iFileName).c_str(), "wb+", _SH_DENYWR);
-#else
- m_File = fopen((FILE_IO_PREFIX + iFileName).c_str(), "wb+");
-#endif // _WIN32
+ #ifdef _WIN32
+ m_File = _fsopen((FILE_IO_PREFIX + iFileName).c_str(), "wb+", _SH_DENYWR);
+ #else
+ m_File = fopen((FILE_IO_PREFIX + iFileName).c_str(), "wb+");
+ #endif // _WIN32
}
return (m_File != nullptr);
@@ -310,7 +302,77 @@ bool cFile::Exists(const AString & a_FileName)
-bool cFile::Delete(const AString & a_FileName)
+bool cFile::Delete(const AString & a_Path)
+{
+ if (IsFolder(a_Path))
+ {
+ return DeleteFolder(a_Path);
+ }
+ else
+ {
+ return DeleteFile(a_Path);
+ }
+}
+
+
+
+
+
+bool cFile::DeleteFolder(const AString & a_FolderName)
+{
+ #ifdef _WIN32
+ return (RemoveDirectoryA(a_FolderName.c_str()) != 0);
+ #else // _WIN32
+ return (rmdir(a_FolderName.c_str()) == 0);
+ #endif // else _WIN32
+}
+
+
+
+
+
+bool cFile::DeleteFolderContents(const AString & a_FolderName)
+{
+ auto Contents = cFile::GetFolderContents(a_FolderName);
+ for (const auto item: Contents)
+ {
+ // Skip "." and ".." altogether:
+ if ((item == ".") || (item == ".."))
+ {
+ continue;
+ }
+
+ // Remove the item:
+ auto WholePath = a_FolderName + GetPathSeparator() + item;
+ if (IsFolder(WholePath))
+ {
+ if (!DeleteFolderContents(WholePath))
+ {
+ return false;
+ }
+ if (!DeleteFolder(WholePath))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!DeleteFile(WholePath))
+ {
+ return false;
+ }
+ }
+ } // for item - Contents[]
+
+ // All deletes succeeded
+ return true;
+}
+
+
+
+
+
+bool cFile::DeleteFile(const AString & a_FileName)
{
return (remove(a_FileName.c_str()) == 0);
}
@@ -331,7 +393,7 @@ bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName
bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName)
{
#ifdef _WIN32
- return (CopyFileA(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0);
+ return (CopyFileA(a_SrcFileName.c_str(), a_DstFileName.c_str(), FALSE) != 0);
#else
// Other OSs don't have a direct CopyFile equivalent, do it the harder way:
std::ifstream src(a_SrcFileName.c_str(), std::ios::binary);
@@ -409,57 +471,85 @@ bool cFile::CreateFolder(const AString & a_FolderPath)
+bool cFile::CreateFolderRecursive(const AString & a_FolderPath)
+{
+ // Special case: Fail if the path is empty
+ if (a_FolderPath.empty())
+ {
+ return false;
+ }
+
+ // Go through each path element and create the folder:
+ auto len = a_FolderPath.length();
+ auto PathSep = GetPathSeparator()[0];
+ for (decltype(len) i = 0; i < len; i++)
+ {
+ if (a_FolderPath[i] == PathSep)
+ {
+ CreateFolder(a_FolderPath.substr(0, i));
+ }
+ }
+ CreateFolder(a_FolderPath);
+
+ // Check the result by querying whether the final path exists:
+ return IsFolder(a_FolderPath);
+}
+
+
+
+
+
AStringVector cFile::GetFolderContents(const AString & a_Folder)
{
AStringVector AllFiles;
#ifdef _WIN32
- // If the folder name doesn't contain the terminating slash / backslash, add it:
- AString FileFilter = a_Folder;
- if (
- !FileFilter.empty() &&
- (FileFilter[FileFilter.length() - 1] != '\\') &&
- (FileFilter[FileFilter.length() - 1] != '/')
- )
- {
- FileFilter.push_back('\\');
- }
+ // If the folder name doesn't contain the terminating slash / backslash, add it:
+ AString FileFilter = a_Folder;
+ if (
+ !FileFilter.empty() &&
+ (FileFilter[FileFilter.length() - 1] != '\\') &&
+ (FileFilter[FileFilter.length() - 1] != '/')
+ )
+ {
+ FileFilter.push_back('\\');
+ }
- // Find all files / folders:
- FileFilter.append("*.*");
- HANDLE hFind;
- WIN32_FIND_DATAA FindFileData;
- if ((hFind = FindFirstFileA(FileFilter.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE)
- {
- do
+ // Find all files / folders:
+ FileFilter.append("*.*");
+ HANDLE hFind;
+ WIN32_FIND_DATAA FindFileData;
+ if ((hFind = FindFirstFileA(FileFilter.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE)
{
- AllFiles.push_back(FindFileData.cFileName);
- } while (FindNextFileA(hFind, &FindFileData));
- FindClose(hFind);
- }
+ do
+ {
+ AllFiles.push_back(FindFileData.cFileName);
+ } while (FindNextFileA(hFind, &FindFileData));
+ FindClose(hFind);
+ }
#else // _WIN32
- DIR * dp;
- struct dirent *dirp;
- AString Folder = a_Folder;
- if (Folder.empty())
- {
- Folder = ".";
- }
- if ((dp = opendir(Folder.c_str())) == nullptr)
- {
- LOGERROR("Error (%i) opening directory \"%s\"\n", errno, Folder.c_str());
- }
- else
- {
- while ((dirp = readdir(dp)) != nullptr)
+ DIR * dp;
+ struct dirent *dirp;
+ AString Folder = a_Folder;
+ if (Folder.empty())
{
- AllFiles.push_back(dirp->d_name);
+ Folder = ".";
+ }
+ if ((dp = opendir(Folder.c_str())) == nullptr)
+ {
+ LOGERROR("Error (%i) opening directory \"%s\"\n", errno, Folder.c_str());
+ }
+ else
+ {
+ while ((dirp = readdir(dp)) != nullptr)
+ {
+ AllFiles.push_back(dirp->d_name);
+ }
+ closedir(dp);
}
- closedir(dp);
- }
#endif // else _WIN32
diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h
index b8381ac0e..aab86811d 100644
--- a/src/OSSupport/File.h
+++ b/src/OSSupport/File.h
@@ -32,17 +32,6 @@ For reading entire files into memory, just use the static cFile::ReadWholeFile()
-#ifndef _WIN32
- #define USE_STDIO_FILE
-#endif // _WIN32
-
-// DEBUG:
-#define USE_STDIO_FILE
-
-
-
-
-
// tolua_begin
class cFile
@@ -101,48 +90,64 @@ public:
/** Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error */
int ReadRestOfFile(AString & a_Contents);
- // tolua_begin
-
/** Returns true if the file specified exists */
- static bool Exists(const AString & a_FileName);
+ static bool Exists(const AString & a_FileName); // Exported in ManualBindings.cpp
+
+ /** Deletes a file or a folder, returns true if successful.
+ Prefer to use DeleteFile or DeleteFolder, since those don't have the penalty of checking whether a_Path is a folder. */
+ static bool Delete(const AString & a_Path); // Exported in ManualBindings.cpp
+
+ /** Deletes a file, returns true if successful.
+ Returns false if a_FileName points to a folder. */
+ static bool DeleteFile(const AString & a_FileName); // Exported in ManualBindings.cpp
- /** Deletes a file, returns true if successful */
- static bool Delete(const AString & a_FileName);
+ /** Deletes a folder, returns true if successful.
+ Returns false if a_FolderName points to a file. */
+ static bool DeleteFolder(const AString & a_FolderName); // Exported in ManualBindings.cpp
+
+ /** Deletes all content from the specified folder.
+ The specified folder itself stays intact.
+ Returns true on success, false on failure. */
+ static bool DeleteFolderContents(const AString & a_FolderName); // Exported in ManualBindings.cpp
/** Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! */
- static bool Rename(const AString & a_OrigPath, const AString & a_NewPath);
+ static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); // Exported in ManualBindings.cpp
- /** Copies a file, returns true if successful. */
- static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName);
+ /** Copies a file, returns true if successful.
+ Overwrites the dest file if it already exists. */
+ static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); // Exported in ManualBindings.cpp
/** Returns true if the specified path is a folder */
- static bool IsFolder(const AString & a_Path);
+ static bool IsFolder(const AString & a_Path); // Exported in ManualBindings.cpp
/** Returns true if the specified path is a regular file */
- static bool IsFile(const AString & a_Path);
+ static bool IsFile(const AString & a_Path); // Exported in ManualBindings.cpp
/** Returns the size of the file, or a negative number on error */
- static long GetSize(const AString & a_FileName);
+ static long GetSize(const AString & a_FileName); // Exported in ManualBindings.cpp
/** 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);
-
- // tolua_end
+ static bool CreateFolder(const AString & a_FolderPath); // Exported in ManualBindings.cpp
- /** 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
+ /** Creates a new folder with the specified name, creating its parents if needed. Path may be relative or absolute.
+ Returns true if the folder exists at the end of the operation (either created, or already existed).
+ Supports only paths that use the path separator used by the current platform (MSVC CRT supports slashes for file paths, too, but this function doesn't) */
+ static bool CreateFolderRecursive(const AString & a_FolderPath); // Exported in ManualBindings.cpp
+
+ /** Returns the entire contents of the specified file as a string. Returns empty string on error. */
+ static AString ReadWholeFile(const AString & a_FileName); // Exported in ManualBindings.cpp
/** 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);
+ static AString ChangeFileExt(const AString & a_FileName, const AString & a_NewExt); // Exported in ManualBindings.cpp
/** 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);
+ If the file is not found / accessible, zero is returned.
+ Works for folders, too, when specified without the trailing path separator. */
+ static unsigned GetLastModificationTime(const AString & a_FileName); // Exported in ManualBindings.cpp
+
+ // tolua_begin
/** 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. */
@@ -162,11 +167,7 @@ public:
void Flush(void);
private:
- #ifdef USE_STDIO_FILE
FILE * m_File;
- #else
- HANDLE m_File;
- #endif
} ; // tolua_export
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index d5ecb8023..ea71103f7 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -109,6 +109,20 @@ cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAdd
m_IsEncrypted(false),
m_LastSentDimension(dimNotSet)
{
+
+ // BungeeCord handling:
+ // If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field:
+ // hostname\00ip-address\00uuid\00profile-properties-as-json
+ AStringVector Params;
+ if (cRoot::Get()->GetServer()->ShouldAllowBungeeCord() && SplitZeroTerminatedStrings(a_ServerAddress, Params) && (Params.size() == 4))
+ {
+ LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
+ m_ServerAddress = Params[0];
+ m_Client->SetIPString(Params[1]);
+ m_Client->SetUUID(cMojangAPI::MakeUUIDShort(Params[2]));
+ m_Client->SetProperties(Params[3]);
+ }
+
// Create the comm log file, if so requested:
if (g_ShouldLogCommIn || g_ShouldLogCommOut)
{
diff --git a/src/World.cpp b/src/World.cpp
index 5920b83fe..824ebf3fa 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -166,7 +166,13 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_ChunkMap(),
m_bAnimals(true),
m_Weather(eWeather_Sunny),
- m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
+ m_WeatherInterval(24000), // Guaranteed 1 game-day of sunshine at server start :)
+ m_MaxSunnyTicks(180000), // 150 real-world minutes -+
+ m_MinSunnyTicks(12000), // 10 real-world minutes |
+ m_MaxRainTicks(24000), // 20 real-world minutes +- all values adapted from Vanilla 1.7.2
+ m_MinRainTicks(12000), // 10 real-world minutes |
+ m_MaxThunderStormTicks(15600), // 13 real-world minutes |
+ m_MinThunderStormTicks(3600), // 3 real-world minutes -+
m_MaxCactusHeight(3),
m_MaxSugarcaneHeight(4),
m_IsCactusBonemealable(false),
@@ -244,17 +250,25 @@ int cWorld::GetDefaultWeatherInterval(eWeather a_Weather)
{
case eWeather_Sunny:
{
- return 14400 + (m_TickRand.randInt() % 4800); // 12 - 16 minutes
+ auto dif = m_MaxSunnyTicks - m_MinSunnyTicks + 1;
+ return m_MinSunnyTicks + (m_TickRand.randInt() % dif);
}
case eWeather_Rain:
{
- return 9600 + (m_TickRand.randInt() % 7200); // 8 - 14 minutes
+ auto dif = m_MaxRainTicks - m_MinRainTicks + 1;
+ return m_MinRainTicks + (m_TickRand.randInt() % dif);
}
case eWeather_ThunderStorm:
{
- return 2400 + (m_TickRand.randInt() % 4800); // 2 - 6 minutes
+ auto dif = m_MaxThunderStormTicks - m_MinThunderStormTicks + 1;
+ return m_MinThunderStormTicks + (m_TickRand.randInt() % dif);
}
}
+
+ #ifndef __clang__
+ ASSERT(!"Unknown weather");
+ return -1;
+ #endif
}
@@ -472,6 +486,29 @@ void cWorld::Start(void)
m_IsDaylightCycleEnabled = IniFile.GetValueSetB("General", "IsDaylightCycleEnabled", true);
int GameMode = IniFile.GetValueSetI("General", "Gamemode", static_cast<int>(m_GameMode));
int Weather = IniFile.GetValueSetI("General", "Weather", static_cast<int>(m_Weather));
+
+ // Load the weather frequency data:
+ if (m_Dimension == dimOverworld)
+ {
+ m_MaxSunnyTicks = IniFile.GetValueSetI("Weather", "MaxSunnyTicks", m_MaxSunnyTicks);
+ m_MinSunnyTicks = IniFile.GetValueSetI("Weather", "MinSunnyTicks", m_MinSunnyTicks);
+ m_MaxRainTicks = IniFile.GetValueSetI("Weather", "MaxRainTicks", m_MaxRainTicks);
+ m_MinRainTicks = IniFile.GetValueSetI("Weather", "MinRainTicks", m_MinRainTicks);
+ m_MaxThunderStormTicks = IniFile.GetValueSetI("Weather", "MaxThunderStormTicks", m_MaxThunderStormTicks);
+ m_MinThunderStormTicks = IniFile.GetValueSetI("Weather", "MinThunderStormTicks", m_MinThunderStormTicks);
+ if (m_MaxSunnyTicks < m_MinSunnyTicks)
+ {
+ std::swap(m_MaxSunnyTicks, m_MinSunnyTicks);
+ }
+ if (m_MaxRainTicks < m_MinRainTicks)
+ {
+ std::swap(m_MaxRainTicks, m_MinRainTicks);
+ }
+ if (m_MaxThunderStormTicks < m_MinThunderStormTicks)
+ {
+ std::swap(m_MaxThunderStormTicks, m_MinThunderStormTicks);
+ }
+ }
if (GetDimension() == dimOverworld)
{
@@ -644,6 +681,11 @@ eWeather cWorld::ChooseNewWeather()
return ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny;
}
}
+
+ #ifndef __clang__
+ ASSERT(!"Unknown weather");
+ return eWeather_Sunny;
+ #endif
}
@@ -839,7 +881,6 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La
TickClients(static_cast<float>(a_Dt.count()));
TickQueuedBlocks();
TickQueuedTasks();
- TickScheduledTasks();
GetSimulatorManager()->Simulate(static_cast<float>(a_Dt.count()));
@@ -962,55 +1003,39 @@ void cWorld::TickMobs(std::chrono::milliseconds a_Dt)
void cWorld::TickQueuedTasks(void)
{
- // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks
- cTasks Tasks;
- {
- cCSLock Lock(m_CSTasks);
- std::swap(Tasks, m_Tasks);
- }
-
- // Execute and delete each task:
- for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr)
- {
- (*itr)->Run(*this);
- } // for itr - m_Tasks[]
-}
-
-
-
-
-
-void cWorld::TickScheduledTasks(void)
-{
// Move the tasks to be executed to a seperate vector to avoid deadlocks on accessing m_Tasks
- cScheduledTasks Tasks;
+ decltype(m_Tasks) Tasks;
{
- cCSLock Lock(m_CSScheduledTasks);
- 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-based for, we're modifying the container
+ cCSLock Lock(m_CSTasks);
+ if (m_Tasks.empty())
{
- if ((*itr)->m_TargetTick < std::chrono::duration_cast<cTickTimeLong>(WorldAge).count())
- {
- auto next = itr;
- ++next;
- Tasks.push_back(std::move(*itr));
- m_ScheduledTasks.erase(itr);
- itr = next;
- }
- else
+ return;
+ }
+
+ // Partition everything to be executed by returning false to move to end of list if time reached
+ auto MoveBeginIterator = std::partition(m_Tasks.begin(), m_Tasks.end(), [this](const decltype(m_Tasks)::value_type & a_Task)
{
- // All the eligible tasks have been moved, bail out now
- break;
+ if (a_Task.first < std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count())
+ {
+ return false;
+ }
+ return true;
}
- }
+ );
+
+ // Cut all the due tasks from m_Tasks into Tasks:
+ Tasks.insert(
+ Tasks.end(),
+ std::make_move_iterator(MoveBeginIterator),
+ std::make_move_iterator(m_Tasks.end())
+ );
+ m_Tasks.erase(MoveBeginIterator, m_Tasks.end());
}
- // Execute and delete each task:
- for (cScheduledTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr)
+ // Execute each task:
+ for (const auto & Task : Tasks)
{
- (*itr)->m_Task->Run(*this);
+ Task.second(*this);
} // for itr - m_Tasks[]
}
@@ -2662,7 +2687,7 @@ void cWorld::UnloadUnusedChunks(void)
void cWorld::QueueUnloadUnusedChunks(void)
{
- QueueTask(cpp14::make_unique<cWorld::cTaskUnloadUnusedChunks>());
+ QueueTask([](cWorld & a_World) { a_World.UnloadUnusedChunks(); });
}
@@ -3161,42 +3186,32 @@ void cWorld::SaveAllChunks(void)
void cWorld::QueueSaveAllChunks(void)
{
- QueueTask(std::make_shared<cWorld::cTaskSaveAllChunks>());
+ QueueTask([](cWorld & a_World) { a_World.SaveAllChunks(); });
}
-void cWorld::QueueTask(cTaskPtr a_Task)
+void cWorld::QueueTask(std::function<void(cWorld &)> a_Task)
{
cCSLock Lock(m_CSTasks);
- m_Tasks.push_back(std::move(a_Task));
+ m_Tasks.emplace_back(0, a_Task);
}
-void cWorld::ScheduleTask(int a_DelayTicks, std::function<void (cWorld&)> a_Func)
-{
- cTaskLambda task(a_Func);
- ScheduleTask(a_DelayTicks, static_cast<cTaskPtr>(std::make_shared<cTaskLambda>(task)));
-}
-void cWorld::ScheduleTask(int a_DelayTicks, cTaskPtr a_Task)
+
+void cWorld::ScheduleTask(int a_DelayTicks, std::function<void (cWorld &)> a_Task)
{
Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count();
-
- // Insert the task into the list of scheduled tasks, ordered by its target tick
- cCSLock Lock(m_CSScheduledTasks);
- for (cScheduledTasks::iterator itr = m_ScheduledTasks.begin(), end = m_ScheduledTasks.end(); itr != end; ++itr)
+
+ // Insert the task into the list of scheduled tasks
{
- if ((*itr)->m_TargetTick >= TargetTick)
- {
- m_ScheduledTasks.insert(itr, cScheduledTaskPtr(new cScheduledTask(TargetTick, a_Task)));
- return;
- }
+ cCSLock Lock(m_CSTasks);
+ m_Tasks.emplace_back(TargetTick, a_Task);
}
- m_ScheduledTasks.push_back(cScheduledTaskPtr(new cScheduledTask(TargetTick, a_Task)));
}
@@ -3625,75 +3640,6 @@ void cWorld::AddQueuedPlayers(void)
////////////////////////////////////////////////////////////////////////////////
-// cWorld::cTaskSaveAllChunks:
-
-void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
-{
- a_World.SaveAllChunks();
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cWorld::cTaskUnloadUnusedChunks
-
-void cWorld::cTaskUnloadUnusedChunks::Run(cWorld & a_World)
-{
- a_World.UnloadUnusedChunks();
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cWorld::cTaskSendBlockToAllPlayers
-
-cWorld::cTaskSendBlockToAllPlayers::cTaskSendBlockToAllPlayers(std::vector<Vector3i> & a_SendQueue) :
- m_SendQueue(a_SendQueue)
-{
-}
-
-void cWorld::cTaskSendBlockToAllPlayers::Run(cWorld & a_World)
-{
- class cPlayerCallback :
- public cPlayerListCallback
- {
- public:
- cPlayerCallback(std::vector<Vector3i> & a_SendQueue, cWorld & a_CallbackWorld) :
- m_SendQueue(a_SendQueue),
- m_World(a_CallbackWorld)
- {
- }
-
- virtual bool Item(cPlayer * a_Player)
- {
- for (std::vector<Vector3i>::const_iterator itr = m_SendQueue.begin(); itr != m_SendQueue.end(); ++itr)
- {
- m_World.SendBlockTo(itr->x, itr->y, itr->z, a_Player);
- }
- return false;
- }
-
- private:
-
- std::vector<Vector3i> m_SendQueue;
- cWorld & m_World;
-
- } PlayerCallback(m_SendQueue, a_World);
-
- a_World.ForEachPlayer(PlayerCallback);
-}
-
-void cWorld::cTaskLambda::Run(cWorld & a_World)
-{
- m_func(a_World);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
// cWorld::cChunkGeneratorCallbacks:
cWorld::cChunkGeneratorCallbacks::cChunkGeneratorCallbacks(cWorld & a_World) :
diff --git a/src/World.h b/src/World.h
index 1902296be..8529ffb4d 100644
--- a/src/World.h
+++ b/src/World.h
@@ -96,72 +96,8 @@ public:
typedef cCSLock super;
public:
cLock(cWorld & a_World);
- } ;
-
-
- /** A common ancestor for all tasks queued onto the tick thread */
- class cTask
- {
- public:
- cTask(const cTask & other) = default;
- virtual ~cTask() {}
- virtual void Run(cWorld & a_World) = 0;
-
- protected:
- cTask() {}
- } ;
-
- typedef SharedPtr<cTask> cTaskPtr;
- typedef std::vector<cTaskPtr> cTasks;
-
-
- class cTaskSaveAllChunks :
- public cTask
- {
- protected:
- // cTask overrides:
- virtual void Run(cWorld & a_World) override;
- } ;
-
-
- class cTaskUnloadUnusedChunks :
- public cTask
- {
- protected:
- // cTask overrides:
- virtual void Run(cWorld & a_World) override;
- };
-
-
- class cTaskSendBlockToAllPlayers :
- public cTask
- {
- public:
- cTaskSendBlockToAllPlayers(std::vector<Vector3i> & a_SendQueue);
-
- protected:
- // cTask overrides:
- virtual void Run(cWorld & a_World) override;
-
- std::vector<Vector3i> m_SendQueue;
- };
-
- class cTaskLambda :
- public cTask
- {
-
- public:
- cTaskLambda(std::function<void(cWorld&)> a_Func) :
- m_func(a_Func)
- { }
-
- protected:
- virtual void Run(cWorld & a_World) override;
-
- std::function<void(cWorld&)> m_func;
};
-
static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates
{
return "cWorld";
@@ -739,13 +675,10 @@ public:
void QueueSaveAllChunks(void); // tolua_export
/** Queues a task onto the tick thread. The task object will be deleted once the task is finished */
- void QueueTask(cTaskPtr a_Task); // Exported in ManualBindings.cpp
+ void QueueTask(std::function<void(cWorld &)> a_Task); // Exported in ManualBindings.cpp
/** Queues a lambda task onto the tick thread, with the specified delay. */
- void ScheduleTask(int a_DelayTicks, std::function<void(cWorld&)> a_Func);
-
- /** Queues a task onto the tick thread, with the specified delay. */
- void ScheduleTask(int a_DelayTicks, cTaskPtr a_Task);
+ void ScheduleTask(int a_DelayTicks, std::function<void(cWorld &)> a_Task);
/** Returns the number of chunks loaded */
int GetNumChunks() const; // tolua_export
@@ -912,27 +845,6 @@ private:
public:
cChunkGeneratorCallbacks(cWorld & a_World);
} ;
-
-
- /** A container for tasks that have been scheduled for a specific game tick */
- class cScheduledTask
- {
- public:
- Int64 m_TargetTick;
- cTaskPtr m_Task;
-
- /** Creates a new scheduled task; takes ownership of the task object passed to it. */
- cScheduledTask(Int64 a_TargetTick, cTaskPtr a_Task) :
- m_TargetTick(a_TargetTick),
- m_Task(a_Task)
- {
- }
-
- virtual ~cScheduledTask() {}
- };
-
- typedef std::unique_ptr<cScheduledTask> cScheduledTaskPtr;
- typedef std::list<cScheduledTaskPtr> cScheduledTasks;
AString m_WorldName;
@@ -1009,6 +921,9 @@ private:
eWeather m_Weather;
int m_WeatherInterval;
+ int m_MaxSunnyTicks, m_MinSunnyTicks;
+ int m_MaxRainTicks, m_MinRainTicks;
+ int m_MaxThunderStormTicks, m_MinThunderStormTicks;
int m_MaxCactusHeight;
int m_MaxSugarcaneHeight;
@@ -1062,16 +977,8 @@ private:
/** Guards the m_Tasks */
cCriticalSection m_CSTasks;
- /** Tasks that have been queued onto the tick thread; guarded by m_CSTasks */
- cTasks m_Tasks;
-
- /** Guards the m_ScheduledTasks */
- cCriticalSection m_CSScheduledTasks;
-
- /** Tasks that have been queued to be executed on the tick thread at target tick in the future.
- Ordered by increasing m_TargetTick.
- Guarded by m_CSScheduledTasks */
- cScheduledTasks m_ScheduledTasks;
+ /** Tasks that have been queued onto the tick thread, possibly to be executed at target tick in the future; guarded by m_CSTasks */
+ std::vector<std::pair<Int64, std::function<void(cWorld &)>>> m_Tasks;
/** Guards m_Clients */
cCriticalSection m_CSClients;
@@ -1118,9 +1025,6 @@ private:
/** Executes all tasks queued onto the tick thread */
void TickQueuedTasks(void);
- /** Executes all tasks queued onto the tick thread */
- void TickScheduledTasks(void);
-
/** Ticks all clients that are in this world */
void TickClients(float a_Dt);