1
0

Merge branch 'master' into Cocoa

This commit is contained in:
Howaner 2014-12-13 20:10:23 +01:00
commit a595a4a842
104 changed files with 2185 additions and 1754 deletions

View File

@ -6,6 +6,15 @@ compiler:
before_install:
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
# g++4.8
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
install:
# g++4.8
- sudo apt-get install -qq g++-4.8
- export CXX="g++-4.8"
# Build MCServer
script: ./CIbuild.sh

View File

@ -18,6 +18,7 @@ mgueydan
MikeHunsinger
mtilden
nesco
p-mcgowan
rs2k
SamJBarney
Sofapriester

View File

@ -2564,7 +2564,9 @@ World:ForEachEntity(
if not(a_Entity:IsMob()) then
return;
end
local Monster = tolua.cast(a_Entity, "cMonster"); -- Get the cMonster out of cEntity, now that we know the entity represents one.
-- Get the cMonster out of cEntity, now that we know the entity represents one.
local Monster = tolua.cast(a_Entity, "cMonster");
if (Monster:GetMobType() == mtSpider) then
Monster:TeleportToCoords(Monster:GetPosX(), Monster:GetPosY() + 100, Monster:GetPosZ());
end

@ -1 +1 @@
Subproject commit 4183d6cfb2049d0757d811a65bd4e75ed78b9f41
Subproject commit 39d980e3a3447ac23f61c7d65426b33ee6c0718d

View File

@ -35,39 +35,15 @@ function Initialize(Plugin)
-- _X: Disabled so that the normal operation doesn't interfere with anything
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
PM:BindCommand("/nick", "debuggers", HandleNickCmd, "- Gives you a custom name");
PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities");
PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities");
PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool");
PM:BindCommand("/testwnd", "debuggers", HandleTestWndCmd, "- Opens up a window using plugin API");
PM:BindCommand("/gc", "debuggers", HandleGCCmd, "- Activates the Lua garbage collector");
PM:BindCommand("/fast", "debuggers", HandleFastCmd, "- Switches between fast and normal movement speed");
PM:BindCommand("/dash", "debuggers", HandleDashCmd, "- Switches between fast and normal sprinting speed");
PM:BindCommand("/hunger", "debuggers", HandleHungerCmd, "- Lists the current hunger-related variables");
PM:BindCommand("/poison", "debuggers", HandlePoisonCmd, "- Sets food-poisoning for 15 seconds");
PM:BindCommand("/starve", "debuggers", HandleStarveCmd, "- Sets the food level to zero");
PM:BindCommand("/fl", "debuggers", HandleFoodLevelCmd, "- Sets the food level to the given value");
PM:BindCommand("/spidey", "debuggers", HandleSpideyCmd, "- Shoots a line of web blocks until it hits non-air");
PM:BindCommand("/ench", "debuggers", HandleEnchCmd, "- Provides an instant dummy enchantment window");
PM:BindCommand("/fs", "debuggers", HandleFoodStatsCmd, "- Turns regular foodstats message on or off");
PM:BindCommand("/arr", "debuggers", HandleArrowCmd, "- Creates an arrow going away from the player");
PM:BindCommand("/fb", "debuggers", HandleFireballCmd, "- Creates a ghast fireball as if shot by the player");
PM:BindCommand("/xpa", "debuggers", HandleAddExperience, "- Adds 200 experience to the player");
PM:BindCommand("/xpr", "debuggers", HandleRemoveXp, "- Remove all xp");
PM:BindCommand("/fill", "debuggers", HandleFill, "- Fills all block entities in current chunk with junk");
PM:BindCommand("/fr", "debuggers", HandleFurnaceRecipe, "- Shows the furnace recipe for the currently held item");
PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace");
PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()");
PM:BindCommand("/cs", "debuggers", HandleChunkStay, "- Tests the ChunkStay Lua integration for the specified chunk coords");
PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings");
PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one");
PM:BindCommand("/wesel", "debuggers", HandleWESel, "- Expands the current WE selection by 1 block in X/Z");
PM:BindCommand("/rmitem", "debuggers", HandleRMItem, "- Remove the specified item from the inventory.");
PM:BindCommand("/pickups", "debuggers", HandlePickups, "- Spawns random pickups around you");
PM:BindCommand("/poof", "debuggers", HandlePoof, "- Nudges pickups close to you away from you");
-- Load the InfoReg shared library:
dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
-- Bind all the commands:
RegisterPluginInfoCommands();
-- Bind all the console commands:
RegisterPluginInfoConsoleCommands();
PM:BindConsoleCommand("sched", HandleConsoleSchedule, "Tests the world scheduling");
Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers)
Plugin:AddWebTab("StressTest", HandleRequest_StressTest)
@ -1643,3 +1619,85 @@ end
function HandleConsoleLoadChunk(a_Split)
-- Check params:
local numParams = #a_Split
if (numParams ~= 3) and (numParams ~= 4) then
return true, "Usage: " .. a_Split[1] .. " <ChunkX> <ChunkZ> [<WorldName>]"
end
-- Get the chunk coords:
local chunkX = tonumber(a_Split[2])
if (chunkX == nil) then
return true, "Not a number: '" .. a_Split[2] .. "'"
end
local chunkZ = tonumber(a_Split[3])
if (chunkZ == nil) then
return true, "Not a number: '" .. a_Split[3] .. "'"
end
-- Get the world:
local world
if (a_Split[4] == nil) then
world = cRoot:Get():GetDefaultWorld()
else
world = cRoot:Get():GetWorld(a_Split[4])
if (world == nil) then
return true, "There's no world named '" .. a_Split[4] .. "'."
end
end
-- Queue a ChunkStay for the chunk, log a message when the chunk is loaded:
world:ChunkStay({{chunkX, chunkZ}}, nil,
function()
LOG("Chunk [" .. chunkX .. ", " .. chunkZ .. "] is loaded")
end
)
return true
end
function HandleConsolePrepareChunk(a_Split)
-- Check params:
local numParams = #a_Split
if (numParams ~= 3) and (numParams ~= 4) then
return true, "Usage: " .. a_Split[1] .. " <ChunkX> <ChunkZ> [<WorldName>]"
end
-- Get the chunk coords:
local chunkX = tonumber(a_Split[2])
if (chunkX == nil) then
return true, "Not a number: '" .. a_Split[2] .. "'"
end
local chunkZ = tonumber(a_Split[3])
if (chunkZ == nil) then
return true, "Not a number: '" .. a_Split[3] .. "'"
end
-- Get the world:
local world
if (a_Split[4] == nil) then
world = cRoot:Get():GetDefaultWorld()
else
world = cRoot:Get():GetWorld(a_Split[4])
if (world == nil) then
return true, "There's no world named '" .. a_Split[4] .. "'."
end
end
-- Queue the chunk for preparing, log a message when prepared:
world:PrepareChunk(chunkX, chunkZ,
function(a_CBChunkX, a_CBChunkZ)
LOG("Chunk [" .. chunkX .. ", " .. chunkZ .. "] has been prepared")
end
)
return true
end

View File

@ -0,0 +1,223 @@
-- Info.lua
-- Implements the g_PluginInfo standard plugin description
g_PluginInfo =
{
Name = "Debuggers",
Version = "14",
Date = "2014-12-11",
Description = [[Contains code for testing and debugging the server. Should not be enabled on a production server!]],
Commands =
{
["/arr"] =
{
Permission = "debuggers",
Handler = HandleArrowCmd,
HelpString = "Creates an arrow going away from the player"
},
["/compo"] =
{
Permission = "debuggers",
Handler = HandleCompo,
HelpString = "Tests the cCompositeChat bindings"
},
["/cs"] =
{
Permission = "debuggers",
Handler = HandleChunkStay,
HelpString = "Tests the ChunkStay Lua integration for the specified chunk coords"
},
["/dash"] =
{
Permission = "debuggers",
Handler = HandleDashCmd,
HelpString = "Switches between fast and normal sprinting speed"
},
["/ench"] =
{
Permission = "debuggers",
Handler = HandleEnchCmd,
HelpString = "Provides an instant dummy enchantment window"
},
["/fast"] =
{
Permission = "debuggers",
Handler = HandleFastCmd,
HelpString = "Switches between fast and normal movement speed"
},
["/fb"] =
{
Permission = "debuggers",
Handler = HandleFireballCmd,
HelpString = "Creates a ghast fireball as if shot by the player"
},
["/ff"] =
{
Permission = "debuggers",
Handler = HandleFurnaceFuel,
HelpString = "Shows how long the currently held item would burn in a furnace"
},
["/fill"] =
{
Permission = "debuggers",
Handler = HandleFill,
HelpString = "Fills all block entities in current chunk with junk"
},
["/fl"] =
{
Permission = "debuggers",
Handler = HandleFoodLevelCmd,
HelpString = "Sets the food level to the given value"
},
["/fr"] =
{
Permission = "debuggers",
Handler = HandleFurnaceRecipe,
HelpString = "Shows the furnace recipe for the currently held item"
},
["/fs"] =
{
Permission = "debuggers",
Handler = HandleFoodStatsCmd,
HelpString = "Turns regular foodstats message on or off"
},
["/gc"] =
{
Permission = "debuggers",
Handler = HandleGCCmd,
HelpString = "Activates the Lua garbage collector"
},
["/hunger"] =
{
Permission = "debuggers",
Handler = HandleHungerCmd,
HelpString = "Lists the current hunger-related variables"
},
["/ke"] =
{
Permission = "debuggers",
Handler = HandleKillEntitiesCmd,
HelpString = "Kills all the loaded entities"
},
["/le"] =
{
Permission = "debuggers",
Handler = HandleListEntitiesCmd,
HelpString = "Shows a list of all the loaded entities"
},
["/nick"] =
{
Permission = "debuggers",
Handler = HandleNickCmd,
HelpString = "Gives you a custom name",
},
["/pickups"] =
{
Permission = "debuggers",
Handler = HandlePickups,
HelpString = "Spawns random pickups around you"
},
["/poison"] =
{
Permission = "debuggers",
Handler = HandlePoisonCmd,
HelpString = "Sets food-poisoning for 15 seconds"
},
["/poof"] =
{
Permission = "debuggers",
Handler = HandlePoof,
HelpString = "Nudges pickups close to you away from you"
},
["/rmitem"] =
{
Permission = "debuggers",
Handler = HandleRMItem,
HelpString = "Remove the specified item from the inventory."
},
["/sb"] =
{
Permission = "debuggers",
Handler = HandleSetBiome,
HelpString = "Sets the biome around you to the specified one"
},
["/sched"] =
{
Permission = "debuggers",
Handler = HandleSched,
HelpString = "Schedules a simple countdown using cWorld:ScheduleTask()"
},
["/spidey"] =
{
Permission = "debuggers",
Handler = HandleSpideyCmd,
HelpString = "Shoots a line of web blocks until it hits non-air"
},
["/starve"] =
{
Permission = "debuggers",
Handler = HandleStarveCmd,
HelpString = "Sets the food level to zero"
},
["/testwnd"] =
{
Permission = "debuggers",
Handler = HandleTestWndCmd,
HelpString = "Opens up a window using plugin API"
},
["/wesel"] =
{
Permission = "debuggers",
Handler = HandleWESel,
HelpString = "Expands the current WE selection by 1 block in X/Z"
},
["/wool"] =
{
Permission = "debuggers",
Handler = HandleWoolCmd,
HelpString = "Sets all your armor to blue wool"
},
["/xpa"] =
{
Permission = "debuggers",
Handler = HandleAddExperience,
HelpString = "Adds 200 experience to the player"
},
["/xpr"] =
{
Permission = "debuggers",
Handler = HandleRemoveXp,
HelpString = "Remove all xp"
},
}, -- Commands
ConsoleCommands =
{
["sched"] =
{
Handler = HandleConsoleSchedule,
HelpString = "Tests the world scheduling",
},
["loadchunk"] =
{
Handler = HandleConsoleLoadChunk,
HelpString = "Loads the specified chunk into memory",
},
["preparechunk"] =
{
Handler = HandleConsolePrepareChunk,
HelpString = "Prepares the specified chunk completely (load / gen / light)",
}
}, -- ConsoleCommands
} -- g_PluginInfo

View File

@ -444,7 +444,18 @@ local function BuildPermissions(a_PluginInfo)
Permissions[info.Permission] = Permission
-- Add the command to the list of commands using this permission:
Permission.CommandsAffected = Permission.CommandsAffected or {}
table.insert(Permission.CommandsAffected, CommandString)
-- First, make sure that we don't already have this command in the list,
-- it may have already been present in a_PluginInfo
local NewCommand = true
for _, existCmd in ipairs(Permission.CommandsAffected) do
if CommandString == existCmd then
NewCommand = false
break
end
end
if NewCommand then
table.insert(Permission.CommandsAffected, CommandString)
end
end
-- Process the command param combinations for permissions:

View File

@ -64,18 +64,12 @@ macro(set_flags)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND (NOT GCC_VERSION VERSION_GREATER 4.6))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++0x")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
#on os x clang adds pthread for us but we need to add it for gcc
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_flags_cxx("-stdlib=libc++")
@ -94,18 +88,11 @@ macro(set_flags)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND (NOT GCC_VERSION VERSION_GREATER 4.6))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++0x")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
# We use a signed char (fixes #640 on RasPi)
add_flags_cxx("-fsigned-char")

View File

@ -53,7 +53,6 @@ set(SHARED_OSS_SRC
../../src/OSSupport/File.cpp
../../src/OSSupport/GZipFile.cpp
../../src/OSSupport/IsThread.cpp
../../src/OSSupport/Timer.cpp
)
set(SHARED_OSS_HDR
@ -62,7 +61,6 @@ set(SHARED_OSS_HDR
../../src/OSSupport/File.h
../../src/OSSupport/GZipFile.h
../../src/OSSupport/IsThread.h
../../src/OSSupport/Timer.h
)
flatten_files(SHARED_SRC)

View File

@ -56,14 +56,13 @@ set(SHARED_OSS_SRC
../../src/OSSupport/File.cpp
../../src/OSSupport/IsThread.cpp
../../src/OSSupport/StackTrace.cpp
../../src/OSSupport/Timer.cpp
)
set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h
../../src/OSSupport/File.h
../../src/OSSupport/IsThread.h
../../src/OSSupport/StackTrace.h
../../src/OSSupport/Timer.h
)
if(WIN32)

View File

@ -58,14 +58,12 @@ set(SHARED_OSS_SRC
../../src/OSSupport/File.cpp
../../src/OSSupport/IsThread.cpp
../../src/OSSupport/StackTrace.cpp
../../src/OSSupport/Timer.cpp
)
set(SHARED_OSS_HDR
../../src/OSSupport/CriticalSection.h
../../src/OSSupport/File.h
../../src/OSSupport/IsThread.h
../../src/OSSupport/StackTrace.h
../../src/OSSupport/Timer.h
)
if(WIN32)

View File

@ -189,7 +189,7 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) :
m_Server(a_Server),
m_ClientSocket(a_ClientSocket),
m_ServerSocket(-1),
m_BeginTick(m_Timer.GetNowTime()),
m_BeginTick(std::chrono::steady_clock::now()),
m_ClientState(csUnencrypted),
m_ServerState(csUnencrypted),
m_Nonce(0),
@ -436,7 +436,8 @@ bool cConnection::RelayFromClient(void)
double cConnection::GetRelativeTime(void)
{
return (double)(m_Timer.GetNowTime() - m_BeginTick) / 1000;
Int64 msec = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_BeginTick).count();
return static_cast<double>(msec) / 1000;
}

View File

@ -10,7 +10,6 @@
#pragma once
#include "ByteBuffer.h"
#include "OSSupport/Timer.h"
#include "PolarSSL++/AesCfb128Decryptor.h"
#include "PolarSSL++/AesCfb128Encryptor.h"
@ -37,8 +36,7 @@ class cConnection
SOCKET m_ClientSocket;
SOCKET m_ServerSocket;
cTimer m_Timer;
long long m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime())
std::chrono::steady_clock::time_point m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime())
enum eConnectionState
{

View File

@ -52,7 +52,9 @@ static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -78,7 +80,9 @@ static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -104,7 +108,9 @@ static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -130,7 +136,9 @@ static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -156,7 +164,9 @@ static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -182,7 +192,9 @@ static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -208,7 +220,9 @@ static int tolua_get_AllToLua_g_BlockFullyOccupiesVoxel(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err))
{
tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err);
}
}
#endif
BlockType = (int)tolua_tonumber(tolua_S, 2, 0);
@ -233,8 +247,8 @@ static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S)
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_iscppstring(tolua_S,1,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
!tolua_iscppstring(tolua_S, 1, 0, &tolua_err) ||
!tolua_isnoobj(tolua_S, 2, &tolua_err)
)
goto tolua_lerror;
else

View File

@ -1874,6 +1874,72 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
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;
}
static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
{
// Function signature: cPlayer:GetPermissions() -> {permissions-array}
@ -3401,6 +3467,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
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);

View File

@ -54,7 +54,7 @@ public:
virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0;
virtual bool OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0;
virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) = 0;
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0;
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 OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;

View File

@ -382,7 +382,7 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup)
bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe)
bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
cCSLock Lock(m_CriticalSection);
bool res = false;

View File

@ -78,7 +78,7 @@ public:
virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override;
virtual bool OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override;
virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) override;
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) override;
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;

View File

@ -448,7 +448,7 @@ bool cPluginManager::CallHookCollectingPickup(cPlayer & a_Player, cPickup & a_Pi
bool cPluginManager::CallHookCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe)
bool cPluginManager::CallHookCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe)
{
FIND_HOOK(HOOK_CRAFTING_NO_RECIPE);
VERIFY_HOOK;
@ -1459,11 +1459,16 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin) const
cPlugin * cPluginManager::GetPlugin(const AString & a_Plugin) const
{
for (PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
{
if (itr->second == nullptr) continue;
if (itr->second == nullptr)
{
// The plugin is currently unloaded
continue;
}
if (itr->second->GetName().compare(a_Plugin) == 0)
{
return itr->second;

View File

@ -187,7 +187,7 @@ public:
bool CallHookChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup);
bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
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 CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd

View File

@ -52,7 +52,10 @@ public:
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 180 + (180 / 4); // So its not aligned with axis
if (a_Rotation > 360) a_Rotation -= 360;
if (a_Rotation > 360)
{
a_Rotation -= 360;
}
a_Rotation = (a_Rotation / 360) * 4;

View File

@ -145,7 +145,10 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
// in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time,
// so the function can only see either the hinge position or orientation, but not both, at any given time. The class itself
// needs extra datamembers.
if (a_Meta & 0x08) return a_Meta;
if (a_Meta & 0x08)
{
return a_Meta;
}
// Holds open/closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C;
@ -173,7 +176,10 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta)
// so the function can only see either the hinge position or orientation, but not both, at any given time.The class itself
// needs extra datamembers.
if (a_Meta & 0x08) return a_Meta;
if (a_Meta & 0x08)
{
return a_Meta;
}
// Holds open/closed meta data. 0x0C == 1100.
NIBBLETYPE OtherMeta = a_Meta & 0x0C;

View File

@ -151,13 +151,21 @@ public:
Neighbors[6] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE));
Neighbors[7] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE));
if (IsUnstable(a_ChunkInterface, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST))
{
Neighbors[0] = true;
}
if (IsUnstable(a_ChunkInterface, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST))
{
Neighbors[1] = true;
}
if (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH))
{
Neighbors[2] = true;
}
if (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH))
{
Neighbors[3] = true;
}
for (int i = 0; i < 8; i++)
{
if (Neighbors[i])
@ -167,12 +175,30 @@ public:
}
if (RailsCnt == 1)
{
if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP;
else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM;
else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP;
else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP;
else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP;
if (Neighbors[7])
{
return E_META_RAIL_ASCEND_ZP;
}
else if (Neighbors[6])
{
return E_META_RAIL_ASCEND_ZM;
}
else if (Neighbors[5])
{
return E_META_RAIL_ASCEND_XM;
}
else if (Neighbors[4])
{
return E_META_RAIL_ASCEND_XP;
}
else if (Neighbors[0] || Neighbors[1])
{
return E_META_RAIL_XM_XP;
}
else if (Neighbors[2] || Neighbors[3])
{
return E_META_RAIL_ZM_ZP;
}
ASSERT(!"Weird neighbor count");
return Meta;
}
@ -185,16 +211,46 @@ public:
}
if (RailsCnt > 1)
{
if (Neighbors[3] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XP;
else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XM;
else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XP;
else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XM;
else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP;
else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM;
else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP;
else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP;
else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP;
if (Neighbors[3] && Neighbors[0] && CanThisRailCurve())
{
return E_META_RAIL_CURVED_ZP_XP;
}
else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve())
{
return E_META_RAIL_CURVED_ZP_XM;
}
else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve())
{
return E_META_RAIL_CURVED_ZM_XP;
}
else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve())
{
return E_META_RAIL_CURVED_ZM_XM;
}
else if (Neighbors[7] && Neighbors[2])
{
return E_META_RAIL_ASCEND_ZP;
}
else if (Neighbors[3] && Neighbors[6])
{
return E_META_RAIL_ASCEND_ZM;
}
else if (Neighbors[5] && Neighbors[0])
{
return E_META_RAIL_ASCEND_XM;
}
else if (Neighbors[4] && Neighbors[1])
{
return E_META_RAIL_ASCEND_XP;
}
else if (Neighbors[0] && Neighbors[1])
{
return E_META_RAIL_XM_XP;
}
else if (Neighbors[2] && Neighbors[3])
{
return E_META_RAIL_ZM_ZP;
}
if (CanThisRailCurve())
{

View File

@ -111,26 +111,36 @@ public:
#ifdef _DEBUG
/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously
class cSingleThreadAccessChecker
{
public:
cSingleThreadAccessChecker(unsigned long * a_ThreadID) :
m_ThreadID(a_ThreadID)
/** Simple RAII class that is used for checking that no two threads are using an object simultanously.
It requires the monitored object to provide the storage for a thread ID.
It uses that storage to check if the thread ID of consecutive calls is the same all the time. */
class cSingleThreadAccessChecker
{
ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID()));
}
~cSingleThreadAccessChecker()
{
*m_ThreadID = 0;
}
protected:
unsigned long * m_ThreadID;
} ;
public:
cSingleThreadAccessChecker(std::thread::id * a_ThreadID) :
m_ThreadID(a_ThreadID)
{
ASSERT(
(*a_ThreadID == std::this_thread::get_id()) || // Either the object is used by current thread...
(*a_ThreadID == std::thread::id()) // ... or by no thread at all
);
#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID))
// Mark as being used by this thread:
*m_ThreadID = std::this_thread::get_id();
}
~cSingleThreadAccessChecker()
{
// Mark as not being used by any thread:
*m_ThreadID = std::thread::id();
}
protected:
/** Points to the storage used for ID of the thread using the object. */
std::thread::id * m_ThreadID;
};
#define CHECK_THREAD cSingleThreadAccessChecker Checker(&m_ThreadID);
#else
#define CHECK_THREAD
@ -146,9 +156,6 @@ protected:
cByteBuffer::cByteBuffer(size_t a_BufferSize) :
m_Buffer(new char[a_BufferSize + 1]),
m_BufferSize(a_BufferSize + 1),
#ifdef _DEBUG
m_ThreadID(0),
#endif // _DEBUG
m_DataStart(0),
m_WritePos(0),
m_ReadPos(0)
@ -174,7 +181,7 @@ cByteBuffer::~cByteBuffer()
bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
// Store the current free space for a check after writing:
@ -221,7 +228,7 @@ bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
size_t cByteBuffer::GetFreeSpace(void) const
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
if (m_WritePos >= m_DataStart)
{
@ -243,7 +250,7 @@ size_t cByteBuffer::GetFreeSpace(void) const
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
size_t cByteBuffer::GetUsedSpace(void) const
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
ASSERT(m_BufferSize >= GetFreeSpace());
ASSERT((m_BufferSize - GetFreeSpace()) >= 1);
@ -257,7 +264,7 @@ size_t cByteBuffer::GetUsedSpace(void) const
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
size_t cByteBuffer::GetReadableSpace(void) const
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
if (m_ReadPos > m_WritePos)
{
@ -276,7 +283,7 @@ size_t cByteBuffer::GetReadableSpace(void) const
bool cByteBuffer::CanReadBytes(size_t a_Count) const
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
return (a_Count <= GetReadableSpace());
}
@ -287,7 +294,7 @@ bool cByteBuffer::CanReadBytes(size_t a_Count) const
bool cByteBuffer::CanWriteBytes(size_t a_Count) const
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
return (a_Count <= GetFreeSpace());
}
@ -298,7 +305,7 @@ bool cByteBuffer::CanWriteBytes(size_t a_Count) const
bool cByteBuffer::ReadChar(char & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(1);
ReadBuf(&a_Value, 1);
@ -311,7 +318,7 @@ bool cByteBuffer::ReadChar(char & a_Value)
bool cByteBuffer::ReadByte(unsigned char & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(1);
ReadBuf(&a_Value, 1);
@ -324,7 +331,7 @@ bool cByteBuffer::ReadByte(unsigned char & a_Value)
bool cByteBuffer::ReadBEShort(short & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(2);
ReadBuf(&a_Value, 2);
@ -338,7 +345,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value)
bool cByteBuffer::ReadBEInt(int & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
@ -352,7 +359,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value)
bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(8);
ReadBuf(&a_Value, 8);
@ -366,7 +373,7 @@ bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
bool cByteBuffer::ReadBEFloat(float & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
@ -380,7 +387,7 @@ bool cByteBuffer::ReadBEFloat(float & a_Value)
bool cByteBuffer::ReadBEDouble(double & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(8);
ReadBuf(&a_Value, 8);
@ -394,7 +401,7 @@ bool cByteBuffer::ReadBEDouble(double & a_Value)
bool cByteBuffer::ReadBool(bool & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(1);
char Value = 0;
@ -409,7 +416,7 @@ bool cByteBuffer::ReadBool(bool & a_Value)
bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
short Length;
if (!ReadBEShort(Length))
@ -430,7 +437,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
UInt32 Value = 0;
int Shift = 0;
@ -452,7 +459,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
UInt32 Size = 0;
if (!ReadVarInt(Size))
@ -472,7 +479,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
bool cByteBuffer::ReadLEInt(int & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
@ -491,6 +498,7 @@ bool cByteBuffer::ReadLEInt(int & a_Value)
bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
{
CHECK_THREAD
Int64 Value;
if (!ReadBEInt64(Value))
{
@ -515,7 +523,7 @@ bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
bool cByteBuffer::WriteChar(char a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(1);
return WriteBuf(&a_Value, 1);
@ -527,7 +535,7 @@ bool cByteBuffer::WriteChar(char a_Value)
bool cByteBuffer::WriteByte(unsigned char a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(1);
return WriteBuf(&a_Value, 1);
@ -539,7 +547,7 @@ bool cByteBuffer::WriteByte(unsigned char a_Value)
bool cByteBuffer::WriteBEShort(short a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(2);
u_short Converted = htons((u_short)a_Value);
@ -552,7 +560,7 @@ bool cByteBuffer::WriteBEShort(short a_Value)
bool cByteBuffer::WriteBEUShort(unsigned short a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(2);
u_short Converted = htons((u_short)a_Value);
@ -565,7 +573,7 @@ bool cByteBuffer::WriteBEUShort(unsigned short a_Value)
bool cByteBuffer::WriteBEInt(int a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(4);
UInt32 Converted = HostToNetwork4(&a_Value);
@ -578,7 +586,7 @@ bool cByteBuffer::WriteBEInt(int a_Value)
bool cByteBuffer::WriteBEInt64(Int64 a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(8);
UInt64 Converted = HostToNetwork8(&a_Value);
@ -591,7 +599,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value)
bool cByteBuffer::WriteBEFloat(float a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(4);
UInt32 Converted = HostToNetwork4(&a_Value);
@ -604,7 +612,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value)
bool cByteBuffer::WriteBEDouble(double a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(8);
UInt64 Converted = HostToNetwork8(&a_Value);
@ -618,7 +626,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value)
bool cByteBuffer::WriteBool(bool a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
return WriteChar(a_Value ? 1 : 0);
}
@ -629,7 +637,7 @@ bool cByteBuffer::WriteBool(bool a_Value)
bool cByteBuffer::WriteVarInt(UInt32 a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
// A 32-bit integer can be encoded by at most 5 bytes:
@ -650,7 +658,7 @@ bool cByteBuffer::WriteVarInt(UInt32 a_Value)
bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early.
bool res = WriteVarInt((UInt32)(a_Value.size()));
@ -667,7 +675,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
bool cByteBuffer::WriteLEInt(int a_Value)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
#ifdef IS_LITTLE_ENDIAN
return WriteBuf((const char *)&a_Value, 4);
@ -683,6 +691,7 @@ bool cByteBuffer::WriteLEInt(int a_Value)
bool cByteBuffer::WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ)
{
CHECK_THREAD
return WriteBEInt64(((Int64)a_BlockX & 0x3FFFFFF) << 38 | ((Int64)a_BlockY & 0xFFF) << 26 | ((Int64)a_BlockZ & 0x3FFFFFF));
}
@ -692,7 +701,7 @@ bool cByteBuffer::WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ)
bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(a_Count);
char * Dst = (char *)a_Buffer; // So that we can do byte math
@ -725,7 +734,7 @@ bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
PUTBYTES(a_Count);
char * Src = (char *)a_Buffer; // So that we can do byte math
@ -755,7 +764,7 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
NEEDBYTES(a_Count);
a_String.clear();
@ -790,7 +799,7 @@ bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars)
{
// Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
CHECK_THREAD;
CHECK_THREAD
CheckValid();
AString RawData;
if (!ReadString(RawData, a_NumChars * 2))
@ -807,7 +816,7 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars)
bool cByteBuffer::SkipRead(size_t a_Count)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
if (!CanReadBytes(a_Count))
{
@ -823,7 +832,7 @@ bool cByteBuffer::SkipRead(size_t a_Count)
void cByteBuffer::ReadAll(AString & a_Data)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
ReadString(a_Data, GetReadableSpace());
}
@ -834,6 +843,7 @@ void cByteBuffer::ReadAll(AString & a_Data)
bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
{
CHECK_THREAD
if (!a_Dst.CanWriteBytes(a_NumBytes) || !CanReadBytes(a_NumBytes))
{
// There's not enough source bytes or space in the dest BB
@ -858,7 +868,7 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
void cByteBuffer::CommitRead(void)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
m_DataStart = m_ReadPos;
}
@ -869,7 +879,7 @@ void cByteBuffer::CommitRead(void)
void cByteBuffer::ResetRead(void)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
m_ReadPos = m_DataStart;
}
@ -882,7 +892,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
{
// Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed)
// Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
CHECK_THREAD;
CHECK_THREAD
CheckValid();
size_t DataStart = m_DataStart;
if (m_ReadPos < m_DataStart)
@ -902,7 +912,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
void cByteBuffer::AdvanceReadPos(size_t a_Count)
{
CHECK_THREAD;
CHECK_THREAD
CheckValid();
m_ReadPos += a_Count;
if (m_ReadPos >= m_BufferSize)

View File

@ -130,13 +130,15 @@ protected:
char * m_Buffer;
size_t m_BufferSize; // Total size of the ringbuffer
#ifdef _DEBUG
volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
#endif // _DEBUG
size_t m_DataStart; // Where the data starts in the ringbuffer
size_t m_WritePos; // Where the data ends in the ringbuffer
size_t m_ReadPos; // Where the next read will start in the ringbuffer
#ifdef _DEBUG
/** The ID of the thread currently accessing the object.
Used for checking that only one thread accesses the object at a time, via cSingleThreadAccessChecker. */
mutable std::thread::id m_ThreadID;
#endif
/** Advances the m_ReadPos by a_Count bytes */
void AdvanceReadPos(size_t a_Count);

View File

@ -113,7 +113,6 @@ SET (HDRS
Map.h
MapManager.h
Matrix4.h
MersenneTwister.h
MobCensus.h
MobFamilyCollecter.h
MobProximityCounter.h

View File

@ -92,6 +92,25 @@ end
local g_ViolationPatterns =
{
-- Parenthesis around comparisons:
{"==[^)]+&&", "Add parenthesis around comparison"},
{"&&[^(]+==", "Add parenthesis around comparison"},
{"==[^)]+||", "Add parenthesis around comparison"},
{"||[^(]+==", "Add parenthesis around comparison"},
{"!=[^)]+&&", "Add parenthesis around comparison"},
{"&&[^(]+!=", "Add parenthesis around comparison"},
{"!=[^)]+||", "Add parenthesis around comparison"},
{"||[^(]+!=", "Add parenthesis around comparison"},
{"<[^)T][^)]*&&", "Add parenthesis around comparison"}, -- Must take special care of templates: "template <T> fn(Args && ...)"
{"&&[^(]+<", "Add parenthesis around comparison"},
{"<[^)T][^)]*||", "Add parenthesis around comparison"}, -- Must take special care of templates: "template <T> fn(Args && ...)"
{"||[^(]+<", "Add parenthesis around comparison"},
-- Cannot check ">" because of "obj->m_Flag &&". Check at least ">=":
{">=[^)]+&&", "Add parenthesis around comparison"},
{"&&[^(]+>=", "Add parenthesis around comparison"},
{">=[^)]+||", "Add parenthesis around comparison"},
{"||[^(]+>=", "Add parenthesis around comparison"},
-- Check against indenting using spaces:
{"^\t* +", "Indenting with a space"},
@ -116,11 +135,11 @@ local g_ViolationPatterns =
-- Space after keywords:
{"[^_]if%(", "Needs a space after \"if\""},
{"for%(", "Needs a space after \"for\""},
{"while%(", "Needs a space after \"while\""},
{"switch%(", "Needs a space after \"switch\""},
{"catch%(", "Needs a space after \"catch\""},
{"template<", "Needs a space after \"template\""},
{"%sfor%(", "Needs a space after \"for\""},
{"%swhile%(", "Needs a space after \"while\""},
{"%sswitch%(", "Needs a space after \"switch\""},
{"%scatch%(", "Needs a space after \"catch\""},
{"%stemplate<", "Needs a space after \"template\""},
-- No space after keyword's parenthesis:
{"[^%a#]if %( ", "Remove the space after \"(\""},
@ -162,6 +181,7 @@ local function ProcessFile(a_FileName)
-- Ref.: http://stackoverflow.com/questions/10416869/iterate-over-possibly-empty-lines-in-a-way-that-matches-the-expectations-of-exis
local lineCounter = 1
local lastIndentLevel = 0
local isLastLineControl = false
all:gsub("\r\n", "\n") -- normalize CRLF into LF-only
string.gsub(all .. "\n", "[^\n]*\n", -- Iterate over each line, while preserving empty lines
function(a_Line)
@ -198,6 +218,24 @@ local function ProcessFile(a_FileName)
end
lastIndentLevel = indentLevel
end
-- Check that control statements have braces on separate lines after them:
-- Note that if statements can be broken into multiple lines, in which case this test is not taken
if (isLastLineControl) then
if not(a_Line:find("^%s*{") or a_Line:find("^%s*#")) then
-- Not followed by a brace, not followed by a preprocessor
ReportViolation(a_FileName, lineCounter - 1, 1, 1, "Control statement needs a brace on separate line")
end
end
local lineWithSpace = " " .. a_Line
isLastLineControl =
lineWithSpace:find("^%s+if %b()") or
lineWithSpace:find("^%s+else if %b()") or
lineWithSpace:find("^%s+for %b()") or
lineWithSpace:find("^%s+switch %b()") or
lineWithSpace:find("^%s+else\n") or
lineWithSpace:find("^%s+else //") or
lineWithSpace:find("^%s+do %b()")
lineCounter = lineCounter + 1
end
@ -227,6 +265,9 @@ 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
ProcessItem(fnam)

View File

@ -28,7 +28,6 @@
#include "Item.h"
#include "Noise/Noise.h"
#include "Root.h"
#include "MersenneTwister.h"
#include "Entities/Player.h"
#include "BlockArea.h"
#include "Bindings/PluginManager.h"

View File

@ -410,7 +410,7 @@ typedef std::list<cChunkCoordsWithBool> cChunkCoordsWithBoolList;
/// Interface class used as a callback for operations that involve chunk coords
/** Interface class used as a callback for operations that involve chunk coords */
class cChunkCoordCallback
{
public:
@ -424,6 +424,27 @@ public:
/** Provides storage for a set of chunk coords together with a callback.
Used for chunk queues that notify about processed items. */
class cChunkCoordsWithCallback
{
public:
cChunkCoordsWithCallback(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback):
m_ChunkX(a_ChunkX),
m_ChunkZ(a_ChunkZ),
m_Callback(a_Callback)
{
}
int m_ChunkX;
int m_ChunkZ;
cChunkCoordCallback * m_Callback;
};
/** Generic template that can store any kind of data together with a triplet of 3 coords*/
template <typename X> class cCoordWithData
{

View File

@ -2349,6 +2349,103 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ)
void cChunkMap::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
// If the chunk is not prepared, queue it in the lighting thread, that will do all the needed processing:
if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid())
{
m_World->GetLightingThread().QueueChunk(a_ChunkX, a_ChunkZ, a_Callback);
return;
}
// The chunk is present and lit, just call the callback:
if (a_Callback != nullptr)
{
a_Callback->Call(a_ChunkX, a_ChunkZ);
}
}
bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk == nullptr)
{
// Generic error while getting the chunk - out of memory?
return false;
}
// Try loading the chunk:
if ((Chunk == nullptr) || (!Chunk->IsValid()))
{
class cPrepareLoadCallback: public cChunkCoordCallback
{
public:
cPrepareLoadCallback(cWorld & a_World, cChunkMap & a_ChunkMap, cChunkCoordCallback * a_Callback):
m_World(a_World),
m_ChunkMap(a_ChunkMap),
m_Callback(a_Callback)
{
}
// cChunkCoordCallback override:
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:
cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ);
if (CBChunk == nullptr)
{
// An error occurred, but we promised to call the callback, so call it even when there's no real chunk data:
if (m_Callback != nullptr)
{
m_Callback->Call(a_CBChunkX, a_CBChunkZ);
}
return;
}
// If the chunk is not valid, queue it in the generator:
if (!CBChunk->IsValid())
{
m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback);
return;
}
// The chunk was loaded, call the callback:
if (m_Callback != nullptr)
{
m_Callback->Call(a_CBChunkX, a_CBChunkZ);
}
}
protected:
cWorld & m_World;
cChunkMap & m_ChunkMap;
cChunkCoordCallback * m_Callback;
};
m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, new cPrepareLoadCallback(*m_World, *this, a_Callback));
return true;
}
// The chunk is valid, just call the callback:
if (a_Callback != nullptr)
{
a_Callback->Call(a_ChunkX, a_ChunkZ);
}
return true;
}
void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);

View File

@ -280,6 +280,20 @@ public:
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkZ);
/** Queues the chunk for preparing - making sure that it's generated and lit.
The specified chunk is queued to be loaded or generated, and lit if needed.
The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called.
It is legal to call without the callback. */
void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible
/** Queues the chunk for generating.
First attempts to load the chunk from the storage. If that fails, queues the chunk for generating.
The specified callback is called after the chunk has been loaded / generated.
It is legal to call without the callback.
Returns true if successful, false if not (possibly an out-of-memory error).
If the return value is true, the callback was / will be called. */
bool GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible
/** Marks the chunk as failed-to-load */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);

View File

@ -16,7 +16,6 @@
#include "Mobs/Monster.h"
#include "ChatColor.h"
#include "OSSupport/Socket.h"
#include "OSSupport/Timer.h"
#include "Items/ItemHandler.h"
#include "Blocks/BlockHandler.h"
#include "Blocks/BlockSlab.h"
@ -25,8 +24,6 @@
#include "Root.h"
#include "Protocol/Authenticator.h"
#include "MersenneTwister.h"
#include "Protocol/ProtocolRecognizer.h"
#include "CompositeChat.h"
#include "Items/ItemSword.h"
@ -41,18 +38,13 @@
/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
#define MAX_BLOCK_CHANGE_INTERACTIONS 20
/** The interval for sending pings to clients.
Vanilla sends one ping every 1 second. */
static const std::chrono::milliseconds PING_TIME_MS = std::chrono::milliseconds(1000);
#define RECI_RAND_MAX (1.f/RAND_MAX)
inline int fRadRand(MTRand & r1, int a_BlockCoord)
{
return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8);
}
int cClientHandle::s_ClientCount = 0;
@ -76,8 +68,6 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_TimeSinceLastPacket(0),
m_Ping(1000),
m_PingID(1),
m_PingStartTime(0),
m_LastPingTime(1000),
m_BlockDigAnimStage(-1),
m_BlockDigAnimSpeed(0),
m_BlockDigAnimX(0),
@ -101,9 +91,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount;
cTimer t1;
m_LastPingTime = t1.GetNowTime();
m_PingStartTime = std::chrono::steady_clock::now();
LOGD("New ClientHandle created at %p", this);
}
@ -199,9 +187,13 @@ void cClientHandle::GenerateOfflineUUID(void)
AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
{
if (ShouldAppendChatPrefixes)
{
return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str());
}
else
{
return Printf("%s", m_Color1.c_str());
}
}
@ -397,8 +389,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
cTimer t1;
m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds
m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
@ -1779,8 +1770,7 @@ void cClientHandle::HandleKeepAlive(int a_KeepAliveID)
{
if (a_KeepAliveID == m_PingID)
{
cTimer t1;
m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2);
m_Ping = std::chrono::steady_clock::now() - m_PingStartTime;
}
}
@ -1788,6 +1778,43 @@ void cClientHandle::HandleKeepAlive(int a_KeepAliveID)
bool cClientHandle::CheckMultiLogin(const AString & a_Username)
{
// If the multilogin is allowed, skip this check entirely:
if ((cRoot::Get()->GetServer()->DoesAllowMultiLogin()))
{
return true;
}
// Check if the player is waiting to be transferred to the World.
if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username))
{
Kick("A player of the username is already logged in");
return false;
}
class cCallback :
public cPlayerListCallback
{
virtual bool Item(cPlayer * a_Player) override
{
return true;
}
} Callback;
// Check if the player is in any World.
if (cRoot::Get()->DoWithPlayer(a_Username, Callback))
{
Kick("A player of the username is already logged in");
return false;
}
return true;
}
bool cClientHandle::HandleHandshake(const AString & a_Username)
{
if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(*this, a_Username))
@ -1798,7 +1825,8 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
return false;
}
}
return true;
return CheckMultiLogin(a_Username);
}
@ -2005,13 +2033,11 @@ void cClientHandle::Tick(float a_Dt)
// Send a ping packet:
if (m_State == csPlaying)
{
cTimer t1;
if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now()))
{
m_PingID++;
m_PingStartTime = t1.GetNowTime();
m_PingStartTime = std::chrono::steady_clock::now();
m_Protocol->SendKeepAlive(m_PingID);
m_LastPingTime = m_PingStartTime;
}
}

View File

@ -215,7 +215,7 @@ public:
const AString & GetUsername(void) const;
void SetUsername( const AString & a_Username);
inline short GetPing(void) const { return m_Ping; }
inline short GetPing(void) const { return static_cast<short>(std::chrono::duration_cast<std::chrono::milliseconds>(m_Ping).count()); }
/** Sets the maximal view distance. */
void SetViewDistance(int a_ViewDistance);
@ -280,6 +280,10 @@ public:
void HandleEntityLeaveBed (int a_EntityID);
void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting);
/** Kicks the client if the same username is already logged in.
Returns false if the client has been kicked, true otherwise. */
bool CheckMultiLogin(const AString & a_Username);
/** Called when the protocol handshake has been received (for protocol versions that support it;
otherwise the first instant when a username is received).
Returns true if the player is to be let in, false if they were disconnected
@ -378,12 +382,15 @@ private:
/** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
float m_TimeSinceLastPacket;
short m_Ping;
int m_PingID;
long long m_PingStartTime;
long long m_LastPingTime;
static const unsigned short PING_TIME_MS = 1000; // Vanilla sends 1 per 20 ticks (1 second or every 1000 ms)
/** Duration of the last completed client ping. */
std::chrono::steady_clock::duration m_Ping;
/** ID of the last ping request sent to the client. */
int m_PingID;
/** Time of the last ping request sent to the client. */
std::chrono::steady_clock::time_point m_PingStartTime;
// Values required for block dig animation
int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging
int m_BlockDigAnimSpeed; // Current speed of the animation (units ???)

View File

@ -294,7 +294,7 @@ void cCraftingRecipes::GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingG
if (Recipe.get() == nullptr)
{
// Allow plugins to intercept a no-recipe-found situation:
cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, a_CraftingGrid, &a_Recipe);
cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, a_CraftingGrid, a_Recipe);
return;
}
for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr)

View File

@ -13,7 +13,7 @@
/// Number of milliseconds per cycle
/** Number of milliseconds per cycle */
const int CYCLE_MILLISECONDS = 100;
@ -87,7 +87,7 @@ void cDeadlockDetect::Execute(void)
} Checker(this);
cRoot::Get()->ForEachWorld(Checker);
cSleep::MilliSleep(CYCLE_MILLISECONDS);
std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS));
} // while (should run)
}
@ -119,7 +119,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
if (WorldAge.m_Age == a_Age)
{
WorldAge.m_NumCyclesSame += 1;
if (WorldAge.m_NumCyclesSame > (1000 * m_IntervalSec) / CYCLE_MILLISECONDS)
if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS)
{
DeadlockDetected();
}

View File

@ -28,7 +28,7 @@ class cDeadlockDetect :
public:
cDeadlockDetect(void);
/// Starts the detection. Hides cIsThread's Start, because we need some initialization
/** Starts the detection. Hides cIsThread's Start, because we need some initialization */
bool Start(int a_IntervalSec);
protected:

View File

@ -665,7 +665,10 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
// Filter out damage types that are not protected by armor:
if (!ArmorCoversAgainst(a_DamageType)) return 0;
if (!ArmorCoversAgainst(a_DamageType))
{
return 0;
}
// Add up all armor points:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
@ -1011,9 +1014,18 @@ void cEntity::HandlePhysics(float 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) NextSpeed.x = 0.f;
if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;
if (Tracer.HitNormal.x != 0.f)
{
NextSpeed.x = 0.f;
}
if (Tracer.HitNormal.y != 0.f)
{
NextSpeed.y = 0.f;
}
if (Tracer.HitNormal.z != 0.f)
{
NextSpeed.z = 0.f;
}
if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground
{
@ -1276,7 +1288,7 @@ bool cEntity::DetectPortal()
return false;
}
if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && m_PortalCooldownData.m_TicksDelayed != 80)
if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && (m_PortalCooldownData.m_TicksDelayed != 80))
{
// Delay teleportation for four seconds if the entity is a non-creative player
m_PortalCooldownData.m_TicksDelayed++;

View File

@ -142,8 +142,13 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if (!IsBlockRail(InsideType))
{
Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); // When an descending minecart hits a flat rail, it goes through the ground; check for this
if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards
// When a descending minecart hits a flat rail, it goes through the ground; check for this
Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta);
if (IsBlockRail(InsideType))
{
// Push cart upwards
AddPosY(1);
}
}
bool WasDetectorRail = false;
@ -218,7 +223,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
// Execute both the entity and block collision checks
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return;
if (EntCol || BlckCol)
{
return;
}
if (GetSpeedZ() != NO_SPEED) // Don't do anything if cart is stationary
{
@ -243,7 +251,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
SetSpeedZ(NO_SPEED);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return;
if (EntCol || BlckCol)
{
return;
}
if (GetSpeedX() != NO_SPEED)
{
@ -422,7 +433,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedX(0);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return;
if (EntCol || BlckCol)
{
return;
}
if (GetSpeedZ() != NO_SPEED)
{
@ -445,7 +459,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedZ(NO_SPEED);
bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
if (EntCol || BlckCol) return;
if (EntCol || BlckCol)
{
return;
}
if (GetSpeedX() != NO_SPEED)
{

View File

@ -11,7 +11,6 @@
#include "../BlockEntities/BlockEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
#include "../Root.h"
#include "../OSSupport/Timer.h"
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
@ -27,7 +26,7 @@
#define PLAYER_INVENTORY_SAVE_INTERVAL 6000
// 1000 = once per second
#define PLAYER_LIST_TIME_MS 1000
#define PLAYER_LIST_TIME_MS std::chrono::milliseconds(1000)
@ -91,9 +90,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
SetMaxHealth(MAX_HEALTH);
m_Health = MAX_HEALTH;
cTimer t1;
m_LastPlayerListTime = t1.GetNowTime();
m_LastPlayerListTime = std::chrono::steady_clock::now();
m_PlayerName = a_PlayerName;
cWorld * World = nullptr;
@ -120,6 +117,11 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
{
m_CanFly = true;
}
if (World->IsGameModeSpectator()) // Otherwise Player will fall out of the world on join
{
m_CanFly = true;
m_IsFlying = true;
}
}
cRoot::Get()->GetServer()->PlayerCreated(this);
@ -263,11 +265,10 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
m_Inventory.UpdateItems();
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
cTimer t1;
if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= t1.GetNowTime())
if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now())
{
m_World->BroadcastPlayerListUpdatePing(*this);
m_LastPlayerListTime = t1.GetNowTime();
m_LastPlayerListTime = std::chrono::steady_clock::now();
}
if (IsFlying())
@ -358,7 +359,7 @@ float cPlayer::GetXpPercentage()
bool cPlayer::SetCurrentExperience(short int a_CurrentXp)
{
if (!(a_CurrentXp >= 0) || (a_CurrentXp > (SHRT_MAX - m_LifetimeTotalXp)))
if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<short>().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
@ -378,18 +379,17 @@ bool cPlayer::SetCurrentExperience(short int a_CurrentXp)
short cPlayer::DeltaExperience(short a_Xp_delta)
{
if (a_Xp_delta > (SHRT_MAX - m_CurrentXp))
if (a_Xp_delta > (std::numeric_limits<short>().max() - m_CurrentXp))
{
// Value was bad, abort and report
LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.",
a_Xp_delta);
LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", a_Xp_delta);
return -1; // Should we instead just return the current Xp?
}
m_CurrentXp += a_Xp_delta;
// Make sure they didn't subtract too much
m_CurrentXp = std::max<short int>(m_CurrentXp, 0);
m_CurrentXp = std::max<short>(m_CurrentXp, 0);
// Update total for score calculation
if (a_Xp_delta > 0)
@ -397,8 +397,7 @@ short cPlayer::DeltaExperience(short 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;
@ -1074,7 +1073,7 @@ bool cPlayer::IsGameModeAdventure(void) const
bool cPlayer::IsGameModeSpectator(void) const
{
return (m_GameMode == gmSpectator) || // Either the player is explicitly in Spectator
((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Adventure
((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Spectator
}
@ -1893,8 +1892,8 @@ void cPlayer::UseEquippedItem(int a_Amount)
void cPlayer::TickBurning(cChunk & a_Chunk)
{
// Don't burn in creative and stop burning in creative if necessary
if (!IsGameModeCreative())
// Don't burn in creative or spectator and stop burning in creative if necessary
if (!IsGameModeCreative() && !IsGameModeSpectator())
{
super::TickBurning(a_Chunk);
}
@ -1913,9 +1912,9 @@ void cPlayer::HandleFood(void)
{
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
if (IsGameModeCreative())
if (IsGameModeCreative() || IsGameModeSpectator())
{
// Hunger is disabled for Creative
// Hunger is disabled for Creative and Spectator
return;
}
@ -2080,7 +2079,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
void cPlayer::ApplyFoodExhaustionFromMovement()
{
if (IsGameModeCreative())
if (IsGameModeCreative() || IsGameModeSpectator())
{
return;
}

View File

@ -516,7 +516,7 @@ protected:
/** The item being dragged by the cursor while in a UI window */
cItem m_DraggingItem;
long long m_LastPlayerListTime;
std::chrono::steady_clock::time_point m_LastPlayerListTime;
cClientHandle * m_ClientHandle;

View File

@ -4,13 +4,15 @@
// Implements the cFastRandom class representing a fast random number generator
#include "Globals.h"
#include <time.h>
#include "FastRandom.h"
////////////////////////////////////////////////////////////////////////////////
// cFastRandom:
#if 0 && defined(_DEBUG)
// Self-test
// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs,
@ -83,16 +85,8 @@ public:
int cFastRandom::m_SeedCounter = 0;
cFastRandom::cFastRandom(void) :
m_Seed(m_SeedCounter++),
m_Counter(0)
m_LinearRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
{
}
@ -102,82 +96,96 @@ cFastRandom::cFastRandom(void) :
int cFastRandom::NextInt(int a_Range)
{
ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
ASSERT(a_Range > 0);
// Make the m_Counter operations as minimal as possible, to emulate atomicity
int Counter = m_Counter++;
// Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function:
int n = a_Range + Counter * 57 + m_Seed * 57 * 57;
n = (n << 13) ^ n;
n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
return ((n / 11) % a_Range);
std::uniform_int_distribution<> distribution(0, a_Range - 1);
return distribution(m_LinearRand);
}
int cFastRandom::NextInt(int a_Range, int a_Salt)
{
ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
ASSERT(a_Range > 0);
// Make the m_Counter operations as minimal as possible, to emulate atomicity
int Counter = m_Counter++;
// Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
int n = a_Range + Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
n = (n << 13) ^ n;
n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
return ((n / 11) % a_Range);
m_LinearRand.seed(a_Salt);
std::uniform_int_distribution<> distribution(0, a_Range - 1);
return distribution(m_LinearRand);
}
float cFastRandom::NextFloat(float a_Range)
{
// Make the m_Counter operations as minimal as possible, to emulate atomicity
int Counter = m_Counter++;
// Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
int n = (int)a_Range + Counter * 57 + m_Seed * 57 * 57;
n = (n << 13) ^ n;
n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
// Convert the integer into float with the specified range:
return (((float)n / (float)0x7fffffff) * a_Range);
std::uniform_real_distribution<float> distribution(0, a_Range);
return distribution(m_LinearRand);
}
float cFastRandom::NextFloat(float a_Range, int a_Salt)
{
// Make the m_Counter operations as minimal as possible, to emulate atomicity
int Counter = m_Counter++;
// Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
int n = (int)a_Range + Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
n = (n << 13) ^ n;
n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
// Convert the integer into float with the specified range:
return (((float)n / (float)0x7fffffff) * a_Range);
m_LinearRand.seed(a_Salt);
std::uniform_real_distribution<float> distribution(0, a_Range);
return distribution(m_LinearRand);
}
int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
{
cFastRandom Random;
return Random.NextInt(a_End - a_Begin + 1) + a_Begin;
std::uniform_int_distribution<> distribution(a_Begin, a_End);
return distribution(m_LinearRand);
}
////////////////////////////////////////////////////////////////////////////////
// MTRand:
MTRand::MTRand() :
m_MersenneRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
{
}
int MTRand::randInt(int a_Range)
{
std::uniform_int_distribution<> distribution(0, a_Range);
return distribution(m_MersenneRand);
}
int MTRand::randInt()
{
std::uniform_int_distribution<> distribution(0, std::numeric_limits<int>::max());
return distribution(m_MersenneRand);
}
double MTRand::rand(double a_Range)
{
std::uniform_real_distribution<> distribution(0, a_Range);
return distribution(m_MersenneRand);
}

View File

@ -22,6 +22,7 @@ salts, the values they get will be different.
#pragma once
#include <random>
@ -30,18 +31,19 @@ salts, the values they get will be different.
class cFastRandom
{
public:
cFastRandom(void);
/// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M
/** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */
int NextInt(int a_Range);
/// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness
/** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness */
int NextInt(int a_Range, int a_Salt);
/// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M
/** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */
float NextFloat(float a_Range);
/// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
/** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness */
float NextFloat(float a_Range, int a_Salt);
/** Returns a random float between 0 and 1. */
@ -49,14 +51,35 @@ public:
/** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End);
protected:
int m_Seed;
int m_Counter;
/// Counter that is used to initialize the seed, incremented for each object created
static int m_SeedCounter;
} ;
private:
std::minstd_rand m_LinearRand;
};
class MTRand
{
public:
MTRand(void);
/** Returns a random integer in the range [0 .. a_Range]. */
int randInt(int a_Range);
/** Returns a random integer in the range [0 .. MAX_INT]. */
int randInt(void);
/** Returns a random floating point number in the range [0 .. a_Range]. */
double rand(double a_Range);
private:
std::mt19937 m_MersenneRand;
};

View File

@ -692,8 +692,14 @@ static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise)
float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4;
oct1 = oct1 * oct1 * oct1;
if (oct1 < 0.f) oct1 = PI_2;
if (oct1 > PI_2) oct1 = PI_2;
if (oct1 < 0.f)
{
oct1 = PI_2;
}
if (oct1 > PI_2)
{
oct1 = PI_2;
}
return oct1;
}

View File

@ -6,7 +6,7 @@
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
#include "../MersenneTwister.h"
#include "FastRandom.h"
@ -110,29 +110,19 @@ void cChunkGenerator::Stop(void)
void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate)
void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback)
{
ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ));
{
cCSLock Lock(m_CS);
// Check if it is already in the queue:
for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
{
// Already in the queue, bail out
return;
}
} // for itr - m_Queue[]
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate));
m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback});
}
m_Event.Set();
@ -242,9 +232,9 @@ void cChunkGenerator::Execute(void)
continue;
}
cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue
cQueueItem item = m_Queue.front(); // Get next chunk from the queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue
m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
@ -258,22 +248,35 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
// Skip the chunk if it's already generated and regeneration is not forced:
if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
// Already generated, ignore request
LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
}
continue;
}
if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
// Skip the chunk if the generator is overloaded:
if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ))
{
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
}
continue;
}
LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
DoGenerate(coords.m_ChunkX, coords.m_ChunkZ);
// Generate the chunk:
LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
DoGenerate(item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
}
NumChunksGenerated++;
} // while (!bStop)
}

View File

@ -119,8 +119,12 @@ public:
bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
/// Queues the chunk for generation; removes duplicate requests
void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate);
/** Queues the chunk for generation
If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink.
a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating.
It is legal to set the callback to nullptr, no callback is called then.
If the generator becomes overloaded and skips this chunk, the callback is still called. */
void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
@ -131,22 +135,46 @@ public:
int GetSeed(void) const { return m_Seed; }
/// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome
/** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
/// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure.
/** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */
static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
private:
struct cQueueItem
{
/** The chunk coords */
int m_ChunkX, m_ChunkZ;
/** Force the regeneration of an already existing chunk */
bool m_ForceGenerate;
/** Callback to call after generating.*/
cChunkCoordCallback * m_Callback;
};
typedef std::list<cQueueItem> cGenQueue;
/** Seed used for the generator. */
int m_Seed;
cCriticalSection m_CS;
cChunkCoordsWithBoolList m_Queue;
cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
cEvent m_evtRemoved; ///< Set when an item is removed from the queue
/** CS protecting access to the queue. */
cCriticalSection m_CS;
/** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */
cGenQueue m_Queue;
/** Set when an item is added to the queue or the thread should terminate. */
cEvent m_Event;
/** Set when an item is removed from the queue. */
cEvent m_evtRemoved;
cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
/** The actual generator engine used to generate chunks. */
cGenerator * m_Generator;
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
@ -158,6 +186,7 @@ private:
// cIsThread override:
virtual void Execute(void) override;
/** Generates the specified chunk and sets it into the chunksink. */
void DoGenerate(int a_ChunkX, int a_ChunkZ);
};

View File

@ -290,17 +290,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc:
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.
{
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(BaseX + x)) / 8;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(BaseZ + z)) / 8;
NOISE_DATATYPE CompBlock = m_Noise1.CubicNoise3D(NoiseX, (float) (y + Segment) / 2, NoiseY);
if (CompBlock < -0.5)
{
Block = E_BLOCK_SOULSAND;
}
else
{
Block = E_BLOCK_NETHERRACK;
}
Block = E_BLOCK_NETHERRACK;
}
a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
}

View File

@ -294,7 +294,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
if (NoCaseCompare(*itr, "BottomLava") == 0)
if (NoCaseCompare(*itr, "Animals") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension)));
}
else if (NoCaseCompare(*itr, "BottomLava") == 0)
{
int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
@ -570,6 +574,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
GridSize, MaxOffset
)));
}
else if (NoCaseCompare(*itr, "SoulsandRims") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed)));
}
else if (NoCaseCompare(*itr, "Snow") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow));

View File

@ -7,6 +7,7 @@
#include "DungeonRoomsFinisher.h"
#include "../FastRandom.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/MobSpawnerEntity.h"
@ -57,6 +58,22 @@ public:
int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ);
// Choose what the mobspawner will spawn.
// 25% chance for a spider, 25% for a skeleton and 50% chance to get a zombie spawer.
int MobType = (a_Noise.IntNoise3DInt(a_OriginX, m_FloorHeight, a_OriginZ) / 7) % 100;
if (MobType <= 25)
{
m_MonsterType = mtSkeleton;
}
else if (MobType <= 50)
{
m_MonsterType = mtSpider;
}
else
{
m_MonsterType = mtZombie;
}
}
protected:
@ -76,6 +93,8 @@ protected:
/** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
Vector3i m_Chest2;
/** The monster type for the mobspawner entity. */
eMonsterType m_MonsterType;
/** Decodes the position index along the room walls into a proper 2D position for a chest.
@ -112,8 +131,8 @@ protected:
{
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width);
int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
for (int y = a_StartY; y < a_EndY; y++)
@ -246,7 +265,9 @@ protected:
)
{
a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0);
// TODO: Set the spawned mob
cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(CenterX, b, CenterZ));
ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
MobSpawner->SetEntity(m_MonsterType);
}
}
} ;

View File

@ -26,6 +26,8 @@
#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1"
#define DEF_ANIMAL_SPAWN_PERCENT 10
#define DEF_NO_ANIMALS 0
@ -65,7 +67,7 @@ void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
// Choose what block to use.
NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ);
if (BlockType < -0.7)
@ -195,10 +197,10 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
{
continue;
}
// Get the top block + 1. This is the place where the grass would finaly be placed:
int y = a_ChunkDesc.GetHeight(x, z) + 1;
if (y >= 255)
{
continue;
@ -281,7 +283,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
{
return false;
}
// All conditions met, place a sugarcane here:
a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
return true;
@ -294,7 +296,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_
void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
// Generate small foliage (1-block):
// TODO: Update heightmap with 1-block-tall foliage
for (int z = 0; z < cChunkDef::Width; z++)
{
@ -319,7 +321,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
// WEIRD, since we're using heightmap, so there should NOT be anything above it
continue;
}
const float xx = (float)BlockX;
float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f);
float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
@ -359,7 +361,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
}
break;
} // case E_BLOCK_GRASS
case E_BLOCK_SAND:
{
int y = Top + 1;
@ -370,7 +372,8 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
(a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) &&
(a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR)
(a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) &&
IsDesertVariant(a_ChunkDesc.GetBiome(x, z))
)
{
a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
@ -391,6 +394,72 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
bool cFinishGenSprinkleFoliage::IsDesertVariant(EMCSBiome a_Biome)
{
return
(
(a_Biome == biDesertHills) ||
(a_Biome == biDesert) ||
(a_Biome == biDesertM)
);
}
////////////////////////////////////////////////////////////////////////////////
// cFinishGenSoulsandRims
void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
{
int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
for (int x = 0; x < 16; x++)
{
int xx = ChunkX + x;
for (int z = 0; z < 16; z++)
{
int zz = ChunkZ + z;
// Place soulsand rims when netherrack gets thin
for (int y = 2; y < MaxHeight - 2; y++)
{
// The current block is air. Let's bail ut.
BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
if (Block == E_BLOCK_AIR)
{
continue;
}
if (
((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) &&
( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) ||
((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) &&
( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR))
)
{
continue;
}
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(xx)) / 32;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(zz)) / 32;
NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, (float) (y) / 4, NoiseY);
if (CompBlock < 0)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND);
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// cFinishGenSnow:
@ -716,7 +785,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
} // for y
} // for x
} // for z
// Turn fluid at the chunk edges into non-stationary fluid:
for (int y = 0; y < cChunkDef::Height; y++)
{
@ -808,12 +877,12 @@ void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
// Not in this chunk
return;
}
// Get the height at which to try:
int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
Height %= m_HeightDistribution.GetSum();
Height = m_HeightDistribution.MapValue(Height);
// Try adding the spring at the height, if unsuccessful, move lower:
for (int y = Height; y > 1; y--)
{
@ -851,7 +920,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
static const struct
{
int x, y, z;
@ -882,7 +951,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
{
return false;
}
// Has exactly one air neighbor, place a spring:
a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
return true;
@ -891,3 +960,236 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int
////////////////////////////////////////////////////////////////////////////////
// cFinishGenPassiveMobs:
cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) :
m_Noise(a_Seed)
{
AString SectionName = "Animals";
int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
switch (a_Dimension)
{
case dimOverworld:
{
DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
break;
}
case dimNether:
case dimEnd: // No nether or end animals (currently)
{
DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
break;
}
default:
{
ASSERT(!"Unhandled world dimension");
DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
break;
}
} // switch (dimension)
m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
{
LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
}
}
void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc)
{
int chunkX = a_ChunkDesc.GetChunkX();
int chunkZ = a_ChunkDesc.GetChunkZ();
int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
if (ChanceRnd > m_AnimalProbability)
{
return;
}
eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
if (RandomMob == mtInvalidType)
{
// No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well.
return;
}
// Try spawning a pack center 10 times, should get roughly the same probability
for (int Tries = 0; Tries < 10; Tries++)
{
int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
{
for (int i = 0; i < 3; i++)
{
int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
}
return;
} // if pack center spawn successful
} // for tries
}
bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
{
if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
{
return false;
}
BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);
// Check block below (opaque, grass, water), and above (air)
if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
{
return false;
}
if (
(AnimalToSpawn != mtSquid) &&
(BlockAtHead != E_BLOCK_AIR) &&
(BlockAtFeet != E_BLOCK_AIR) &&
(!cBlockInfo::IsTransparent(BlockUnderFeet))
)
{
return false;
}
if (
(BlockUnderFeet != E_BLOCK_GRASS) &&
((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
)
{
return false;
}
if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
{
return false;
}
double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5);
double AnimalY = a_RelY;
double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);
cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
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);
return true;
}
eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc)
{
std::set<eMonsterType> ListOfSpawnables;
int chunkX = a_ChunkDesc.GetChunkX();
int chunkZ = a_ChunkDesc.GetChunkZ();
int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;
// Check biomes first to get a list of animals
switch (a_ChunkDesc.GetBiome(x, z))
{
// No animals in deserts or non-overworld dimensions
case biNether:
case biEnd:
case biDesertHills:
case biDesert:
case biDesertM:
{
return mtInvalidType;
}
// Mooshroom only - no other mobs on mushroom islands
case biMushroomIsland:
case biMushroomShore:
{
return mtMooshroom;
}
// Add squid in ocean biomes
case biOcean:
case biFrozenOcean:
case biFrozenRiver:
case biRiver:
case biDeepOcean:
{
ListOfSpawnables.insert(mtSquid);
break;
}
// Add ocelots in jungle biomes
case biJungle:
case biJungleHills:
case biJungleEdge:
case biJungleM:
case biJungleEdgeM:
{
ListOfSpawnables.insert(mtOcelot);
break;
}
// Add horses in plains-like biomes
case biPlains:
case biSunflowerPlains:
case biSavanna:
case biSavannaPlateau:
case biSavannaM:
case biSavannaPlateauM:
{
ListOfSpawnables.insert(mtHorse);
break;
}
// Add wolves in forest and spruce forests
case biForest:
case biTaiga:
case biMegaTaiga:
case biColdTaiga:
case biColdTaigaM:
{
ListOfSpawnables.insert(mtWolf);
break;
}
// Nothing special about this biome
default:
{
break;
}
}
ListOfSpawnables.insert(mtChicken);
ListOfSpawnables.insert(mtCow);
ListOfSpawnables.insert(mtPig);
ListOfSpawnables.insert(mtSheep);
if (ListOfSpawnables.empty())
{
return mtInvalidType;
}
int RandMob = (m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size();
auto MobIter = ListOfSpawnables.begin();
std::advance(MobIter, RandMob);
return *MobIter;
}

View File

@ -18,6 +18,7 @@
#include "ComposableGenerator.h"
#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"
#include "../Mobs/Monster.h"
@ -117,19 +118,41 @@ protected:
class cFinishGenSoulsandRims :
public cFinishGen
{
public:
cFinishGenSoulsandRims(int a_Seed) :
m_Noise(a_Seed)
{
}
protected:
cNoise m_Noise;
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
class cFinishGenSprinkleFoliage :
public cFinishGen
{
public:
cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
protected:
cNoise m_Noise;
int m_Seed;
/// Tries to place sugarcane at the coords specified, returns true if successful
bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ);
// Returns true is the specified biome is a desert or its variant
static bool IsDesertVariant(EMCSBiome a_biome);
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@ -167,31 +190,31 @@ public:
{
m_IsAllowedBelow[idx] = false;
}
// Load the allowed blocks into m_IsAllowedBelow
for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr)
{
m_IsAllowedBelow[*itr] = true;
}
// Initialize all the biome types.
for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx)
{
m_IsBiomeAllowed[idx] = false;
}
// Load the allowed biomes into m_IsBiomeAllowed
for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr)
{
m_IsBiomeAllowed[*itr] = true;
}
}
protected:
cNoise m_Noise;
BLOCKTYPE m_BlockType;
int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns.
int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap);
// Returns true if the given biome is a biome that is allowed.
@ -206,7 +229,7 @@ protected:
return m_IsAllowedBelow[a_BlockBelow];
}
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@ -223,11 +246,11 @@ public:
m_Level(a_Level)
{
}
int GetLevel(void) const { return m_Level; }
protected:
int m_Level;
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
} ;
@ -241,7 +264,7 @@ class cFinishGenPreSimulator :
{
public:
cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava);
protected:
bool m_PreSimulateFallingBlocks;
@ -253,7 +276,7 @@ protected:
cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data
);
/** For each fluid block:
- if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top)
- all fluid on the chunk's edge is made flowing
@ -278,7 +301,7 @@ class cFinishGenFluidSprings :
{
public:
cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
protected:
cNoise m_Noise;
@ -289,10 +312,43 @@ protected:
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
/// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful
/** Tries to place a spring at the specified coords, checks neighbors. Returns true if successful. */
bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z);
} ;
/** This class populates generated chunks with packs of biome-dependant animals
Animals: cows, sheep, pigs, mooshrooms, squid, horses, wolves, ocelots */
class cFinishGenPassiveMobs :
public cFinishGen
{
public:
cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension);
protected:
/** The noise used as the source of randomness */
cNoise m_Noise;
/** Chance, [0..100], that an animal pack will be generated in a chunk */
int m_AnimalProbability;
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
/** Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and returns true */
bool TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn);
/** Picks a random animal from biome-dependant list for a random position in the chunk.
Returns the chosen mob type, or mtInvalid if no mob chosen. */
eMonsterType GetRandomMob(cChunkDesc & a_ChunkDesc);
} ;

View File

@ -20,6 +20,7 @@ in a depth-first processing. Each of the descendants will branch randomly, if no
#include "MineShafts.h"
#include "../Cuboid.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/MobSpawnerEntity.h"
@ -875,7 +876,9 @@ void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
)
{
a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
// TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ));
ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER));
MobSpawner->SetEntity(mtCaveSpider);
}
}

View File

@ -6,7 +6,6 @@
#include "Globals.h"
#include "Noise3DGenerator.h"
#include "../OSSupport/File.h"
#include "../OSSupport/Timer.h"
#include "../IniFile.h"
#include "../LinearInterpolation.h"
#include "../LinearUpscale.h"
@ -739,7 +738,7 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break;
case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
case biFrozenOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
@ -764,7 +763,7 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
case biNether: a_HeightAmp = 0.01f; a_MidPoint = 64; break;
case biOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break;
case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break;
case biRoofedForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biRoofedForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biSavanna: a_HeightAmp = 0.3f; a_MidPoint = 62; break;

View File

@ -184,9 +184,9 @@ template class SizeChecker<UInt16, 2>;
// OS-dependent stuff:
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x501 // We want to target WinXP and higher
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT _WIN32_WINNT_WS03 // We want to target Windows XP with Service Pack 2 & Windows Server 2003 with Service Pack 1 and higher
#include <Windows.h>
#include <winsock2.h>
@ -241,6 +241,7 @@ template class SizeChecker<UInt16, 2>;
// STL stuff:
#include <chrono>
#include <vector>
#include <list>
#include <deque>
@ -251,17 +252,17 @@ template class SizeChecker<UInt16, 2>;
#include <set>
#include <queue>
#include <limits>
#include <chrono>
#ifndef TEST_GLOBALS
// Common headers (part 1, without macros):
#include "StringUtils.h"
#include "OSSupport/Sleep.h"
#include "OSSupport/CriticalSection.h"
#include "OSSupport/Semaphore.h"
#include "OSSupport/Event.h"
#include "OSSupport/Thread.h"
#include "OSSupport/File.h"
#include "Logger.h"
#include "OSSupport/StackTrace.h"

View File

@ -82,7 +82,7 @@ protected:
bool m_IsValid;
/// The parser for the multipart data, if used
std::auto_ptr<cMultipartParser> m_MultipartParser;
std::unique_ptr<cMultipartParser> m_MultipartParser;
/// Name of the currently parsed part in multipart data
AString m_CurrentPartName;

View File

@ -507,7 +507,11 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize();
for (int i = 0; i < a_Size; i++)
{
if (m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage)
if (
(m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType) &&
(m_Slots[i + a_Offset].m_ItemCount < MaxStackSize) &&
(m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage)
)
{
int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount;
if (NumFree >= a_Item.m_ItemCount)
@ -533,7 +537,7 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
if (a_Mode > 0)
{
// If we got more left, find first empty slot
for (int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++)
for (int i = 0; (i < a_Size) && (a_Item.m_ItemCount > 0); i++)
{
if (m_Slots[i + a_Offset].m_ItemType == -1)
{

View File

@ -226,6 +226,8 @@ void cLightingThread::Execute(void)
} // CSLock(m_CS)
LightChunk(*Item);
Item->Disable();
delete Item;
}
}
@ -236,6 +238,16 @@ void cLightingThread::Execute(void)
void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
// If the chunk is already lit, skip it:
if (m_World->IsChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ))
{
if (a_Item.m_CallbackAfter != nullptr)
{
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ);
}
return;
}
cChunkDef::BlockNibbles BlockLight, SkyLight;
ReadChunks(a_Item.m_ChunkX, a_Item.m_ChunkZ);
@ -314,8 +326,6 @@ void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ);
}
a_Item.Disable();
delete &a_Item;
}

View File

@ -45,12 +45,12 @@ void cLogger::LogSimple(AString a_Message, eLogLevel a_LogLevel)
AString Line;
#ifdef _DEBUG
Printf(Line, "[%04lx|%02d:%02d:%02d] %s\n", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str());
Printf(Line, "[%04llx|%02d:%02d:%02d] %s\n", static_cast<UInt64>(std::hash<std::thread::id>()(std::this_thread::get_id())), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str());
#else
Printf(Line, "[%02d:%02d:%02d] %s\n", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str());
#endif
cCSLock Lock(m_CriticalSection);
for (size_t i = 0; i < m_LogListeners.size(); i++)
{

View File

@ -1,456 +0,0 @@
// MersenneTwister.h
// Mersenne Twister random number generator -- a C++ class MTRand
// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu
// The Mersenne Twister is an algorithm for generating random numbers. It
// was designed with consideration of the flaws in various other generators.
// The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
// are far greater. The generator is also fast; it avoids multiplication and
// division, and it benefits from caches and pipelines. For more information
// see the inventors' web page at
// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
// Reference
// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
// Copyright (C) 2000 - 2009, Richard J. Wagner
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The names of its contributors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#ifndef MERSENNETWISTER_H
#define MERSENNETWISTER_H
// Not thread safe (unless auto-initialization is avoided and each thread has
// its own MTRand object)
#include <iostream>
#include <climits>
#include <cstdio>
#include <ctime>
#include <cmath>
class MTRand {
// Data
public:
typedef UInt32 uint32; // unsigned integer type, at least 32 bits
enum { N = 624 }; // length of state vector
enum { SAVE = N + 1 }; // length of array for save()
protected:
enum { M = 397 }; // period parameter
uint32 state[N]; // internal state
uint32 *pNext; // next value to get from state
uint32 left; // number of values left before reload needed
// Methods
public:
MTRand( const uint32 oneSeed ); // initialize with a simple uint32
MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array
MTRand(); // auto-initialize with /dev/urandom or time() and clock()
MTRand( const MTRand& o ); // copy
// Do NOT use for CRYPTOGRAPHY without securely hashing several returned
// values together, otherwise the generator state can be learned after
// reading 624 consecutive values.
// Access to 32-bit random numbers
uint32 randInt(); // integer in [0,2^32-1]
uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32
double rand(); // real number in [0,1]
double rand( const double n ); // real number in [0,n]
double randExc(); // real number in [0,1)
double randExc( const double n ); // real number in [0,n)
double randDblExc(); // real number in (0,1)
double randDblExc( const double n ); // real number in (0,n)
double operator()(); // same as rand()
// Access to 53-bit random numbers (capacity of IEEE double precision)
double rand53(); // real number in [0,1)
// Access to nonuniform random number distributions
double randNorm( const double mean = 0.0, const double stddev = 1.0 );
// Re-seeding functions with same behavior as initializers
void seed( const uint32 oneSeed );
void seed( uint32 *const bigSeed, const uint32 seedLength = N );
void seed();
// Saving and loading generator state
void save( uint32* saveArray ) const; // to array of size SAVE
void load( uint32 *const loadArray ); // from such array
friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand );
friend std::istream& operator>>( std::istream& is, MTRand& mtrand );
MTRand& operator=( const MTRand& o );
protected:
void initialize( const uint32 oneSeed );
void reload();
uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; }
uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; }
uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; }
uint32 mixBits( const uint32 u, const uint32 v ) const
{ return hiBit(u) | loBits(v); }
uint32 magic( const uint32 u ) const
{ return loBit(u) ? 0x9908b0dfUL : 0x0UL; }
uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const
{ return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); }
static uint32 hash( time_t t, clock_t c );
};
// Functions are defined in order of usage to assist inlining
inline MTRand::uint32 MTRand::hash( time_t t, clock_t c )
{
// Get a uint32 from t and c
// Better than uint32(x) in case x is floating point in [0,1]
// Based on code by Lawrence Kirby (fred@genesis.demon.co.uk)
static uint32 differ = 0; // guarantee time-based seeds will change
uint32 h1 = 0;
unsigned char *p = (unsigned char *) &t;
for( size_t i = 0; i < sizeof(t); ++i )
{
h1 *= UCHAR_MAX + 2U;
h1 += p[i];
}
uint32 h2 = 0;
p = (unsigned char *) &c;
for( size_t j = 0; j < sizeof(c); ++j )
{
h2 *= UCHAR_MAX + 2U;
h2 += p[j];
}
return ( h1 + differ++ ) ^ h2;
}
inline void MTRand::initialize( const uint32 seed )
{
// Initialize generator state with seed
// See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
// In previous versions, most significant bits (MSBs) of the seed affect
// only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
uint32 *s = state;
uint32 *r = state;
uint32 i = 1;
*s++ = seed & 0xffffffffUL;
for( ; i < N; ++i )
{
*s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL;
r++;
}
}
inline void MTRand::reload()
{
// Generate N new values in state
// Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
static const int MmN = int(M) - int(N); // in case enums are unsigned
uint32 *p = state;
int i;
for( i = N - M; i--; ++p )
*p = twist( p[M], p[0], p[1] );
for( i = M; --i; ++p )
*p = twist( p[MmN], p[0], p[1] );
*p = twist( p[MmN], p[0], state[0] );
left = N, pNext = state;
}
inline void MTRand::seed( const uint32 oneSeed )
{
// Seed the generator with a simple uint32
initialize(oneSeed);
reload();
}
inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
{
// Seed the generator with an array of uint32's
// There are 2^19937-1 possible initial states. This function allows
// all of those to be accessed by providing at least 19937 bits (with a
// default seed length of N = 624 uint32's). Any bits above the lower 32
// in each element are discarded.
// Just call seed() if you want to get array from /dev/urandom
initialize(19650218UL);
uint32 i = 1;
uint32 j = 0;
uint32 k = ( static_cast<uint32>(N) > seedLength ? static_cast<uint32>(N) : seedLength );
for( ; k; --k )
{
state[i] =
state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL );
state[i] += ( bigSeed[j] & 0xffffffffUL ) + j;
state[i] &= 0xffffffffUL;
++i; ++j;
if( i >= N ) { state[0] = state[N-1]; i = 1; }
if( j >= seedLength ) j = 0;
}
for( k = N - 1; k; --k )
{
state[i] =
state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL );
state[i] -= i;
state[i] &= 0xffffffffUL;
++i;
if( i >= N ) { state[0] = state[N-1]; i = 1; }
}
state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array
reload();
}
inline void MTRand::seed()
{
// Seed the generator with an array from /dev/urandom if available
// Otherwise use a hash of time() and clock() values
// First try getting an array from /dev/urandom
/* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!\D5Ne
FILE* urandom = fopen( "/dev/urandom", "rb" );
if( urandom )
{
uint32 bigSeed[N];
register uint32 *s = bigSeed;
register int i = N;
register bool success = true;
while( success && i-- )
success = fread( s++, sizeof(uint32), 1, urandom );
fclose(urandom);
if( success ) { seed( bigSeed, N ); return; }
}
*/
// Was not successful, so use time() and clock() instead
seed( hash( time(NULL), clock() ) );
}
inline MTRand::MTRand( const uint32 oneSeed )
{ seed(oneSeed); }
inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength )
{ seed(bigSeed,seedLength); }
inline MTRand::MTRand()
{ seed(); }
inline MTRand::MTRand( const MTRand& o )
{
const uint32 *t = o.state;
uint32 *s = state;
int i = N;
for( ; i--; *s++ = *t++ ) {}
left = o.left;
pNext = &state[N-left];
}
inline MTRand::uint32 MTRand::randInt()
{
// Pull a 32-bit integer from the generator state
// Every other access function simply transforms the numbers extracted here
if( left == 0 ) reload();
--left;
uint32 s1;
s1 = *pNext++;
s1 ^= (s1 >> 11);
s1 ^= (s1 << 7) & 0x9d2c5680UL;
s1 ^= (s1 << 15) & 0xefc60000UL;
return ( s1 ^ (s1 >> 18) );
}
inline MTRand::uint32 MTRand::randInt( const uint32 n )
{
// Find which bits are used in n
// Optimized by Magnus Jonsson (magnus@smartelectronix.com)
uint32 used = n;
used |= used >> 1;
used |= used >> 2;
used |= used >> 4;
used |= used >> 8;
used |= used >> 16;
// Draw numbers until one is found in [0,n]
uint32 i;
do
i = randInt() & used; // toss unused bits to shorten search
while( i > n );
return i;
}
inline double MTRand::rand()
{ return double(randInt()) * (1.0/4294967295.0); }
inline double MTRand::rand( const double n )
{ return rand() * n; }
inline double MTRand::randExc()
{ return double(randInt()) * (1.0/4294967296.0); }
inline double MTRand::randExc( const double n )
{ return randExc() * n; }
inline double MTRand::randDblExc()
{ return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); }
inline double MTRand::randDblExc( const double n )
{ return randDblExc() * n; }
inline double MTRand::rand53()
{
uint32 a = randInt() >> 5, b = randInt() >> 6;
return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada
}
inline double MTRand::randNorm( const double mean, const double stddev )
{
// Return a real number from a normal (Gaussian) distribution with given
// mean and standard deviation by polar form of Box-Muller transformation
double x, y, r;
do
{
x = 2.0 * rand() - 1.0;
y = 2.0 * rand() - 1.0;
r = x * x + y * y;
}
while ( r >= 1.0 || r == 0.0 );
double s = sqrt( -2.0 * log(r) / r );
return mean + x * s * stddev;
}
inline double MTRand::operator()()
{
return rand();
}
inline void MTRand::save( uint32* saveArray ) const
{
const uint32 *s = state;
uint32 *sa = saveArray;
int i = N;
for( ; i--; *sa++ = *s++ ) {}
*sa = left;
}
inline void MTRand::load( uint32 *const loadArray )
{
uint32 *s = state;
uint32 *la = loadArray;
int i = N;
for( ; i--; *s++ = *la++ ) {}
left = *la;
pNext = &state[N-left];
}
inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand )
{
const MTRand::uint32 *s = mtrand.state;
int i = mtrand.N;
for( ; i--; os << *s++ << "\t" ) {}
return os << mtrand.left;
}
inline std::istream& operator>>( std::istream& is, MTRand& mtrand )
{
MTRand::uint32 *s = mtrand.state;
int i = mtrand.N;
for( ; i--; is >> *s++ ) {}
is >> mtrand.left;
mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left];
return is;
}
inline MTRand& MTRand::operator=( const MTRand& o )
{
if( this == &o ) return (*this);
const uint32 *t = o.state;
uint32 *s = state;
int i = N;
for( ; i--; *s++ = *t++ ) {}
left = o.left;
pNext = &state[N-left];
return (*this);
}
#endif // MERSENNETWISTER_H
// Change log:
//
// v0.1 - First release on 15 May 2000
// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
// - Translated from C to C++
// - Made completely ANSI compliant
// - Designed convenient interface for initialization, seeding, and
// obtaining numbers in default or user-defined ranges
// - Added automatic seeding from /dev/urandom or time() and clock()
// - Provided functions for saving and loading generator state
//
// v0.2 - Fixed bug which reloaded generator one step too late
//
// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew
//
// v0.4 - Removed trailing newline in saved generator format to be consistent
// with output format of built-in types
//
// v0.5 - Improved portability by replacing static const int's with enum's and
// clarifying return values in seed(); suggested by Eric Heimburg
// - Removed MAXINT constant; use 0xffffffffUL instead
//
// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits
// - Changed integer [0,n] generator to give better uniformity
//
// v0.7 - Fixed operator precedence ambiguity in reload()
// - Added access for real numbers in (0,1) and (0,n)
//
// v0.8 - Included time.h header to properly support time_t and clock_t
//
// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto
// - Allowed for seeding with arrays of any length
// - Added access for real numbers in [0,1) with 53-bit resolution
// - Added access for real numbers from normal (Gaussian) distributions
// - Increased overall speed by optimizing twist()
// - Doubled speed of integer [0,n] generation
// - Fixed out-of-range number generation on 64-bit machines
// - Improved portability by substituting literal constants for long enum's
// - Changed license from GNU LGPL to BSD
//
// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev"
// - Changed randNorm algorithm from basic to polar form for efficiency
// - Updated includes from deprecated <xxxx.h> to standard <cxxxx> forms
// - Cleaned declarations and definitions to please Intel compiler
// - Revised twist() operator to work on ones'-complement machines
// - Fixed reload() function to work when N and M are unsigned
// - Added copy constructor and copy operator from Salvador Espana

View File

@ -61,7 +61,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
{
std::set<eMonsterType> allowedMobs;
if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
if ((a_Biome == biMushroomIsland) || (a_Biome == biMushroomShore))
{
addIfAllowed(mtMooshroom, allowedMobs);
}
@ -84,7 +84,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
addIfAllowed(mtCreeper, allowedMobs);
addIfAllowed(mtSquid, allowedMobs);
if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
if ((a_Biome != biDesert) && (a_Biome != biBeach) && (a_Biome != biOcean))
{
addIfAllowed(mtSheep, allowedMobs);
addIfAllowed(mtPig, allowedMobs);
@ -93,11 +93,11 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
addIfAllowed(mtEnderman, allowedMobs);
addIfAllowed(mtSlime, allowedMobs); // MG TODO : much more complicated rule
if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills)
if ((a_Biome == biForest) || (a_Biome == biForestHills) || (a_Biome == biTaiga) || (a_Biome == biTaigaHills))
{
addIfAllowed(mtWolf, allowedMobs);
}
else if (a_Biome == biJungle || a_Biome == biJungleHills)
else if ((a_Biome == biJungle) || (a_Biome == biJungleHills))
{
addIfAllowed(mtOcelot, allowedMobs);
}
@ -285,6 +285,19 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
)
);
}
case mtMooshroom:
{
return (
(TargetBlock == E_BLOCK_AIR) &&
(BlockAbove == E_BLOCK_AIR) &&
(BlockBelow == E_BLOCK_MYCELIUM) &&
(
(a_Biome == biMushroomShore) ||
(a_Biome == biMushroomIsland)
)
);
}
default:
{

View File

@ -75,7 +75,9 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
}
if (m_Target == nullptr)
{
return;
}
cTracer LineOfSight(GetWorld());
Vector3d AttackDirection(m_Target->GetPosition() - GetPosition());

View File

@ -34,7 +34,7 @@ void cBlaze::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
if (m_Target != nullptr && m_AttackInterval > 3.0)
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;

View File

@ -36,7 +36,7 @@ void cGhast::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
if (m_Target != nullptr && m_AttackInterval > 3.0)
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;

View File

@ -9,7 +9,6 @@
#include "../Entities/Player.h"
#include "../Entities/ExpOrb.h"
#include "../MonsterConfig.h"
#include "../MersenneTwister.h"
#include "../Chunk.h"
#include "../FastRandom.h"
@ -267,7 +266,9 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
}
if ((m_Target != nullptr) && m_Target->IsDestroyed())
{
m_Target = nullptr;
}
// Burning in daylight
HandleDaylightBurning(a_Chunk);
@ -1028,22 +1029,34 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
MTRand r1;
if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedHelmet().IsEmpty()) a_Drops.push_back(GetEquippedHelmet());
if (!GetEquippedHelmet().IsEmpty())
{
a_Drops.push_back(GetEquippedHelmet());
}
}
if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedChestplate().IsEmpty()) a_Drops.push_back(GetEquippedChestplate());
if (!GetEquippedChestplate().IsEmpty())
{
a_Drops.push_back(GetEquippedChestplate());
}
}
if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedLeggings().IsEmpty()) a_Drops.push_back(GetEquippedLeggings());
if (!GetEquippedLeggings().IsEmpty())
{
a_Drops.push_back(GetEquippedLeggings());
}
}
if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedBoots().IsEmpty()) a_Drops.push_back(GetEquippedBoots());
if (!GetEquippedBoots().IsEmpty())
{
a_Drops.push_back(GetEquippedBoots());
}
}
}
@ -1056,7 +1069,10 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel)
MTRand r1;
if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2)))
{
if (!GetEquippedWeapon().IsEmpty()) a_Drops.push_back(GetEquippedWeapon());
if (!GetEquippedWeapon().IsEmpty())
{
a_Drops.push_back(GetEquippedWeapon());
}
}
}

View File

@ -49,17 +49,17 @@ void cPig::OnRightClicked(cPlayer & a_Player)
a_Player.Detach();
return;
}
if (m_Attachee->IsPlayer())
{
// Another player is already sitting in here, cannot attach
return;
}
// Detach whatever is sitting in this pig now:
m_Attachee->Detach();
}
// Attach the player to this pig
a_Player.AttachTo(this);
}
@ -100,7 +100,7 @@ void cPig::Tick(float a_Dt, cChunk & a_Chunk)
bool cPig::DoTakeDamage(TakeDamageInfo & a_TDI)
{
{
if (!super::DoTakeDamage(a_TDI))
{
return false;

View File

@ -71,7 +71,7 @@ void cSkeleton::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
if (m_Target != nullptr && m_AttackInterval > 3.0)
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
Vector3d Speed = GetLookVector() * 20;

View File

@ -38,7 +38,7 @@ void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk)
{
BLOCKTYPE BlockBelow = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()) - 1, (int) floor(GetPosZ()));
BLOCKTYPE Block = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()));
if (Block == E_BLOCK_AIR && cBlockInfo::IsSolid(BlockBelow))
if ((Block == E_BLOCK_AIR) && cBlockInfo::IsSolid(BlockBelow))
{
m_World->SetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()), E_BLOCK_SNOW, 0);
}

View File

@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Witch.h"
#include "FastRandom.h"

View File

@ -2,7 +2,6 @@
#pragma once
#include "AggressiveMonster.h"
#include "../MersenneTwister.h"

View File

@ -13,12 +13,9 @@ SET (SRCS
IsThread.cpp
ListenThread.cpp
Semaphore.cpp
Sleep.cpp
Socket.cpp
SocketThreads.cpp
StackTrace.cpp
Thread.cpp
Timer.cpp
)
SET (HDRS
@ -31,12 +28,9 @@ SET (HDRS
ListenThread.h
Queue.h
Semaphore.h
Sleep.h
Socket.h
SocketThreads.h
StackTrace.h
Thread.h
Timer.h
)
if(NOT MSVC)

View File

@ -1,6 +1,5 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "IsThread.h"
@ -9,41 +8,12 @@
////////////////////////////////////////////////////////////////////////////////
// cCriticalSection:
#ifdef _DEBUG
cCriticalSection::cCriticalSection()
{
#ifdef _WIN32
InitializeCriticalSection(&m_CriticalSection);
#else
pthread_mutexattr_init(&m_Attributes);
pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE);
if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0)
{
LOGERROR("Could not initialize Critical Section!");
}
#endif
#ifdef _DEBUG
m_IsLocked = 0;
#endif // _DEBUG
}
cCriticalSection::~cCriticalSection()
{
#ifdef _WIN32
DeleteCriticalSection(&m_CriticalSection);
#else
if (pthread_mutex_destroy(&m_CriticalSection) != 0)
{
LOGWARNING("Could not destroy Critical Section!");
}
pthread_mutexattr_destroy(&m_Attributes);
#endif
m_IsLocked = 0;
}
#endif // _DEBUG
@ -51,15 +21,11 @@ cCriticalSection::~cCriticalSection()
void cCriticalSection::Lock()
{
#ifdef _WIN32
EnterCriticalSection(&m_CriticalSection);
#else
pthread_mutex_lock(&m_CriticalSection);
#endif
m_Mutex.lock();
#ifdef _DEBUG
m_IsLocked += 1;
m_OwningThreadID = cIsThread::GetCurrentID();
m_OwningThreadID = std::this_thread::get_id();
#endif // _DEBUG
}
@ -74,11 +40,7 @@ void cCriticalSection::Unlock()
m_IsLocked -= 1;
#endif // _DEBUG
#ifdef _WIN32
LeaveCriticalSection(&m_CriticalSection);
#else
pthread_mutex_unlock(&m_CriticalSection);
#endif
m_Mutex.unlock();
}
@ -97,7 +59,7 @@ bool cCriticalSection::IsLocked(void)
bool cCriticalSection::IsLockedByCurrentThread(void)
{
return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID()));
return ((m_IsLocked > 0) && (m_OwningThreadID == std::this_thread::get_id()));
}
#endif // _DEBUG

View File

@ -1,5 +1,7 @@
#pragma once
#include <mutex>
#include <thread>
@ -8,8 +10,6 @@
class cCriticalSection
{
public:
cCriticalSection(void);
~cCriticalSection();
void Lock(void);
void Unlock(void);
@ -17,6 +17,7 @@ public:
// 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);
bool IsLocked(void);
bool IsLockedByCurrentThread(void);
#else
@ -27,15 +28,10 @@ public:
private:
#ifdef _DEBUG
int m_IsLocked; // Number of times this CS is locked
unsigned long m_OwningThreadID;
std::thread::id m_OwningThreadID;
#endif // _DEBUG
#ifdef _WIN32
CRITICAL_SECTION m_CriticalSection;
#else // _WIN32
pthread_mutex_t m_CriticalSection;
pthread_mutexattr_t m_Attributes;
#endif // else _WIN32
std::recursive_mutex m_Mutex;
} ALIGN_8;

View File

@ -12,68 +12,9 @@
cEvent::cEvent(void)
cEvent::cEvent(void) :
m_ShouldWait(true)
{
#ifdef _WIN32
m_Event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_Event == nullptr)
{
LOGERROR("cEvent: cannot create event, GLE = %u. Aborting server.", (unsigned)GetLastError());
abort();
}
#else // *nix
m_bIsNamed = false;
m_Event = new sem_t;
if (sem_init(m_Event, 0, 0))
{
// This path is used by MacOS, because it doesn't support unnamed semaphores.
delete m_Event;
m_bIsNamed = true;
AString EventName;
Printf(EventName, "cEvent%p", this);
m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0);
if (m_Event == SEM_FAILED)
{
AString error = GetOSErrorString(errno);
LOGERROR("cEvent: Cannot create event, err = %s. Aborting server.", error.c_str());
abort();
}
// Unlink the semaphore immediately - it will continue to function but will not pollute the namespace
// We don't store the name, so can't call this in the destructor
if (sem_unlink(EventName.c_str()) != 0)
{
AString error = GetOSErrorString(errno);
LOGWARN("ERROR: Could not unlink cEvent. (%s)", error.c_str());
}
}
#endif // *nix
}
cEvent::~cEvent()
{
#ifdef _WIN32
CloseHandle(m_Event);
#else
if (m_bIsNamed)
{
if (sem_close(m_Event) != 0)
{
AString error = GetOSErrorString(errno);
LOGERROR("ERROR: Could not close cEvent. (%s)", error.c_str());
}
}
else
{
sem_destroy(m_Event);
delete m_Event;
m_Event = nullptr;
}
#endif
}
@ -82,67 +23,48 @@ cEvent::~cEvent()
void cEvent::Wait(void)
{
#ifdef _WIN32
DWORD res = WaitForSingleObject(m_Event, INFINITE);
if (res != WAIT_OBJECT_0)
{
LOGWARN("cEvent: waiting for the event failed: %u, GLE = %u. Continuing, but server may be unstable.", (unsigned)res, (unsigned)GetLastError());
}
#else
int res = sem_wait(m_Event);
if (res != 0)
{
AString error = GetOSErrorString(errno);
LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str());
}
#endif
std::unique_lock<std::mutex> Lock(m_Mutex);
while (m_ShouldWait)
{
m_CondVar.wait(Lock);
}
m_ShouldWait = true;
}
bool cEvent::Wait(int a_TimeoutMSec)
bool cEvent::Wait(unsigned a_TimeoutMSec)
{
#ifdef _WIN32
DWORD res = WaitForSingleObject(m_Event, (DWORD)a_TimeoutMSec);
switch (res)
auto dst = std::chrono::system_clock::now() + std::chrono::milliseconds(a_TimeoutMSec);
std::unique_lock<std::mutex> Lock(m_Mutex); // We assume that this lock is acquired without much delay - we are the only user of the mutex
while (m_ShouldWait && (std::chrono::system_clock::now() <= dst))
{
switch (m_CondVar.wait_until(Lock, dst))
{
case WAIT_OBJECT_0: return true; // Regular event signalled
case WAIT_TIMEOUT: return false; // Regular event timeout
default:
case std::cv_status::no_timeout:
{
LOGWARN("cEvent: waiting for the event failed: %u, GLE = %u. Continuing, but server may be unstable.", (unsigned)res, (unsigned)GetLastError());
// The wait was successful, check for spurious wakeup:
if (!m_ShouldWait)
{
m_ShouldWait = true;
return true;
}
// This was a spurious wakeup, wait again:
continue;
}
case std::cv_status::timeout:
{
// The wait timed out, return failure:
return false;
}
}
#else
// Get the current time:
timespec timeout;
if (clock_gettime(CLOCK_REALTIME, &timeout) == -1)
{
LOGWARN("cEvent: Getting current time failed: %i, err = %s. Continuing, but the server may be unstable.", errno, GetOSErrorString(errno).c_str());
return false;
}
} // switch (wait_until())
} // while (m_ShouldWait && not timeout)
// Add the specified timeout:
timeout.tv_sec += a_TimeoutMSec / 1000;
timeout.tv_nsec += (a_TimeoutMSec % 1000) * 1000000; // 1 msec = 1000000 usec
// Wait with timeout:
int res = sem_timedwait(m_Event, &timeout);
switch (res)
{
case 0: return true; // Regular event signalled
case ETIMEDOUT: return false; // Regular even timeout
default:
{
AString error = GetOSErrorString(errno);
LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str());
return false;
}
}
#endif
// The wait timed out in the while condition:
return false;
}
@ -151,19 +73,11 @@ bool cEvent::Wait(int a_TimeoutMSec)
void cEvent::Set(void)
{
#ifdef _WIN32
if (!SetEvent(m_Event))
{
LOGWARN("cEvent: Could not set cEvent: GLE = %u", (unsigned)GetLastError());
}
#else
int res = sem_post(m_Event);
if (res != 0)
{
AString error = GetOSErrorString(errno);
LOGWARN("cEvent: Could not set cEvent: %i, err = %s", res, error.c_str());
}
#endif
{
std::unique_lock<std::mutex> Lock(m_Mutex);
m_ShouldWait = false;
}
m_CondVar.notify_one();
}

View File

@ -1,16 +1,17 @@
// Event.h
// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for
// Implemented as an Event on Win and as a 1-semaphore on *nix
// Interfaces to the cEvent object representing a synchronization primitive that can be waited-for
// Implemented using C++11 condition variable and mutex
#pragma once
#ifndef CEVENT_H_INCLUDED
#define CEVENT_H_INCLUDED
#include <mutex>
#include <condition_variable>
@ -20,32 +21,32 @@ class cEvent
{
public:
cEvent(void);
~cEvent();
/** Waits until the event has been set.
If the event has been set before it has been waited for, Wait() returns immediately. */
void Wait(void);
/** Sets the event - releases one thread that has been waiting in Wait().
If there was no thread waiting, the next call to Wait() will not block. */
void Set (void);
/** Waits for the event until either it is signalled, or the (relative) timeout is passed.
Returns true if the event was signalled, false if the timeout was hit or there was an error. */
bool Wait(int a_TimeoutMSec);
bool Wait(unsigned a_TimeoutMSec);
private:
#ifdef _WIN32
HANDLE m_Event;
#else
sem_t * m_Event;
bool m_bIsNamed;
#endif
/** Used for checking for spurious wakeups. */
bool m_ShouldWait;
/** Mutex protecting m_ShouldWait from multithreaded access. */
std::mutex m_Mutex;
/** The condition variable used as the Event. */
std::condition_variable m_CondVar;
} ;
#endif // CEVENT_H_INCLUDED

View File

@ -5,49 +5,40 @@
// This class will eventually suupersede the old cThread class
#include "Globals.h"
#include "IsThread.h"
// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
#if defined(_MSC_VER) && defined(_DEBUG)
//
// Usage: SetThreadName (-1, "MainThread");
//
// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
struct THREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1 = caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
};
#pragma pack(pop)
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1 = caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
static void SetThreadName(DWORD dwThreadID, const char * threadName)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
/** Sets the name of a thread with the specified ID
(When in MSVC, the debugger provides "thread naming" by catching special exceptions)
*/
static void SetThreadName(std::thread * a_Thread, const char * a_ThreadName)
{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
THREADNAME_INFO info { 0x1000, a_ThreadName, GetThreadId(a_Thread->native_handle()), 0 };
__try
{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}
#endif // _MSC_VER && _DEBUG
@ -57,13 +48,9 @@ static void SetThreadName(DWORD dwThreadID, const char * threadName)
////////////////////////////////////////////////////////////////////////////////
// cIsThread:
cIsThread::cIsThread(const AString & iThreadName) :
cIsThread::cIsThread(const AString & a_ThreadName) :
m_ShouldTerminate(false),
m_ThreadName(iThreadName),
#ifdef _WIN32
m_ThreadID(0),
#endif
m_Handle(NULL_HANDLE)
m_ThreadName(a_ThreadName)
{
}
@ -83,35 +70,24 @@ cIsThread::~cIsThread()
bool cIsThread::Start(void)
{
ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread?
#ifdef _WIN32
// Create the thread suspended, so that the mHandle variable is valid in the thread procedure
m_ThreadID = 0;
m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID);
if (m_Handle == NULL)
{
LOGERROR("ERROR: Could not create thread \"%s\", GLE = %u!", m_ThreadName.c_str(), (unsigned)GetLastError());
return false;
}
ResumeThread(m_Handle);
try
{
m_Thread = std::thread(&cIsThread::Execute, this);
#if defined(_DEBUG) && defined(_MSC_VER)
// Thread naming is available only in MSVC
if (!m_ThreadName.empty())
{
SetThreadName(m_ThreadID, m_ThreadName.c_str());
}
#endif // _DEBUG and _MSC_VER
#else // _WIN32
if (pthread_create(&m_Handle, NULL, thrExecute, this))
#if defined (_MSC_VER) && defined(_DEBUG)
if (!m_ThreadName.empty())
{
LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str());
return false;
SetThreadName(&m_Thread, m_ThreadName.c_str());
}
#endif // else _WIN32
#endif
return true;
return true;
}
catch (std::system_error & a_Exception)
{
LOGERROR("cIsThread::Start error %i: could not construct thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str());
return false;
}
}
@ -120,10 +96,12 @@ bool cIsThread::Start(void)
void cIsThread::Stop(void)
{
if (m_Handle == NULL_HANDLE)
if (!m_Thread.joinable())
{
// The thread hasn't been started or has already been joined
return;
}
m_ShouldTerminate = true;
Wait();
}
@ -134,59 +112,23 @@ void cIsThread::Stop(void)
bool cIsThread::Wait(void)
{
if (m_Handle == NULL_HANDLE)
LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
if (m_Thread.joinable())
{
return true;
try
{
m_Thread.join();
return true;
}
catch (std::system_error & a_Exception)
{
LOGERROR("cIsThread::Wait error %i: could not join thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str());
return false;
}
}
#ifdef LOGD // ProtoProxy doesn't have LOGD
LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
#endif // LOGD
#ifdef _WIN32
int res = WaitForSingleObject(m_Handle, INFINITE);
m_Handle = NULL;
#ifdef LOGD // ProtoProxy doesn't have LOGD
LOGD("Thread %s finished", m_ThreadName.c_str());
#endif // LOGD
return (res == WAIT_OBJECT_0);
#else // _WIN32
int res = pthread_join(m_Handle, NULL);
m_Handle = NULL_HANDLE;
#ifdef LOGD // ProtoProxy doesn't have LOGD
LOGD("Thread %s finished", m_ThreadName.c_str());
#endif // LOGD
return (res == 0);
#endif // else _WIN32
}
unsigned long cIsThread::GetCurrentID(void)
{
#ifdef _WIN32
return (unsigned long) GetCurrentThreadId();
#else
return (unsigned long) pthread_self();
#endif
}
bool cIsThread::IsCurrentThread(void) const
{
#ifdef _WIN32
return (GetCurrentThreadId() == m_ThreadID);
#else
return (m_Handle == pthread_self());
#endif
LOGD("Thread %s finished", m_ThreadName.c_str());
return true;
}

View File

@ -16,8 +16,7 @@ In the descending class' constructor call the Start() method to start the thread
#pragma once
#ifndef CISTHREAD_H_INCLUDED
#define CISTHREAD_H_INCLUDED
#include <thread>
@ -33,7 +32,7 @@ protected:
volatile bool m_ShouldTerminate;
public:
cIsThread(const AString & iThreadName);
cIsThread(const AString & a_ThreadName);
virtual ~cIsThread();
/// Starts the thread; returns without waiting for the actual start
@ -45,56 +44,14 @@ public:
/// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag
bool Wait(void);
/// Returns the OS-dependent thread ID for the caller's thread
static unsigned long GetCurrentID(void);
/** Returns true if the thread calling this function is the thread contained within this object. */
bool IsCurrentThread(void) const;
bool IsCurrentThread(void) const { return std::this_thread::get_id() == m_Thread.get_id(); }
protected:
AString m_ThreadName;
// Value used for "no handle":
#ifdef _WIN32
#define NULL_HANDLE NULL
#else
#define NULL_HANDLE 0
#endif
#ifdef _WIN32
DWORD m_ThreadID;
HANDLE m_Handle;
static DWORD __stdcall thrExecute(LPVOID a_Param)
{
// Create a window so that the thread can be identified by 3rd party tools:
HWND IdentificationWnd = CreateWindowA("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL);
// Run the thread:
((cIsThread *)a_Param)->Execute();
// Destroy the identification window:
DestroyWindow(IdentificationWnd);
return 0;
}
#else // _WIN32
pthread_t m_Handle;
static void * thrExecute(void * a_Param)
{
(static_cast<cIsThread *>(a_Param))->Execute();
return NULL;
}
#endif // else _WIN32
std::thread m_Thread;
} ;
#endif // CISTHREAD_H_INCLUDED

View File

@ -163,6 +163,29 @@ public:
return false;
}
/** Removes all items for which the predicate returns true. */
template <class Predicate>
void RemoveIf(Predicate a_Predicate)
{
cCSLock Lock(m_CS);
for (auto itr = m_Contents.begin(); itr != m_Contents.end();)
{
if (a_Predicate(*itr))
{
auto itr2 = itr;
++itr2;
m_Contents.erase(itr);
m_evtRemoved.Set();
itr = itr2;
}
else
{
++itr;
}
} // for itr - m_Contents[]
}
private:
/// The contents of the queue
QueueType m_Contents;

View File

@ -1,19 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#ifndef _WIN32
#include <unistd.h>
#endif
void cSleep::MilliSleep( unsigned int a_MilliSeconds)
{
#ifdef _WIN32
Sleep(a_MilliSeconds); // Don't tick too much
#else
usleep(a_MilliSeconds*1000);
#endif
}

View File

@ -1,7 +0,0 @@
#pragma once
class cSleep
{
public:
static void MilliSleep( unsigned int a_MilliSeconds);
};

View File

@ -74,7 +74,7 @@ public:
inline static bool IsSocketError(int a_ReturnedValue)
{
#ifdef _WIN32
return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0);
return ((a_ReturnedValue == SOCKET_ERROR) || (a_ReturnedValue == 0));
#else
return (a_ReturnedValue <= 0);
#endif

View File

@ -86,7 +86,8 @@ void cSocketThreads::RemoveClient(const cCallback * a_Client)
}
} // for itr - m_Threads[]
ASSERT(!"Removing an unknown client");
// This client wasn't found.
// It's not an error, because it may have been removed by a different thread in the meantime.
}
@ -491,10 +492,17 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
{
case sSlot::ssNormal:
{
// Notify the callback that the remote has closed the socket; keep the slot
m_Slots[i].m_Client->SocketClosed();
// Close the socket on our side:
m_Slots[i].m_State = sSlot::ssRemoteClosed;
m_Slots[i].m_Socket.CloseSocket();
// Notify the callback that the remote has closed the socket, *after* removing the socket:
cCallback * client = m_Slots[i].m_Client;
m_Slots[i] = m_Slots[--m_NumSlots];
if (client != nullptr)
{
client->SocketClosed();
}
break;
}
case sSlot::ssWritingRestOut:

View File

@ -1,137 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
#ifdef _MSC_VER
//
// Usage: SetThreadName (-1, "MainThread");
//
// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1 = caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
static void SetThreadName(DWORD dwThreadID, const char * threadName)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = threadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}
#endif // _MSC_VER
cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */)
: m_ThreadFunction( a_ThreadFunction)
, m_Param( a_Param)
, m_Event( new cEvent())
, m_StopEvent( 0)
{
if (a_ThreadName)
{
m_ThreadName.assign(a_ThreadName);
}
}
cThread::~cThread()
{
delete m_Event;
m_Event = NULL;
if (m_StopEvent)
{
m_StopEvent->Wait();
delete m_StopEvent;
m_StopEvent = NULL;
}
}
void cThread::Start( bool a_bWaitOnDelete /* = true */)
{
if (a_bWaitOnDelete)
m_StopEvent = new cEvent();
#ifndef _WIN32
pthread_t SndThread;
if (pthread_create( &SndThread, NULL, MyThread, this))
LOGERROR("ERROR: Could not create thread!");
#else
DWORD ThreadID = 0;
HANDLE hThread = CreateThread(NULL // security
, 0 // stack size
, (LPTHREAD_START_ROUTINE) MyThread // function name
, this // parameters
, 0 // flags
, &ThreadID); // thread id
CloseHandle( hThread);
#ifdef _MSC_VER
if (!m_ThreadName.empty())
{
SetThreadName(ThreadID, m_ThreadName.c_str());
}
#endif // _MSC_VER
#endif
// Wait until thread has actually been created
m_Event->Wait();
}
#ifdef _WIN32
unsigned long cThread::MyThread(void* a_Param)
#else
void *cThread::MyThread( void *a_Param)
#endif
{
cThread* self = (cThread*)a_Param;
cEvent* StopEvent = self->m_StopEvent;
ThreadFunc* ThreadFunction = self->m_ThreadFunction;
void* ThreadParam = self->m_Param;
// Set event to let other thread know this thread has been created and it's safe to delete the cThread object
self->m_Event->Set();
ThreadFunction( ThreadParam);
if (StopEvent) StopEvent->Set();
return 0;
}

View File

@ -1,26 +0,0 @@
#pragma once
class cThread
{
public:
typedef void (ThreadFunc)(void*);
cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0);
~cThread();
void Start( bool a_bWaitOnDelete = true);
void WaitForThread();
private:
ThreadFunc* m_ThreadFunction;
#ifdef _WIN32
static unsigned long MyThread(void* a_Param);
#else
static void *MyThread( void *lpParam);
#endif
void* m_Param;
cEvent* m_Event;
cEvent* m_StopEvent;
AString m_ThreadName;
};

View File

@ -1,37 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Timer.h"
cTimer::cTimer(void)
{
#ifdef _WIN32
QueryPerformanceFrequency(&m_TicksPerSecond);
#endif
}
long long cTimer::GetNowTime(void)
{
#ifdef _WIN32
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart);
#else
struct timeval now;
gettimeofday(&now, NULL);
return (long long)now.tv_sec * 1000 + (long long)now.tv_usec / 1000;
#endif
}

View File

@ -1,32 +0,0 @@
// Timer.h
// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy
#pragma once
class cTimer
{
public:
cTimer(void);
// Returns the current time expressed in milliseconds
long long GetNowTime(void);
private:
#ifdef _WIN32
LARGE_INTEGER m_TicksPerSecond;
#endif
} ;

View File

@ -5,7 +5,7 @@
#include "Globals.h"
#include "ProbabDistrib.h"
#include "MersenneTwister.h"
#include "FastRandom.h"

View File

@ -161,26 +161,38 @@ class cMojangAPI::cUpdateThread :
{
typedef cIsThread super;
public:
cUpdateThread() :
super("cMojangAPI::cUpdateThread")
cUpdateThread(cMojangAPI & a_MojangAPI) :
super("cMojangAPI::cUpdateThread"),
m_MojangAPI(a_MojangAPI)
{
}
~cUpdateThread()
{
// Notify the thread that it should stop:
m_ShouldTerminate = true;
m_evtNotify.Set();
// Wait for the thread to actually finish work:
Stop();
}
protected:
/** The cMojangAPI instance to update. */
cMojangAPI & m_MojangAPI;
/** The event used for notifying that the thread should terminate, as well as timing. */
cEvent m_evtNotify;
// cIsThread override:
virtual void Execute(void) override
{
do
{
cRoot::Get()->GetMojangAPI().Update();
} while (!m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes
m_MojangAPI.Update();
} while (!m_ShouldTerminate && !m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes until termination request
}
} ;
@ -197,7 +209,7 @@ cMojangAPI::cMojangAPI(void) :
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
m_RankMgr(nullptr),
m_UpdateThread(new cUpdateThread())
m_UpdateThread(new cUpdateThread(*this))
{
}

View File

@ -16,7 +16,6 @@
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
#include "CommandOutput.h"
#include "DeadlockDetect.h"
#include "OSSupport/Timer.h"
#include "LoggerListeners.h"
#include "BuildInfo.h"
#include "IniFile.h"
@ -42,7 +41,6 @@ cRoot* cRoot::s_Root = nullptr;
cRoot::cRoot(void) :
m_pDefaultWorld(nullptr),
m_InputThread(nullptr),
m_Server(nullptr),
m_MonsterConfig(nullptr),
m_CraftingRecipes(nullptr),
@ -68,26 +66,24 @@ cRoot::~cRoot()
void cRoot::InputThread(void * a_Params)
void cRoot::InputThread(cRoot & a_Params)
{
cRoot & self = *(cRoot*)a_Params;
cLogCommandOutputCallback Output;
while (!self.m_bStop && !self.m_bRestart && !m_TerminateEventRaised && std::cin.good())
while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
{
AString Command;
std::getline(std::cin, Command);
if (!Command.empty())
{
self.ExecuteConsoleCommand(TrimString(Command), Output);
a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
}
}
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:
self.m_bStop = true;
a_Params.m_bStop = true;
}
}
@ -120,9 +116,7 @@ void cRoot::Start(void)
m_bStop = false;
while (!m_bStop)
{
cTimer Time;
long long mseconds = Time.GetNowTime();
auto BeginTime = std::chrono::steady_clock::now();
m_bRestart = false;
LoadGlobalSettings();
@ -191,21 +185,25 @@ void cRoot::Start(void)
#if !defined(ANDROID_NDK)
LOGD("Starting InputThread...");
m_InputThread = new cThread( InputThread, this, "cRoot::InputThread");
m_InputThread->Start( false); // We should NOT wait? Otherwise we can't stop the server from other threads than the input thread
try
{
m_InputThread = std::thread(InputThread, std::ref(*this));
m_InputThread.detach();
}
catch (std::system_error & a_Exception)
{
LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what());
}
#endif
long long finishmseconds = Time.GetNowTime();
finishmseconds -= mseconds;
LOG("Startup complete, took %lld ms!", finishmseconds);
LOG("Startup complete, took %ld ms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
#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
{
cSleep::MilliSleep(1000);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (m_TerminateEventRaised)
@ -213,10 +211,6 @@ void cRoot::Start(void)
m_bStop = true;
}
#if !defined(ANDROID_NDK)
delete m_InputThread; m_InputThread = nullptr;
#endif
// Stop the server:
m_WebAdmin->Stop();
LOG("Shutting down server...");
@ -649,6 +643,22 @@ bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback
bool cRoot::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback)
{
for (auto World : m_WorldsByName)
{
if (World.second->DoWithPlayer(a_PlayerName, a_Callback))
{
return true;
}
}
return false;
}
AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
{
return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion);

View File

@ -6,6 +6,7 @@
#include "HTTPServer/HTTPServer.h"
#include "Defines.h"
#include "RankManager.h"
#include <thread>
@ -128,7 +129,10 @@ 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)
@ -177,7 +181,7 @@ private:
cCriticalSection m_CSPendingCommands;
cCommandQueue m_PendingCommands;
cThread * m_InputThread;
std::thread m_InputThread;
cServer * m_Server;
cMonsterConfig * m_MonsterConfig;
@ -212,10 +216,10 @@ private:
/// Does the actual work of executing a command
void DoExecuteConsoleCommand(const AString & a_Cmd);
static void InputThread(void* a_Params);
static cRoot* s_Root;
static void InputThread(cRoot & a_Params);
}; // tolua_export

View File

@ -4,7 +4,6 @@
#include "Server.h"
#include "ClientHandle.h"
#include "OSSupport/Timer.h"
#include "Mobs/Monster.h"
#include "OSSupport/Socket.h"
#include "Root.h"
@ -20,8 +19,6 @@
#include "Protocol/ProtocolRecognizer.h"
#include "CommandOutput.h"
#include "MersenneTwister.h"
#include "IniFile.h"
#include "Vector3.h"
@ -75,22 +72,20 @@ cServer::cTickThread::cTickThread(cServer & a_Server) :
void cServer::cTickThread::Execute(void)
{
cTimer Timer;
long long msPerTick = 50;
long long LastTime = Timer.GetNowTime();
auto LastTime = std::chrono::steady_clock::now();
static const auto msPerTick = std::chrono::milliseconds(50);
while (!m_ShouldTerminate)
{
long long NowTime = Timer.GetNowTime();
float DeltaTime = (float)(NowTime-LastTime);
m_ShouldTerminate = !m_Server.Tick(DeltaTime);
long long TickTime = Timer.GetNowTime() - NowTime;
auto NowTime = std::chrono::steady_clock::now();
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
cSleep::MilliSleep((unsigned int)(msPerTick - TickTime));
std::this_thread::sleep_for(msPerTick - TickTime);
}
LastTime = NowTime;
@ -201,6 +196,7 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, 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_PlayerCount = 0;
m_PlayerCountDiff = 0;
@ -303,6 +299,23 @@ int cServer::GetNumPlayers(void) const
bool cServer::IsPlayerInQueue(AString a_Username)
{
cCSLock Lock(m_CSClients);
for (auto client : m_Clients)
{
if ((client->GetUsername()).compare(a_Username) == 0)
{
return true;
}
}
return false;
}
void cServer::PrepareKeys(void)
{
LOGD("Generating protocol encryption keypair...");

View File

@ -67,6 +67,14 @@ public: // tolua_export
int GetNumPlayers(void) const;
void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; }
/** Check if the player is queued to be transferred to a World.
Returns true is Player is found in queue. */
bool IsPlayerInQueue(AString a_Username);
/** Can login more than once with same username.
Returns false if it is not allowed, true otherwise. */
bool DoesAllowMultiLogin(void) { return m_bAllowMultiLogin; }
// Hardcore mode or not:
bool IsHardcore(void) const { return m_bIsHardcore; }
@ -216,6 +224,9 @@ private:
int m_MaxPlayers;
bool m_bIsHardcore;
/** True - allow same username to login more than once False - only once */
bool m_bAllowMultiLogin;
cTickThread m_TickThread;
cEvent m_RestartEvent;

View File

@ -133,8 +133,10 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a
/*
Disabled because of causing problems and being useless atm
char BlockBelow = m_World.GetBlock(a_X, a_Y - 1, a_Z); // If there is nothing or fluid below it -> dominating flow is down :D
if (BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow))
if ((BlockBelow == E_BLOCK_AIR) || IsAllowedBlock(BlockBelow))
{
return Y_MINUS;
}
*/
NIBBLETYPE LowestPoint = m_World.GetBlockMeta(a_X, a_Y, a_Z); // Current Block Meta so only lower points will be counted
@ -182,7 +184,9 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a
}
if (LowestPoint == m_World.GetBlockMeta(a_X, a_Y, a_Z))
{
return NONE;
}
if (a_X - X > 0)
{

View File

@ -416,24 +416,30 @@ static bool isLegalUTF8(const unsigned char * source, int length)
{
default: return false;
// Everything else falls through when "true"...
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 4: if (((a = (*--srcptr)) < 0x80) || (a > 0xbf)) return false;
case 3: if (((a = (*--srcptr)) < 0x80) || (a > 0xbf)) return false;
case 2:
{
if ((a = (*--srcptr)) > 0xBF) return false;
if ((a = (*--srcptr)) > 0xbf)
{
return false;
}
switch (*source)
{
// no fall-through in this inner switch
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
case 0xe0: if (a < 0xa0) return false; break;
case 0xed: if (a > 0x9f) return false; break;
case 0xf0: if (a < 0x90) return false; break;
case 0xf4: if (a > 0x8f) return false; break;
default: if (a < 0x80) return false;
}
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
case 1: if ((*source >= 0x80) && (*source < 0xc2)) return false;
}
if (*source > 0xf4)
{
return false;
}
if (*source > 0xF4) return false;
return true;
}
@ -446,11 +452,11 @@ AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length)
AString UTF16;
UTF16.reserve(a_UTF8Length * 3);
const unsigned char * source = (const unsigned char*)a_UTF8;
const unsigned char * source = (const unsigned char *)a_UTF8;
const unsigned char * sourceEnd = source + a_UTF8Length;
const int halfShift = 10; // used for shifting by 10 bits
const unsigned int halfBase = 0x0010000UL;
const unsigned int halfMask = 0x3FFUL;
const unsigned int halfMask = 0x3ffUL;
while (source < sourceEnd)
{
@ -481,7 +487,7 @@ AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length)
if (ch <= UNI_MAX_BMP)
{
// Target is a character <= 0xFFFF
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
if ((ch >= UNI_SUR_HIGH_START) && (ch <= UNI_SUR_LOW_END))
{
// UTF-16 surrogate values are illegal in UTF-32
ch = ' ';
@ -520,7 +526,10 @@ are equivalent to the following loop:
{
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
if (tmpBytesToRead)
{
ch <<= 6;
}
} while (tmpBytesToRead > 0);
}
---------------------------------------------------------------------
@ -723,15 +732,15 @@ AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_
/// Converts one Hex character in a Base64 encoding into the data value
static inline int UnBase64(char c)
{
if (c >='A' && c <= 'Z')
if ((c >='A') && (c <= 'Z'))
{
return c - 'A';
}
if (c >='a' && c <= 'z')
if ((c >='a') && (c <= 'z'))
{
return c - 'a' + 26;
}
if (c >= '0' && c <= '9')
if ((c >= '0') && (c <= '9'))
{
return c - '0' + 52;
}

View File

@ -14,15 +14,15 @@
cTracer::cTracer(cWorld * a_World)
: m_World(a_World)
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);
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,10 +37,16 @@ cTracer::~cTracer()
float cTracer::SigNum( float a_Num)
float cTracer::SigNum(float a_Num)
{
if (a_Num < 0.f) return -1.f;
if (a_Num > 0.f) return 1.f;
if (a_Num < 0.f)
{
return -1.f;
}
if (a_Num > 0.f)
{
return 1.f;
}
return 0.f;
}
@ -59,7 +65,10 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
step.z = (int) SigNum(dir.z);
// normalize the direction vector
if (dir.SqrLength() > 0.f) dir.Normalize();
if (dir.SqrLength() > 0.f)
{
dir.Normalize();
}
// how far we must move in the ray direction before
// we encounter a new voxel in x-direction
@ -127,7 +136,7 @@ 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)
bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight)
{
if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height))
{
@ -249,35 +258,42 @@ int LinesCross(float x0, float y0, float x1, float y1, float x2, float y2, float
{
// float linx, liny;
float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2);
if (std::abs(d)<0.001) {return 0;}
float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d;
if (AB>=0.0 && AB<=1.0)
float d = (x1 - x0) * (y3 - y2) - (y1 - y0) * (x3 - x2);
if (std::abs(d) < 0.001)
{
float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d;
if (CD>=0.0 && CD<=1.0)
return 0;
}
float AB = ((y0 - y2) * (x3 - x2) - (x0 - x2) * (y3 - y2)) / d;
if ((AB >= 0.0) && (AB <= 1.0))
{
float CD = ((y0 - y2) * (x1 - x0) - (x0 - x2) * (y1 - y0)) / d;
if ((CD >= 0.0) && (CD <= 1.0))
{
// linx=x0+AB*(x1-x0);
// liny=y0+AB*(y1-y0);
// linx = x0 + AB * (x1 - x0);
// liny = y0 + AB * (y1 - y0);
return 1;
}
}
return 0;
}
// intersect3D_SegmentPlane(): intersect a segment and a plane
// Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;}
// Output: *I0 = the intersect point (when it exists)
// Return: 0 = disjoint (no intersection)
// 1 = intersection in the unique point *I0
// 2 = the segment lies in the plane
int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal)
int cTracer::intersect3D_SegmentPlane(const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal)
{
Vector3f u = a_End - a_Origin; // a_Ray.P1 - S.P0;
Vector3f w = a_Origin - a_PlanePos; // S.P0 - Pn.V0;
float D = a_PlaneNormal.Dot( u); // dot(Pn.n, u);
float N = -(a_PlaneNormal.Dot( w)); // -dot(a_Plane.n, w);
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)
@ -294,12 +310,12 @@ int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f
// they are not parallel
// compute intersect param
float sI = N / D;
if (sI < 0 || sI > 1)
if ((sI < 0) || (sI > 1))
{
return 0; // no intersection
}
// Vector3f I ( a_Ray->GetOrigin() + sI * u);// S.P0 + sI * u; // compute segment intersect point
// Vector3f I (a_Ray->GetOrigin() + sI * u);// S.P0 + sI * u; // compute segment intersect point
RealHit = a_Origin + u * sI;
return 1;
}
@ -311,9 +327,9 @@ int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f
int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos)
{
Vector3i SmallBlockPos = a_BlockPos;
char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
char BlockID = m_World->GetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
if (BlockID == E_BLOCK_AIR || IsBlockWater(BlockID))
if ((BlockID == E_BLOCK_AIR) || IsBlockWater(BlockID))
{
return 0;
}
@ -324,86 +340,86 @@ int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Ve
Vector3f Look = (end - start);
Look.Normalize();
float dot = Look.Dot( Vector3f(-1, 0, 0)); // first face normal is x -1
float dot = Look.Dot(Vector3f(-1, 0, 0)); // first face normal is x -1
if (dot < 0)
{
int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1);
int Lines = LinesCross(start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1);
if (Lines == 1)
{
Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1);
Lines = LinesCross(start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1);
if (Lines == 1)
{
intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0));
intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(-1, 0, 0));
return 1;
}
}
}
dot = Look.Dot( Vector3f(0, 0, -1)); // second face normal is z -1
dot = Look.Dot(Vector3f(0, 0, -1)); // second face normal is z -1
if (dot < 0)
{
int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1);
int Lines = LinesCross(start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1);
if (Lines == 1)
{
Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1);
Lines = LinesCross(start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1);
if (Lines == 1)
{
intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1));
intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(0, 0, -1));
return 2;
}
}
}
dot = Look.Dot( Vector3f(1, 0, 0)); // third face normal is x 1
dot = Look.Dot(Vector3f(1, 0, 0)); // third face normal is x 1
if (dot < 0)
{
int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1);
int Lines = LinesCross(start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1);
if (Lines == 1)
{
Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1);
Lines = LinesCross(start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1);
if (Lines == 1)
{
intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0));
intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0));
return 3;
}
}
}
dot = Look.Dot( Vector3f(0, 0, 1)); // fourth face normal is z 1
dot = Look.Dot(Vector3f(0, 0, 1)); // fourth face normal is z 1
if (dot < 0)
{
int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1);
int Lines = LinesCross(start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1);
if (Lines == 1)
{
Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1);
Lines = LinesCross(start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1);
if (Lines == 1)
{
intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1));
intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1));
return 4;
}
}
}
dot = Look.Dot( Vector3f(0, 1, 0)); // fifth face normal is y 1
dot = Look.Dot(Vector3f(0, 1, 0)); // fifth face normal is y 1
if (dot < 0)
{
int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1);
int Lines = LinesCross(start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1);
if (Lines == 1)
{
Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1);
Lines = LinesCross(start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1);
if (Lines == 1)
{
intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0));
intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0));
return 5;
}
}
}
dot = Look.Dot( Vector3f(0, -1, 0)); // sixth face normal is y -1
dot = Look.Dot(Vector3f(0, -1, 0)); // sixth face normal is y -1
if (dot < 0)
{
int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1);
int Lines = LinesCross(start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1);
if (Lines == 1)
{
Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1);
Lines = LinesCross(start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1);
if (Lines == 1)
{
intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0));
intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(0, -1, 0));
return 6;
}
}

View File

@ -1167,7 +1167,7 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player)
{
m_MaximumCost = 39;
}
if (m_MaximumCost >= 40 && !a_Player.IsGameModeCreative())
if ((m_MaximumCost >= 40) && !a_Player.IsGameModeCreative())
{
Input.Empty();
}
@ -2322,7 +2322,7 @@ cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
{
return nullptr;
}
return &(itr->second[0]);
return itr->second.data();
}

View File

@ -11,7 +11,6 @@
#include "IniFile.h"
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
#include "OSSupport/Timer.h"
#include "SetChunkData.h"
// Serializers
@ -45,7 +44,6 @@
#include "MobCensus.h"
#include "MobSpawner.h"
#include "MersenneTwister.h"
#include "Generating/Trees.h"
#include "Bindings/PluginManager.h"
#include "Blocks/BlockHandler.h"
@ -92,7 +90,6 @@ public:
m_PrepareDistance(a_PrepareDistance),
m_MaxIdx(a_PrepareDistance * a_PrepareDistance),
m_NumPrepared(0),
m_LastReportTime(0),
m_LastReportChunkCount(0)
{
// Start the thread:
@ -113,12 +110,12 @@ public:
m_MaxIdx = m_PrepareDistance * m_PrepareDistance;
int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once
m_NextIdx = maxQueue;
m_LastReportTime = m_Timer.GetNowTime();
m_LastReportTime = std::chrono::steady_clock::now();
for (int i = 0; i < maxQueue; i++)
{
int chunkX, chunkZ;
DecodeChunkCoords(i, chunkX, chunkZ);
m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
m_World.PrepareChunk(chunkX, chunkZ, this);
} // for i
// Wait for the lighting thread to prepare everything. Event is set in the Call() callback:
@ -146,16 +143,12 @@ protected:
/** Event used to signal that the preparation is finished. */
cEvent m_EvtFinished;
/** The timer used to report progress every second. */
cTimer m_Timer;
/** The timestamp of the last progress report emitted. */
long long m_LastReportTime;
std::chrono::steady_clock::time_point m_LastReportTime;
/** Number of chunks prepared when the last progress report was emitted. */
int m_LastReportChunkCount;
// cChunkCoordCallback override:
virtual void Call(int a_ChunkX, int a_ChunkZ)
{
@ -178,15 +171,15 @@ protected:
}
// Report progress every 1 second:
long long now = m_Timer.GetNowTime();
if (now - m_LastReportTime > 1000)
auto Now = std::chrono::steady_clock::now();
if (Now - m_LastReportTime > std::chrono::seconds(1))
{
float percentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx;
float chunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / (now - m_LastReportTime);
LOG("Preparing spawn (%s): %.02f%% done (%d chunks out of %d; %.02f chunks / sec)",
m_World.GetName().c_str(), percentDone, m_NumPrepared, m_MaxIdx, chunkSpeed
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)",
m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed
);
m_LastReportTime = now;
m_LastReportTime = Now;
m_LastReportChunkCount = m_NumPrepared;
}
}
@ -239,23 +232,22 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) :
void cWorld::cTickThread::Execute(void)
{
cTimer Timer;
auto LastTime = std::chrono::steady_clock::now();
static const auto msPerTick = std::chrono::milliseconds(50);
auto TickTime = std::chrono::steady_clock::duration(50);
const Int64 msPerTick = 50;
Int64 LastTime = Timer.GetNowTime();
Int64 TickDuration = 50;
while (!m_ShouldTerminate)
{
Int64 NowTime = Timer.GetNowTime();
float DeltaTime = (float)(NowTime - LastTime);
m_World.Tick(DeltaTime, (int)TickDuration);
TickDuration = Timer.GetNowTime() - NowTime;
auto NowTime = std::chrono::steady_clock::now();
auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count();
auto LastTickMsec = std::chrono::duration_cast<std::chrono::duration<int>>(TickTime).count();
m_World.Tick(static_cast<float>(msec), LastTickMsec);
TickTime = std::chrono::steady_clock::now() - NowTime;
if (TickDuration < msPerTick)
if (TickTime < msPerTick)
{
// Stretch tick time until it's at least msPerTick
cSleep::MilliSleep((unsigned int)(msPerTick - TickDuration));
std::this_thread::sleep_for(msPerTick - TickTime);
}
LastTime = NowTime;
@ -354,6 +346,10 @@ cWorld::~cWorld()
Serializer.Save();
m_MapManager.SaveMapData();
// Explicitly destroy the chunkmap, so that it's guaranteed to be destroyed before the other internals
// This fixes crashes on stopping the server, because chunk destructor deletes entities and those access the world.
m_ChunkMap.reset();
}
@ -614,7 +610,7 @@ void cWorld::Start(void)
}
// Adjust the enum-backed variables into their respective bounds:
m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmAdventure);
m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmSpectator);
m_TNTShrapnelLevel = (eShrapnelLevel)Clamp(TNTShrapnelLevel, (int)slNone, (int)slAll);
m_Weather = (eWeather) Clamp(Weather, (int)wSunny, (int)wStorm);
@ -747,7 +743,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, PreSimulator");
a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals");
break;
}
case dimNether:
@ -759,7 +755,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", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
break;
}
@ -1809,7 +1805,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_FlyAwaySpeed /= 100; // Pre-divide, so that we don't have to divide each time inside the loop
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR)
if (!IsValidItem(itr->m_ItemType) || (itr->m_ItemType == E_BLOCK_AIR))
{
// Don't spawn pickup if item isn't even valid; should prevent client crashing too
continue;
@ -1835,7 +1831,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
{
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR)
if (!IsValidItem(itr->m_ItemType) || (itr->m_ItemType == E_BLOCK_AIR))
{
continue;
}
@ -2911,6 +2907,15 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ)
void cWorld::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter)
{
m_ChunkMap->PrepareChunk(a_ChunkX, a_ChunkZ, a_CallAfter);
}
void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ);
@ -3021,7 +3026,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkZ);
m_ChunkMap->GenerateChunk(a_ChunkX, a_ChunkZ);
}
@ -3130,6 +3135,11 @@ bool cWorld::HasEntity(int a_UniqueID)
}
// Check if the entity is in the chunkmap:
if (m_ChunkMap.get() == nullptr)
{
// Chunkmap has already been destroyed, there are no entities anymore.
return false;
}
return m_ChunkMap->HasEntity(a_UniqueID);
}
@ -3668,3 +3678,4 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch

View File

@ -10,7 +10,6 @@
#define MAX_PLAYERS 65535
#include "Simulator/SimulatorManager.h"
#include "MersenneTwister.h"
#include "ChunkMap.h"
#include "WorldStorage/WorldStorage.h"
#include "Generating/ChunkGenerator.h"
@ -26,6 +25,7 @@
#include "MapManager.h"
#include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h"
#include "FastRandom.h"
#include "ClientHandle.h"
@ -375,6 +375,12 @@ public:
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkZ);
/** Queues the chunk for preparing - making sure that it's generated and lit.
The specified chunk is queued to be loaded or generated, and lit if needed.
The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called.
It is legal to call with no callback. */
void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr);
/** Marks the chunk as failed-to-load: */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
@ -808,7 +814,7 @@ 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
private:
friend class cRoot;

View File

@ -22,11 +22,11 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa
a_Writer.AddByte("Type", a_FireworkItem.m_Type);
if (!a_FireworkItem.m_Colours.empty())
{
a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size());
a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size());
}
if (!a_FireworkItem.m_FadeColours.empty())
{
a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size());
a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size());
}
a_Writer.EndCompound();
a_Writer.EndList();
@ -41,11 +41,11 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa
a_Writer.AddByte("Type", a_FireworkItem.m_Type);
if (!a_FireworkItem.m_Colours.empty())
{
a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size());
a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size());
}
if (!a_FireworkItem.m_FadeColours.empty())
{
a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size());
a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size());
}
a_Writer.EndCompound();
break;

View File

@ -112,7 +112,7 @@ void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer)
a_Writer.AddInt("zCenter", m_Map->GetCenterZ());
const cMap::cColorList & Data = m_Map->GetData();
a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size());
a_Writer.AddByteArray("colors", (char *)Data.data(), Data.size());
a_Writer.EndCompound();
}
@ -190,7 +190,7 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
CurrLine = a_NBT.FindChildByName(Data, "colors");
if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray))
{
memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels);
memcpy(m_Map->m_Data.data(), a_NBT.GetData(CurrLine), NumPixels);
}
return true;

187
src/WorldStorage/WSSAnvil.cpp Normal file → Executable file
View File

@ -631,7 +631,7 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
// Load the proper BlockEntity type based on the block type:
BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ);
NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ);
std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
std::unique_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
if (be.get() == nullptr)
{
continue;
@ -858,7 +858,7 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI
return nullptr;
}
std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels");
if (CurrentLine >= 0)
@ -908,7 +908,7 @@ cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagId
{
return nullptr; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
std::unique_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items);
return Chest.release();
}
@ -925,7 +925,7 @@ cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int
return nullptr;
}
std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
if (currentLine >= 0)
@ -967,7 +967,7 @@ cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_T
{
return nullptr; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
}
std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items);
return Dispenser.release();
}
@ -989,7 +989,7 @@ cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_Tag
{
return nullptr; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
}
std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
return Dropper.release();
}
@ -1006,7 +1006,7 @@ cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_T
return nullptr;
}
std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
short ItemType = 0, ItemData = 0;
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
@ -1043,7 +1043,7 @@ cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_Tag
return nullptr; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
}
std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
std::unique_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
// Load slots:
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
@ -1148,7 +1148,7 @@ cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagI
{
return nullptr; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
}
std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
return Hopper.release();
}
@ -1165,7 +1165,7 @@ cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_Tag
return nullptr;
}
std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
if (Record >= 0)
{
@ -1186,7 +1186,7 @@ cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_Tag
return nullptr;
}
std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
@ -1221,7 +1221,7 @@ cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_T
return nullptr;
}
std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int note = a_NBT.FindChildByName(a_TagIdx, "note");
if (note >= 0)
{
@ -1242,7 +1242,7 @@ cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx
return nullptr;
}
std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
std::unique_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
if (currentLine >= 0)
@ -1495,7 +1495,7 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cBoat> Boat(new cBoat(0, 0, 0));
std::unique_ptr<cBoat> Boat(new cBoat(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx))
{
return;
@ -1509,7 +1509,7 @@ void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
void cWSSAnvil::LoadEnderCrystalFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0));
std::unique_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0));
if (!LoadEntityBaseFromNBT(*EnderCrystal.get(), a_NBT, a_TagIdx))
{
return;
@ -1526,12 +1526,15 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "TileID");
int MetaIdx = a_NBT.FindChildByName(a_TagIdx, "Data");
if ((TypeIdx < 0) || (MetaIdx < 0)) { return; }
if ((TypeIdx < 0) || (MetaIdx < 0))
{
return;
}
int Type = a_NBT.GetInt(TypeIdx);
NIBBLETYPE Meta = (NIBBLETYPE)a_NBT.GetByte(MetaIdx);
std::auto_ptr<cFallingBlock> FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta));
std::unique_ptr<cFallingBlock> FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta));
if (!LoadEntityBaseFromNBT(*FallingBlock.get(), a_NBT, a_TagIdx))
{
return;
@ -1545,7 +1548,7 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN
void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height
std::unique_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@ -1564,7 +1567,7 @@ void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
std::auto_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0));
std::unique_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@ -1591,7 +1594,7 @@ void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0));
std::unique_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@ -1608,7 +1611,7 @@ void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0));
std::unique_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@ -1625,7 +1628,7 @@ void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0));
std::unique_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0));
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@ -1654,7 +1657,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
return;
}
std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
std::unique_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx))
{
return;
@ -1676,7 +1679,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0));
std::unique_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0));
if (!LoadEntityBaseFromNBT(*TNT.get(), a_NBT, a_TagIdx))
{
return;
@ -1698,7 +1701,7 @@ void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadExpOrbFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0));
std::unique_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0));
if (!LoadEntityBaseFromNBT(*ExpOrb.get(), a_NBT, a_TagIdx))
{
return;
@ -1797,7 +1800,7 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT
return;
}
std::auto_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0));
std::unique_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0));
if (!LoadEntityBaseFromNBT(*ItemFrame.get(), a_NBT, a_TagIdx))
{
return;
@ -1822,7 +1825,7 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
std::unique_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx))
{
return;
@ -1892,7 +1895,7 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0), cItem()));
std::unique_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0), cItem()));
if (!LoadProjectileBaseFromNBT(*SplashPotion.get(), a_NBT, a_TagIdx))
{
return;
@ -1916,7 +1919,7 @@ void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedN
void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
std::unique_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx))
{
return;
@ -1932,7 +1935,7 @@ void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
std::unique_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx))
{
return;
@ -1948,7 +1951,7 @@ void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
std::unique_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx))
{
return;
@ -1964,7 +1967,7 @@ void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
std::unique_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx))
{
return;
@ -1980,7 +1983,7 @@ void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
std::unique_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx))
{
return;
@ -1996,7 +1999,7 @@ void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cPar
void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cBat> Monster(new cBat());
std::unique_ptr<cBat> Monster(new cBat());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2016,7 +2019,7 @@ void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cBlaze> Monster(new cBlaze());
std::unique_ptr<cBlaze> Monster(new cBlaze());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2036,7 +2039,7 @@ void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cCaveSpider> Monster(new cCaveSpider());
std::unique_ptr<cCaveSpider> Monster(new cCaveSpider());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2056,7 +2059,7 @@ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cChicken> Monster(new cChicken());
std::unique_ptr<cChicken> Monster(new cChicken());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2076,7 +2079,7 @@ void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cCow> Monster(new cCow());
std::unique_ptr<cCow> Monster(new cCow());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2096,7 +2099,7 @@ void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cCreeper> Monster(new cCreeper());
std::unique_ptr<cCreeper> Monster(new cCreeper());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2116,7 +2119,7 @@ void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cEnderDragon> Monster(new cEnderDragon());
std::unique_ptr<cEnderDragon> Monster(new cEnderDragon());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2136,7 +2139,7 @@ void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNB
void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cEnderman> Monster(new cEnderman());
std::unique_ptr<cEnderman> Monster(new cEnderman());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2156,7 +2159,7 @@ void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cGhast> Monster(new cGhast());
std::unique_ptr<cGhast> Monster(new cGhast());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2176,7 +2179,7 @@ void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cGiant> Monster(new cGiant());
std::unique_ptr<cGiant> Monster(new cGiant());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2196,17 +2199,19 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type");
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type");
int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color");
int StyleIdx = a_NBT.FindChildByName(a_TagIdx, "Style");
if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0)) { return; }
if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0))
{
return;
}
int Type = a_NBT.GetInt(TypeIdx);
int Color = a_NBT.GetInt(ColorIdx);
int Style = a_NBT.GetInt(StyleIdx);
std::auto_ptr<cHorse> Monster(new cHorse(Type, Color, Style, 1));
std::unique_ptr<cHorse> Monster(new cHorse(Type, Color, Style, 1));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
@ -2227,7 +2232,7 @@ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cIronGolem> Monster(new cIronGolem());
std::unique_ptr<cIronGolem> Monster(new cIronGolem());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2256,7 +2261,7 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT
int Size = a_NBT.GetInt(SizeIdx);
std::auto_ptr<cMagmaCube> Monster(new cMagmaCube(Size));
std::unique_ptr<cMagmaCube> Monster(new cMagmaCube(Size));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2276,7 +2281,7 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cMooshroom> Monster(new cMooshroom());
std::unique_ptr<cMooshroom> Monster(new cMooshroom());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2296,7 +2301,7 @@ void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cOcelot> Monster(new cOcelot());
std::unique_ptr<cOcelot> Monster(new cOcelot());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2316,7 +2321,7 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cPig> Monster(new cPig());
std::unique_ptr<cPig> Monster(new cPig());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2343,7 +2348,7 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
Color = (int)a_NBT.GetByte(ColorIdx);
}
std::auto_ptr<cSheep> Monster(new cSheep(Color));
std::unique_ptr<cSheep> Monster(new cSheep(Color));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2369,7 +2374,7 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cSilverfish> Monster(new cSilverfish());
std::unique_ptr<cSilverfish> Monster(new cSilverfish());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2390,12 +2395,14 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "SkeletonType");
if (TypeIdx < 0) { return; }
if (TypeIdx < 0)
{
return;
}
bool Type = ((a_NBT.GetByte(TypeIdx) == 1) ? true : false);
std::auto_ptr<cSkeleton> Monster(new cSkeleton(Type));
std::unique_ptr<cSkeleton> Monster(new cSkeleton(Type));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2424,7 +2431,7 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
int Size = a_NBT.GetInt(SizeIdx);
std::auto_ptr<cSlime> Monster(new cSlime(Size));
std::unique_ptr<cSlime> Monster(new cSlime(Size));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2444,7 +2451,7 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cSnowGolem> Monster(new cSnowGolem());
std::unique_ptr<cSnowGolem> Monster(new cSnowGolem());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2464,7 +2471,7 @@ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cSpider> Monster(new cSpider());
std::unique_ptr<cSpider> Monster(new cSpider());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2484,7 +2491,7 @@ void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cSquid> Monster(new cSquid());
std::unique_ptr<cSquid> Monster(new cSquid());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2505,12 +2512,14 @@ void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Profession");
if (TypeIdx < 0) { return; }
if (TypeIdx < 0)
{
return;
}
int Type = a_NBT.GetInt(TypeIdx);
std::auto_ptr<cVillager> Monster(new cVillager(cVillager::eVillagerType(Type)));
std::unique_ptr<cVillager> Monster(new cVillager(cVillager::eVillagerType(Type)));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2530,7 +2539,7 @@ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT &
void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cWitch> Monster(new cWitch());
std::unique_ptr<cWitch> Monster(new cWitch());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2550,7 +2559,7 @@ void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cWither> Monster(new cWither());
std::unique_ptr<cWither> Monster(new cWither());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2576,7 +2585,7 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cWolf> Monster(new cWolf());
std::unique_ptr<cWolf> Monster(new cWolf());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2630,12 +2639,14 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int IsVillagerIdx = a_NBT.FindChildByName(a_TagIdx, "IsVillager");
if (IsVillagerIdx < 0) { return; }
if (IsVillagerIdx < 0)
{
return;
}
bool IsVillagerZombie = ((a_NBT.GetByte(IsVillagerIdx) == 1) ? true : false);
std::auto_ptr<cZombie> Monster(new cZombie(IsVillagerZombie));
std::unique_ptr<cZombie> Monster(new cZombie(IsVillagerZombie));
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2655,7 +2666,7 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cZombiePigman> Monster(new cZombiePigman());
std::unique_ptr<cZombiePigman> Monster(new cZombiePigman());
if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
{
return;
@ -2912,6 +2923,8 @@ cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_R
bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading)
{
bool writeOutNeeded = false;
if (m_File.IsOpen())
{
// Already open
@ -2937,11 +2950,26 @@ bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading)
if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header))
{
// Cannot read the header - perhaps the file has just been created?
// Try writing a nullptr header (both chunk offsets and timestamps):
// Try writing a nullptr header for chunk offsets:
memset(m_Header, 0, sizeof(m_Header));
writeOutNeeded = true;
}
// Load the TimeStamps:
if (m_File.Read(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps))
{
// Cannot read the time stamps - perhaps the file has just been created?
// Try writing a nullptr header for timestamps:
memset(m_TimeStamps, 0, sizeof(m_TimeStamps));
writeOutNeeded = true;
}
if (writeOutNeeded)
{
m_File.Seek(0);
if (
(m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets
(m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps
(m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Write chunk offsets
(m_File.Write(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps)) // Write chunk timestamps
)
{
LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str());
@ -3072,7 +3100,13 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
);
return false;
}
// Store the header info in the table
m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize);
// Set the modification time
m_TimeStamps[LocalX + 32 * LocalZ] = htonl(static_cast<u_long>(time(nullptr)));
if (m_File.Seek(0) < 0)
{
LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
@ -3083,6 +3117,11 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
if (m_File.Write(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps))
{
LOGWARNING("Cannot save chunk [%d, %d], writing timestamps to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str());
return false;
}
return true;
}

3
src/WorldStorage/WSSAnvil.h Normal file → Executable file
View File

@ -80,7 +80,8 @@ protected:
// First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count
unsigned m_Header[MCA_MAX_CHUNKS];
// Chunk timestamps, following the chunk headers, are unused by MCS
// Chunk timestamps, following the chunk headers
unsigned m_TimeStamps[MCA_MAX_CHUNKS];
/// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number.
unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data);

Some files were not shown because too many files have changed in this diff Show More