diff options
Diffstat (limited to 'src')
103 files changed, 2454 insertions, 1409 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index e9a5ea0c6..2d0300ebf 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -1,8 +1,6 @@ $#include "../Globals.h" -$#include "tolua_base.h" - // Typedefs from Globals.h, so that we don't have to process that file: typedef long long Int64; typedef int Int32; @@ -14,6 +12,7 @@ typedef unsigned short UInt16; $cfile "../ChunkDef.h" +$cfile "../BiomeDef.h" $cfile "../../lib/inifile/iniFile.h" diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 83351591c..00e62fcf6 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -228,6 +228,9 @@ bool cLuaState::PushFunction(const char * a_FunctionName) return false; } + // Push the error handler for lua_pcall() + lua_pushcfunction(m_LuaState, &ReportFnCallErrors); + lua_getglobal(m_LuaState, a_FunctionName); if (!lua_isfunction(m_LuaState, -1)) { @@ -249,6 +252,9 @@ bool cLuaState::PushFunction(int a_FnRef) ASSERT(IsValid()); ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + // Push the error handler for lua_pcall() + lua_pushcfunction(m_LuaState, &ReportFnCallErrors); + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() if (!lua_isfunction(m_LuaState, -1)) { @@ -264,27 +270,29 @@ bool cLuaState::PushFunction(int a_FnRef) -bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName) +bool cLuaState::PushFunction(const cTableRef & a_TableRef) { ASSERT(IsValid()); ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack - - lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref + + // Push the error handler for lua_pcall() + lua_pushcfunction(m_LuaState, &ReportFnCallErrors); + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef.GetTableRef()); // Get the table ref if (!lua_istable(m_LuaState, -1)) { // Not a table, bail out lua_pop(m_LuaState, 1); return false; } - lua_getfield(m_LuaState, -1, a_FnName); + lua_getfield(m_LuaState, -1, a_TableRef.GetFnName()); if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1)) { // Not a valid function, bail out lua_pop(m_LuaState, 2); return false; } - lua_remove(m_LuaState, -2); // Remove the table ref from the stack - m_CurrentFunctionName = "<table_callback>"; + Printf(m_CurrentFunctionName, "<table-callback %s>", a_TableRef.GetFnName()); m_NumCurrentFunctionArgs = 0; return true; } @@ -624,18 +632,6 @@ void cLuaState::Push(cTNTEntity * a_TNTEntity) -void cLuaState::Push(cCreeper * a_Creeper) -{ - ASSERT(IsValid()); - - tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper"); - m_NumCurrentFunctionArgs += 1; -} - - - - - void cLuaState::Push(Vector3i * a_Vector) { ASSERT(IsValid()); @@ -732,11 +728,13 @@ void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal) bool cLuaState::CallFunction(int a_NumResults) { ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first - ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); + ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call + ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler - int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0); - if (ReportErrors(s)) + int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, -m_NumCurrentFunctionArgs - 2); + if (s != 0) { + // The error has already been printed together with the stacktrace LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str()); m_NumCurrentFunctionArgs = -1; m_CurrentFunctionName.clear(); @@ -964,15 +962,24 @@ bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status) void cLuaState::LogStackTrace(void) { + LogStackTrace(m_LuaState); +} + + + + + +void cLuaState::LogStackTrace(lua_State * a_LuaState) +{ LOGWARNING("Stack trace:"); lua_Debug entry; int depth = 0; - while (lua_getstack(m_LuaState, depth, &entry)) + while (lua_getstack(a_LuaState, depth, &entry)) { - int status = lua_getinfo(m_LuaState, "Sln", &entry); + int status = lua_getinfo(a_LuaState, "Sln", &entry); assert(status); - LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); + LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)"); depth++; } LOGWARNING("Stack trace end"); @@ -1005,6 +1012,17 @@ AString cLuaState::GetTypeText(int a_StackPos) +int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) +{ + LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); + LogStackTrace(a_LuaState); + return 1; // We left the error message on the stack as the return value +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 796559b6f..414e5e4b2 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -85,6 +85,23 @@ public: } ; + /** Used for calling functions stored in a reference-stored table */ + class cTableRef + { + int m_TableRef; + const char * m_FnName; + public: + cTableRef(int a_TableRef, const char * a_FnName) : + m_TableRef(a_TableRef), + m_FnName(a_FnName) + { + } + + int GetTableRef(void) const { return m_TableRef; } + const char * GetFnName(void) const { return m_FnName; } + } ; + + /// A dummy class that's used only to delimit function args from return values for cLuaState::Call() class cRet { @@ -133,24 +150,6 @@ public: /// Returns true if a_FunctionName is a valid Lua function that can be called bool HasFunction(const char * a_FunctionName); - /** Pushes the function of the specified name onto the stack. - Returns true if successful. Logs a warning on failure (incl. m_SubsystemName) - */ - bool PushFunction(const char * a_FunctionName); - - /** Pushes a function that has been saved into the global registry, identified by a_FnRef. - Returns true if successful. Logs a warning on failure - */ - bool PushFunction(int a_FnRef); - - /** Pushes a function that is stored in a table ref. - Returns true if successful, false on failure. Doesn't log failure. - */ - bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName); - - /// Pushes a usertype of the specified class type onto the stack - void PushUserType(void * a_Object, const char * a_Type); - // Push a value onto the stack void Push(const AString & a_String); void Push(const AStringVector & a_Vector); @@ -178,12 +177,11 @@ public: void Push(cWebAdmin * a_WebAdmin); void Push(const HTTPTemplateRequest * a_Request); void Push(cTNTEntity * a_TNTEntity); - void Push(cCreeper * a_Creeper); void Push(Vector3i * a_Vector); void Push(void * a_Ptr); void Push(cHopperEntity * a_Hopper); void Push(cBlockEntity * a_BlockEntity); - + /// Call any 0-param 0-return Lua function in a single line: template <typename FnT> bool Call(FnT a_FnName) @@ -802,25 +800,6 @@ public: } - /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, bool & a_ReturnedVal); - - /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, AString & a_ReturnedVal); - - /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, int & a_ReturnedVal); - - /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged - void GetReturn(int a_StackPos, double & a_ReturnedVal); - - /** - Calls the function that has been pushed onto the stack by PushFunction(), - with arguments pushed by PushXXX(). - Returns true if successful, logs a warning on failure. - */ - bool CallFunction(int a_NumReturnValues); - /// Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions bool CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam = -1); @@ -848,6 +827,9 @@ public: /// Logs all items in the current stack trace to the server console void LogStackTrace(void); + /// Logs all items in the current stack trace to the server console + static void LogStackTrace(lua_State * a_LuaState); + /// Returns the type of the item on the specified position in the stack AString GetTypeText(int a_StackPos); @@ -867,6 +849,47 @@ protected: /// Number of arguments currently pushed (for the Push / Call chain) int m_NumCurrentFunctionArgs; + + + /** Pushes the function of the specified name onto the stack. + Returns true if successful. Logs a warning on failure (incl. m_SubsystemName) + */ + bool PushFunction(const char * a_FunctionName); + + /** Pushes a function that has been saved into the global registry, identified by a_FnRef. + Returns true if successful. Logs a warning on failure + */ + bool PushFunction(int a_FnRef); + + /** Pushes a function that is stored in a referenced table by name + Returns true if successful. Logs a warning on failure + */ + bool PushFunction(const cTableRef & a_TableRef); + + /// Pushes a usertype of the specified class type onto the stack + void PushUserType(void * a_Object, const char * a_Type); + + /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, bool & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, AString & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, int & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, double & a_ReturnedVal); + + /** + Calls the function that has been pushed onto the stack by PushFunction(), + with arguments pushed by PushXXX(). + Returns true if successful, logs a warning on failure. + */ + bool CallFunction(int a_NumReturnValues); + + /** Used as the error reporting function for function calls */ + static int ReportFnCallErrors(lua_State * a_LuaState); } ; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 6221727c4..ebee2d697 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -984,6 +984,73 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) +class cLuaScheduledWorldTask : + public cWorld::cScheduledTask +{ +public: + cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef, int a_Ticks) : + cScheduledTask(a_Ticks), + m_Plugin(a_Plugin), + m_FnRef(a_FnRef) + { + } + +protected: + cPluginLua & m_Plugin; + int m_FnRef; + + // cWorld::cTask overrides: + virtual void Run(cWorld & a_World) override + { + m_Plugin.Call(m_FnRef, &a_World); + } +}; + + + + + +static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) +{ + // Binding for cWorld::ScheduleTask + // Params: function, Ticks + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!lua_isfunction(tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + int Ticks = (int) tolua_tonumber (tolua_S, 3, 0); + + self->ScheduleTask(new cLuaScheduledWorldTask(*Plugin, FnRef, Ticks)); + return 0; +} + + + + + static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) { cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); @@ -991,7 +1058,6 @@ static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); lua_newtable(tolua_S); - int newTable = lua_gettop(tolua_S); int index = 1; cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); while (iter != AllPlugins.end()) @@ -1883,7 +1949,6 @@ static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) const cWebPlugin::TabNameList & TabNames = self->GetTabNames(); lua_newtable(tolua_S); - int newTable = lua_gettop(tolua_S); int index = 1; cWebPlugin::TabNameList::const_iterator iter = TabNames.begin(); while(iter != TabNames.end()) @@ -1904,6 +1969,35 @@ static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) +static int tolua_cClientHandle_SendPluginMessage(lua_State * L) +{ + cLuaState S(L); + if ( + !S.CheckParamUserType(1, "cClientHandle") || + !S.CheckParamString(2, 3) || + !S.CheckParamEnd(4) + ) + { + return 0; + } + cClientHandle * Client = (cClientHandle *)tolua_tousertype(L, 1, NULL); + if (Client == NULL) + { + LOGWARNING("ClientHandle is nil in cClientHandle:SendPluginMessage()"); + S.LogStackTrace(); + return 0; + } + AString Channel, Message; + Channel.assign(lua_tostring(L, 2), lua_strlen(L, 2)); + Message.assign(lua_tostring(L, 3), lua_strlen(L, 3)); + Client->SendPluginMessage(Channel, Message); + return 0; +} + + + + + static int Lua_ItemGrid_GetSlotCoords(lua_State * L) { tolua_Error tolua_err; @@ -1953,118 +2047,72 @@ public: virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock")) + bool res = false; + if (!m_LuaState.Call( + cLuaState::cTableRef(m_TableRef, "OnNextBlock"), + a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_EntryFace, + cLuaState::Return, res + )) { // No such function in the table, skip the callback return false; } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - m_LuaState.Push(a_BlockType); - m_LuaState.Push(a_BlockMeta); - m_LuaState.Push(a_EntryFace); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); return res; } virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData")) + bool res = false; + if (!m_LuaState.Call( + cLuaState::cTableRef(m_TableRef, "OnNextBlockNoData"), + a_BlockX, a_BlockY, a_BlockZ, a_EntryFace, + cLuaState::Return, res + )) { // No such function in the table, skip the callback return false; } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - m_LuaState.Push(a_EntryFace); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); return res; } virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnOutOfWorld")) + bool res = false; + if (!m_LuaState.Call( + cLuaState::cTableRef(m_TableRef, "OnOutOfWorld"), + a_BlockX, a_BlockY, a_BlockZ, + cLuaState::Return, res + )) { // No such function in the table, skip the callback return false; } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); return res; } virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnIntoWorld")) + bool res = false; + if (!m_LuaState.Call( + cLuaState::cTableRef(m_TableRef, "OnIntoWorld"), + a_BlockX, a_BlockY, a_BlockZ, + cLuaState::Return, res + )) { // No such function in the table, skip the callback return false; } - m_LuaState.Push(a_BlockX); - m_LuaState.Push(a_BlockY); - m_LuaState.Push(a_BlockZ); - if (!m_LuaState.CallFunction(1)) - { - return false; - } - bool res = false; - if (lua_isboolean(m_LuaState, -1)) - { - res = (lua_toboolean(m_LuaState, -1) != 0); - } - lua_pop(m_LuaState, 1); return res; } virtual void OnNoMoreHits(void) override { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoMoreHits")) - { - // No such function in the table, skip the callback - return; - } - m_LuaState.CallFunction(0); + m_LuaState.Call(cLuaState::cTableRef(m_TableRef, "OnNoMoreHits")); } virtual void OnNoChunk(void) override { - if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoChunk")) - { - // No such function in the table, skip the callback - return; - } - m_LuaState.CallFunction(0); + m_LuaState.Call(cLuaState::cTableRef(m_TableRef, "OnNoChunk")); } protected: @@ -2237,6 +2285,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); + tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask); tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); @@ -2292,6 +2341,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cClientHandle"); tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE); tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE); + tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cItemGrid"); diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 209842e55..4c4664815 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -90,6 +90,8 @@ bool cPluginLua::Initialize(void) // Load all files for this plugin, and execute them AStringVector Files = cFile::GetFolderContents(PluginPath.c_str()); + + int numFiles = 0; for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) { if (itr->rfind(".lua") == AString::npos) @@ -101,9 +103,20 @@ bool cPluginLua::Initialize(void) { Close(); return false; + } + else + { + numFiles++; } } // for itr - Files[] + if (numFiles == 0) + { + LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str()); + Close(); + return false; + } + // Call intialize function bool res = false; if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res)) @@ -423,7 +436,7 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & { case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esMonster: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cMonster *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 68e6aea33..24bb914d1 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -169,9 +169,9 @@ void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); - a_SettingsIni.SetValue("Plugins", "Plugin", "Core"); - a_SettingsIni.SetValue("Plugins", "Plugin", "TransAPI"); - a_SettingsIni.SetValue("Plugins", "Plugin", "ChatLog"); + a_SettingsIni.AddValue("Plugins", "Plugin", "Core"); + a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI"); + a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog"); } diff --git a/src/Bindings/tolua++.h b/src/Bindings/tolua++.h index 4dfd06318..73e65b746 100644 --- a/src/Bindings/tolua++.h +++ b/src/Bindings/tolua++.h @@ -2,12 +2,18 @@ // tolua++.h // Redirection file, needed because ToLua++ generates the Bindings.cpp file with >> #include "tolua++.h" << +// Only used from Bindings.cpp #include "tolua++/include/tolua++.h" +#ifdef _MSC_VER + // Disable specific warnings for the generated Bindings.cpp file: + #pragma warning(disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning) +#endif // _MSC_VER + diff --git a/src/Bindings/tolua_base.h b/src/Bindings/tolua_base.h deleted file mode 100644 index 6a76f97b1..000000000 --- a/src/Bindings/tolua_base.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef TOLUA_BASE_H -#define TOLUA_BASE_H - -#pragma warning(disable:4800) // This file is ONLY included by Bindings.cpp and it throws lots of C4800 warnings - -#include "tolua++/include/tolua++.h" - - - - - -class ToluaBase { - - int lua_instance; - -protected: - - lua_State* lua_state; - - void lua_stacktrace(lua_State* L) const - { - lua_Debug entry; - int depth = 0; - - while (lua_getstack(L, depth, &entry)) - { - lua_getinfo(L, "Sln", &entry); - - LOGERROR("%s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); - depth++; - } - } - - - bool report_errors(int status) const - { - if ( status!=0 ) - { - const char* s = lua_tostring(lua_state, -1); - LOGERROR("-- %s", s ); - //lua_pop(lua_state, 1); - LOGERROR("Stack:"); - lua_stacktrace( lua_state ); - return true; - } - return false; - } - - bool push_method(const char* name, lua_CFunction f) const { - - if (!lua_state) return false; - - lua_getref(lua_state, lua_instance); - lua_pushstring(lua_state, name); - //LOGINFO("1. push_method() Stack size: %i", lua_gettop( lua_state ) ); - lua_gettable(lua_state, -2); - //LOGINFO("2. push_method() Stack size: %i", lua_gettop( lua_state ) ); - - if (lua_isnil(lua_state, -1)) { - - // pop the table - lua_pop(lua_state, 2); - return false; - - } else { - - if (f) { - if (lua_iscfunction(lua_state, -1)) { - lua_pop(lua_state, 2); - return false; - }; - /* // not for now - lua_pushcfunction(lua_state, f); - if (lua_rawequal(lua_state, -1, -2)) { - - // avoid recursion, pop both functions and the table - lua_pop(lua_state, 3); - return false; - }; - - // pop f - lua_pop(lua_state, 1); - */ - }; - - // swap table with function - lua_insert(lua_state, -2); - }; - - return true; - }; - - void dbcall(lua_State* L, int nargs, int nresults) const { - - // using lua_call for now - int s = lua_pcall(L, nargs, nresults, 0); - report_errors( s ); - }; -public: - - int GetInstance() { return lua_instance; } - lua_State* GetLuaState() { return lua_state; } - - void tolua__set_instance(lua_State* L, lua_Object lo) { - - lua_state = L; - - lua_pushvalue(L, lo); - lua_instance = lua_ref(lua_state, 1); - }; - - ToluaBase() { - - lua_state = NULL; - }; - - ~ToluaBase() { - - if (lua_state) { - - lua_unref(lua_state, lua_instance); - }; - }; -}; - -#endif - - diff --git a/src/Bindings/virtual_method_hooks.lua b/src/Bindings/virtual_method_hooks.lua index 15ff1d7f8..c610d424f 100644 --- a/src/Bindings/virtual_method_hooks.lua +++ b/src/Bindings/virtual_method_hooks.lua @@ -504,3 +504,11 @@ end + +function post_output_hook() + print("Bindings have been generated.") +end + + + + diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp new file mode 100644 index 000000000..89a1cdefb --- /dev/null +++ b/src/BiomeDef.cpp @@ -0,0 +1,131 @@ + +// BiomeDef.cpp + +// Implements biome helper functions + +#include "Globals.h" +#include "BiomeDef.h" + + +EMCSBiome StringToBiome(const AString & a_BiomeString) +{ + // If it is a number, return it: + int res = atoi(a_BiomeString.c_str()); + if ((res != 0) || (a_BiomeString.compare("0") == 0)) + { + // It was a valid number + return (EMCSBiome)res; + } + + // Convert using the built-in map: + static struct { + EMCSBiome m_Biome; + const char * m_String; + } BiomeMap[] = + { + {biOcean, "Ocean"} , + {biPlains, "Plains"}, + {biDesert, "Desert"}, + {biExtremeHills, "ExtremeHills"}, + {biForest, "Forest"}, + {biTaiga, "Taiga"}, + {biSwampland, "Swampland"}, + {biRiver, "River"}, + {biNether, "Hell"}, + {biNether, "Nether"}, + {biEnd, "Sky"}, + {biEnd, "End"}, + {biFrozenOcean, "FrozenOcean"}, + {biFrozenRiver, "FrozenRiver"}, + {biIcePlains, "IcePlains"}, + {biIcePlains, "Tundra"}, + {biIceMountains, "IceMountains"}, + {biMushroomIsland, "MushroomIsland"}, + {biMushroomShore, "MushroomShore"}, + {biBeach, "Beach"}, + {biDesertHills, "DesertHills"}, + {biForestHills, "ForestHills"}, + {biTaigaHills, "TaigaHills"}, + {biExtremeHillsEdge, "ExtremeHillsEdge"}, + {biJungle, "Jungle"}, + {biJungleHills, "JungleHills"}, + + // Release 1.7 biomes: + {biJungleEdge, "JungleEdge"}, + {biDeepOcean, "DeepOcean"}, + {biStoneBeach, "StoneBeach"}, + {biColdBeach, "ColdBeach"}, + {biBirchForest, "BirchForest"}, + {biBirchForestHills, "BirchForestHills"}, + {biRoofedForest, "RoofedForest"}, + {biColdTaiga, "ColdTaiga"}, + {biColdTaigaHills, "ColdTaigaHills"}, + {biMegaTaiga, "MegaTaiga"}, + {biMegaTaigaHills, "MegaTaigaHills"}, + {biExtremeHillsPlus, "ExtremeHillsPlus"}, + {biSavanna, "Savanna"}, + {biSavannaPlateau, "SavannaPlateau"}, + {biMesa, "Mesa"}, + {biMesaPlateauF, "MesaPlateauF"}, + {biMesaPlateau, "MesaPlateau"}, + + // Release 1.7 variants: + {biSunflowerPlains, "SunflowerPlains"}, + {biDesertM, "DesertM"}, + {biExtremeHillsM, "ExtremeHillsM"}, + {biFlowerForest, "FlowerForest"}, + {biTaigaM, "TaigaM"}, + {biSwamplandM, "SwamplandM"}, + {biIcePlainsSpikes, "IcePlainsSpikes"}, + {biJungleM, "JungleM"}, + {biJungleEdgeM, "JungleEdgeM"}, + {biBirchForestM, "BirchForestM"}, + {biBirchForestHillsM, "BirchForestHillsM"}, + {biRoofedForestM, "RoofedForestM"}, + {biColdTaigaM, "ColdTaigaM"}, + {biMegaSpruceTaiga, "MegaSpruceTaiga"}, + {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, + {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, + {biSavannaM, "SavannaM"}, + {biSavannaPlateauM, "SavannaPlateauM"}, + {biMesaBryce, "MesaBryce"}, + {biMesaPlateauFM, "MesaPlateauFM"}, + {biMesaPlateauM, "MesaPlateauM"}, + } ; + + for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++) + { + if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0) + { + return BiomeMap[i].m_Biome; + } + } // for i - BiomeMap[] + return (EMCSBiome)-1; +} + + + + + +bool IsBiomeNoDownfall(EMCSBiome a_Biome) +{ + switch (a_Biome) + { + case biDesert: + case biDesertHills: + case biDesertM: + case biSavanna: + case biSavannaM: + case biSavannaPlateau: + case biSavannaPlateauM: + case biNether: + case biEnd: + { + return true; + } + default: + { + return false; + } + } +} diff --git a/src/BiomeDef.h b/src/BiomeDef.h new file mode 100644 index 000000000..df1e387f0 --- /dev/null +++ b/src/BiomeDef.h @@ -0,0 +1,107 @@ + +// BiomeDef.h + +// Defines relevant information and methods related to biomes + + + + + +#pragma once + + + + + +// tolua_begin +/** Biome IDs +The first batch corresponds to the clientside biomes, used by MineCraft. +BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client +*/ +enum EMCSBiome +{ + biOcean = 0, + biPlains = 1, + biDesert = 2, + biExtremeHills = 3, + biForest = 4, + biTaiga = 5, + biSwampland = 6, + biRiver = 7, + biHell = 8, // same as Nether + biNether = 8, + biSky = 9, // same as biEnd + biEnd = 9, + biFrozenOcean = 10, + biFrozenRiver = 11, + biIcePlains = 12, + biTundra = 12, // same as Ice Plains + biIceMountains = 13, + biMushroomIsland = 14, + biMushroomShore = 15, + biBeach = 16, + biDesertHills = 17, + biForestHills = 18, + biTaigaHills = 19, + biExtremeHillsEdge = 20, + biJungle = 21, + biJungleHills = 22, + + // Release 1.7 biomes: + biJungleEdge = 23, + biDeepOcean = 24, + biStoneBeach = 25, + biColdBeach = 26, + biBirchForest = 27, + biBirchForestHills = 28, + biRoofedForest = 29, + biColdTaiga = 30, + biColdTaigaHills = 31, + biMegaTaiga = 32, + biMegaTaigaHills = 33, + biExtremeHillsPlus = 34, + biSavanna = 35, + biSavannaPlateau = 36, + biMesa = 37, + biMesaPlateauF = 38, + biMesaPlateau = 39, + + // Automatically capture the maximum consecutive biome value into biMaxBiome: + biNumBiomes, // True number of biomes, since they are zero-based + biMaxBiome = biNumBiomes - 1, // The maximum biome value + + // Add this number to the biomes to get the variant + biVariant = 128, + + // Release 1.7 biome variants: + biSunflowerPlains = 129, + biDesertM = 130, + biExtremeHillsM = 131, + biFlowerForest = 132, + biTaigaM = 133, + biSwamplandM = 134, + biIcePlainsSpikes = 140, + biJungleM = 149, + biJungleEdgeM = 151, + biBirchForestM = 155, + biBirchForestHillsM = 156, + biRoofedForestM = 157, + biColdTaigaM = 158, + biMegaSpruceTaiga = 160, + biMegaSpruceTaigaHills = 161, + biExtremeHillsPlusM = 162, + biSavannaM = 163, + biSavannaPlateauM = 164, + biMesaBryce = 165, + biMesaPlateauFM = 166, + biMesaPlateauM = 167, +} ; + +/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure. +extern EMCSBiome StringToBiome(const AString & a_BiomeString); + +/// Returns true if the biome has no downfall - deserts and savannas +extern bool IsBiomeNoDownfall(EMCSBiome a_Biome); + + +// tolua_end diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index f15553968..c6643bcff 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -307,7 +307,7 @@ void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) /// Updates the current recipe, based on the current input void cFurnaceEntity::UpdateInput(void) { - if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput)) + if (!m_Contents.GetSlot(fsInput).IsEqual(m_LastInput)) { // The input is different from what we had before, reset the cooking time m_TimeCooked = 0; @@ -417,7 +417,7 @@ bool cFurnaceEntity::CanCookInputToOutput(void) const return true; } - if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out)) + if (!m_Contents.GetSlot(fsOutput).IsEqual(*m_CurrentRecipe->Out)) { // The output slot is blocked with something that cannot be stacked with the recipe's output return false; diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index eac59e74d..2255cad64 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -407,7 +407,7 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl m_Contents.SetSlot(i, One); return true; } - else if (m_Contents.GetSlot(i).IsStackableWith(One)) + else if (m_Contents.GetSlot(i).IsEqual(One)) { if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) { @@ -544,7 +544,7 @@ bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstS } for (int i = 0; i < ContentsWidth * ContentsHeight; i++) { - if (m_Contents.GetSlot(i).IsStackableWith(DestSlot)) + if (m_Contents.GetSlot(i).IsEqual(DestSlot)) { if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) { diff --git a/src/BlockID.cpp b/src/BlockID.cpp index 05d4c6595..095865d07 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -20,7 +20,7 @@ bool g_BlockPistonBreakable[256]; bool g_BlockIsSnowable[256]; bool g_BlockRequiresSpecialTool[256]; bool g_BlockIsSolid[256]; -bool g_BlockIsTorchPlaceable[256]; +bool g_BlockFullyOccupiesVoxel[256]; @@ -267,106 +267,6 @@ AString ItemToFullString(const cItem & a_Item) -EMCSBiome StringToBiome(const AString & a_BiomeString) -{ - // If it is a number, return it: - int res = atoi(a_BiomeString.c_str()); - if ((res != 0) || (a_BiomeString.compare("0") == 0)) - { - // It was a valid number - return (EMCSBiome)res; - } - - // Convert using the built-in map: - static struct { - EMCSBiome m_Biome; - const char * m_String; - } BiomeMap[] = - { - {biOcean, "Ocean"} , - {biPlains, "Plains"}, - {biDesert, "Desert"}, - {biExtremeHills, "ExtremeHills"}, - {biForest, "Forest"}, - {biTaiga, "Taiga"}, - {biSwampland, "Swampland"}, - {biRiver, "River"}, - {biNether, "Hell"}, - {biNether, "Nether"}, - {biEnd, "Sky"}, - {biEnd, "End"}, - {biFrozenOcean, "FrozenOcean"}, - {biFrozenRiver, "FrozenRiver"}, - {biIcePlains, "IcePlains"}, - {biIcePlains, "Tundra"}, - {biIceMountains, "IceMountains"}, - {biMushroomIsland, "MushroomIsland"}, - {biMushroomShore, "MushroomShore"}, - {biBeach, "Beach"}, - {biDesertHills, "DesertHills"}, - {biForestHills, "ForestHills"}, - {biTaigaHills, "TaigaHills"}, - {biExtremeHillsEdge, "ExtremeHillsEdge"}, - {biJungle, "Jungle"}, - {biJungleHills, "JungleHills"}, - - // Release 1.7 biomes: - {biJungleEdge, "JungleEdge"}, - {biDeepOcean, "DeepOcean"}, - {biStoneBeach, "StoneBeach"}, - {biColdBeach, "ColdBeach"}, - {biBirchForest, "BirchForest"}, - {biBirchForestHills, "BirchForestHills"}, - {biRoofedForest, "RoofedForest"}, - {biColdTaiga, "ColdTaiga"}, - {biColdTaigaHills, "ColdTaigaHills"}, - {biMegaTaiga, "MegaTaiga"}, - {biMegaTaigaHills, "MegaTaigaHills"}, - {biExtremeHillsPlus, "ExtremeHillsPlus"}, - {biSavanna, "Savanna"}, - {biSavannaPlateau, "SavannaPlateau"}, - {biMesa, "Mesa"}, - {biMesaPlateauF, "MesaPlateauF"}, - {biMesaPlateau, "MesaPlateau"}, - - // Release 1.7 variants: - {biSunflowerPlains, "SunflowerPlains"}, - {biDesertM, "DesertM"}, - {biExtremeHillsM, "ExtremeHillsM"}, - {biFlowerForest, "FlowerForest"}, - {biTaigaM, "TaigaM"}, - {biSwamplandM, "SwamplandM"}, - {biIcePlainsSpikes, "IcePlainsSpikes"}, - {biJungleM, "JungleM"}, - {biJungleEdgeM, "JungleEdgeM"}, - {biBirchForestM, "BirchForestM"}, - {biBirchForestHillsM, "BirchForestHillsM"}, - {biRoofedForestM, "RoofedForestM"}, - {biColdTaigaM, "ColdTaigaM"}, - {biMegaSpruceTaiga, "MegaSpruceTaiga"}, - {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, - {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, - {biSavannaM, "SavannaM"}, - {biSavannaPlateauM, "SavannaPlateauM"}, - {biMesaBryce, "MesaBryce"}, - {biMesaPlateauFM, "MesaPlateauFM"}, - {biMesaPlateauM, "MesaPlateauM"}, - } ; - - for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++) - { - if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0) - { - return BiomeMap[i].m_Biome; - } - } // for i - BiomeMap[] - return (EMCSBiome)-1; -} - - - - - int StringToMobType(const AString & a_MobString) { static struct { @@ -591,7 +491,7 @@ public: memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent)); memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig)); memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable)); - memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable)); + memset(g_BlockFullyOccupiesVoxel, 0x00, sizeof(g_BlockFullyOccupiesVoxel)); // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 for (size_t i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++) @@ -867,6 +767,7 @@ public: g_BlockIsSolid[E_BLOCK_MELON_STEM] = false; g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false; g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false; + g_BlockIsSolid[E_BLOCK_POWERED_RAIL] = false; g_BlockIsSolid[E_BLOCK_RAIL] = false; g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false; g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_ON] = false; @@ -891,67 +792,67 @@ public: g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false; // Torch placeable blocks: - g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true; - g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true; - g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true; - g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true; - g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true; - g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true; - g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true; - g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true; - g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true; - g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true; - g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true; - g_BlockIsTorchPlaceable[E_BLOCK_PACKED_ICE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true; - g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true; - g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true; - g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true; - g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true; - g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true; - g_BlockIsTorchPlaceable[E_BLOCK_STONE_BRICKS] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_BEDROCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_BLOCK_OF_COAL] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_BLOCK_OF_REDSTONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_BOOKCASE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_BRICK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_CLAY] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_COAL_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_COBBLESTONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_COMMAND_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_CRAFTING_TABLE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DIAMOND_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DIAMOND_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DIRT] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DISPENSER] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DOUBLE_STONE_SLAB] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DOUBLE_WOODEN_SLAB] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_DROPPER] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_EMERALD_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_EMERALD_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_END_STONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_FURNACE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_GLOWSTONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_GOLD_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_GOLD_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_GRASS] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_GRAVEL] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_HARDENED_CLAY] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_HAY_BALE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_HUGE_BROWN_MUSHROOM] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_HUGE_RED_MUSHROOM] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_IRON_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_IRON_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_JACK_O_LANTERN] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_JUKEBOX] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_LAPIS_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_LAPIS_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_LOG] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_MELON] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_MOSSY_COBBLESTONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_MYCELIUM] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_NETHERRACK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_NETHER_BRICK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_NETHER_QUARTZ_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_NOTE_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_OBSIDIAN] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_PACKED_ICE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_PLANKS] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_PUMPKIN] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_QUARTZ_BLOCK] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_LAMP_OFF] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_LAMP_ON] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_ORE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_ORE_GLOWING] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_SANDSTONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_SAND] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_SILVERFISH_EGG] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_SPONGE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_STAINED_CLAY] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_WOOL] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_STONE] = true; + g_BlockFullyOccupiesVoxel[E_BLOCK_STONE_BRICKS] = true; } } BlockPropertiesInitializer; diff --git a/src/BlockID.h b/src/BlockID.h index 288719ccf..b31c589b9 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -876,9 +876,6 @@ extern AString ItemTypeToString(short a_ItemType); /// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string. extern AString ItemToFullString(const cItem & a_Item); -/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure. -extern EMCSBiome StringToBiome(const AString & a_BiomeString); - /// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT) extern int StringToMobType(const AString & a_MobString); @@ -909,7 +906,7 @@ extern bool g_BlockPistonBreakable[256]; extern bool g_BlockIsSnowable[256]; extern bool g_BlockRequiresSpecialTool[256]; extern bool g_BlockIsSolid[256]; -extern bool g_BlockIsTorchPlaceable[256]; +extern bool g_BlockFullyOccupiesVoxel[256]; diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h index 488c58ac5..88f7c4b32 100644 --- a/src/Blocks/BlockChest.h +++ b/src/Blocks/BlockChest.h @@ -42,13 +42,13 @@ public: { return false; } - double rot = a_Player->GetRotation(); + double yaw = a_Player->GetYaw(); if ( (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) ) { - a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + a_BlockMeta = ((yaw >= -90) && (yaw < 90)) ? 2 : 3; return true; } if ( @@ -56,12 +56,12 @@ public: (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) ) { - a_BlockMeta = (rot < 0) ? 4 : 5; + a_BlockMeta = (yaw < 0) ? 4 : 5; return true; } // Single chest, get meta from rotation only - a_BlockMeta = RotationToMetaData(rot); + a_BlockMeta = RotationToMetaData(yaw); return true; } @@ -80,7 +80,7 @@ public: return; } - double rot = a_Player->GetRotation(); + double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw // Choose meta from player rotation, choose only between 2 or 3 NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; if ( diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h index e7e18bac9..5a8e54eda 100644 --- a/src/Blocks/BlockComparator.h +++ b/src/Blocks/BlockComparator.h @@ -53,7 +53,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetYaw()); return true; } diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 03a79d47d..1e86446dd 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -42,7 +42,7 @@ public: } a_BlockType = m_BlockType; - a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + a_BlockMeta = PlayerYawToMetaData(a_Player->GetYaw()); return true; } diff --git a/src/Blocks/BlockDropSpenser.h b/src/Blocks/BlockDropSpenser.h index b7f20825d..ce9cc0c5d 100644 --- a/src/Blocks/BlockDropSpenser.h +++ b/src/Blocks/BlockDropSpenser.h @@ -31,7 +31,7 @@ public: a_BlockType = m_BlockType; // FIXME: Do not use cPiston class for dispenser placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch()); return true; } } ; diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h index 50d8e38e0..b648248d0 100644 --- a/src/Blocks/BlockEnderchest.h +++ b/src/Blocks/BlockEnderchest.h @@ -30,7 +30,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = RotationToMetaData(a_Player->GetYaw()); return true; } diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h index 6423a7cb0..779f12ee1 100644 --- a/src/Blocks/BlockFenceGate.h +++ b/src/Blocks/BlockFenceGate.h @@ -25,7 +25,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + a_BlockMeta = PlayerYawToMetaData(a_Player->GetYaw()); return true; } @@ -33,7 +33,7 @@ public: virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override { NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation()); + NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetYaw()); OldMetaData ^= 4; // Toggle the gate if ((OldMetaData & 1) == (NewMetaData & 1)) { diff --git a/src/Blocks/BlockFurnace.h b/src/Blocks/BlockFurnace.h index 5b067d7b7..693eac331 100644 --- a/src/Blocks/BlockFurnace.h +++ b/src/Blocks/BlockFurnace.h @@ -35,7 +35,7 @@ public: a_BlockType = m_BlockType; // FIXME: Do not use cPiston class for furnace placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), 0); return true; } diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h index 5f0d95dee..6c198efc4 100644 --- a/src/Blocks/BlockGlowstone.h +++ b/src/Blocks/BlockGlowstone.h @@ -20,8 +20,8 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { // Reset meta to 0 - // TODO: More drops? - a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0)); + MTRand r1; + a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, (char)(2 + r1.randInt(2)), 0)); } } ; diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h index 15fe2071c..5b0c89127 100644 --- a/src/Blocks/BlockLever.h +++ b/src/Blocks/BlockLever.h @@ -20,7 +20,7 @@ public: // Flip the ON bit on/off using the XOR bitwise operation NIBBLETYPE Meta = (a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08); - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER, Meta); // SetMeta doesn't work for unpowering levers, so setblock a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); } diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp index 3e1ca1d15..88259d96e 100644 --- a/src/Blocks/BlockPiston.cpp +++ b/src/Blocks/BlockPiston.cpp @@ -60,7 +60,7 @@ bool cBlockPistonHandler::GetPlacementBlockTypeMeta( ) { a_BlockType = m_BlockType; - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch()); return true; } diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 724241935..5187f24eb 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -86,7 +86,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + a_BlockMeta = PlayerYawToMetaData(a_Player->GetYaw()); return true; } diff --git a/src/Blocks/BlockRedstone.h b/src/Blocks/BlockRedstone.h index 1bd9995f2..5ffb77fb6 100644 --- a/src/Blocks/BlockRedstone.h +++ b/src/Blocks/BlockRedstone.h @@ -20,7 +20,7 @@ public: virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { - return ((a_RelY > 0) && g_BlockIsTorchPlaceable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); + return ((a_RelY > 0) && g_BlockFullyOccupiesVoxel[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); } diff --git a/src/Blocks/BlockRedstoneRepeater.h b/src/Blocks/BlockRedstoneRepeater.h index 1fcddd4f8..713664659 100644 --- a/src/Blocks/BlockRedstoneRepeater.h +++ b/src/Blocks/BlockRedstoneRepeater.h @@ -25,7 +25,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetYaw()); return true; } diff --git a/src/Blocks/BlockStairs.h b/src/Blocks/BlockStairs.h index fa378e4b5..e463612f5 100644 --- a/src/Blocks/BlockStairs.h +++ b/src/Blocks/BlockStairs.h @@ -26,7 +26,7 @@ public: ) override { a_BlockType = m_BlockType; - a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = RotationToMetaData(a_Player->GetYaw()); switch (a_BlockFace) { case BLOCK_FACE_TOP: break; diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h index 9e543dfd7..faba9c4f5 100644 --- a/src/Blocks/BlockTorch.h +++ b/src/Blocks/BlockTorch.h @@ -98,7 +98,7 @@ public: static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace) { - if ( !g_BlockIsTorchPlaceable[a_BlockType] ) + if ( !g_BlockFullyOccupiesVoxel[a_BlockType] ) { return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright (for glass, etc.); exceptions won't even be sent by client, no need to handle } @@ -127,7 +127,7 @@ public: { return i; } - else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM)) + else if ((g_BlockFullyOccupiesVoxel[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM)) { // Otherwise, if block in that direction is torch placeable and we haven't gotten to it via the bottom face, return that face return i; @@ -161,7 +161,7 @@ public: // No need to check for upright orientation, it was done when the torch was placed return true; } - else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] ) + else if ( !g_BlockFullyOccupiesVoxel[BlockInQuestion] ) { return false; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 853138769..275099540 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,11 +1,7 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 2.8.2) project (MCServer) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++03") -endif() - include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/") include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include") @@ -25,12 +21,34 @@ if (NOT MSVC) list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp") + # If building a windows version, but not using MSVC, add the resources directly to the makefile: + if (WIN32) + FILE(GLOB ResourceFiles + "Resources/*.rc" + ) + list(APPEND SOURCE "${ResourceFiles}") + endif() + + else () + # Generate the Bindings if they don't exist: + if (NOT EXISTS "${PROJECT_SOURCE_DIR}/Bindings/Bindings.cpp") + message("Bindings.cpp not found, generating now") + set(tolua_executable ${PROJECT_SOURCE_DIR}/Bindings/tolua++.exe) + execute_process( + COMMAND ${tolua_executable} -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/Bindings + ) + endif() + + # Add all subfolders as solution-folders: + list(APPEND FOLDERS "Resources") function(includefolder PATH) FILE(GLOB FOLDER_FILES "${PATH}/*.cpp" "${PATH}/*.h" + "${PATH}/*.rc" ) source_group("${PATH}" FILES ${FOLDER_FILES}) endfunction(includefolder) @@ -59,25 +77,31 @@ else () SET_SOURCE_FILES_PROPERTIES( "StackWalker.cpp LeakFinder.h" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\"" ) + list(APPEND SOURCE "Resources/MCServer.rc") endif() - set(EXECUTABLE MCServer) add_executable(${EXECUTABLE} ${SOURCE}) -set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer) -if (MSVC) - # MSVC generator adds a "Debug" or "Release" postfixes to the EXECUTABLE_OUTPUT_PATH, we need to cancel them: - SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES PREFIX "../") - SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES IMPORT_PREFIX "../") -endif() +# Output the executable into the $/MCServer folder, so that it has access to external resources: +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer) +SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_DEBUGPROFILE ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_RELEASEPROFILE ${CMAKE_SOURCE_DIR}/MCServer +) # Make the debug executable have a "_debug" suffix SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES DEBUG_POSTFIX "_debug") +# Make the profiled executables have a "_profile" postfix +SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES DEBUGPROFILE_POSTFIX "_debug_profile") +SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES RELEASEPROFILE_POSTFIX "_profile") + # Precompiled headers (2nd part) if (MSVC) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index fb26e983d..0735c8144 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1743,7 +1743,14 @@ bool cChunk::AddClient(cClientHandle* a_Client) for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) { - LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str()); + /* + // DEBUG: + LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", + (*itr)->GetUniqueID(), (*itr)->GetClass(), + m_PosX, m_PosY, m_PosZ, + a_Client->GetUsername().c_str() + ); + */ (*itr)->SpawnOn(*a_Client); } return true; @@ -1768,7 +1775,13 @@ void cChunk::RemoveClient( cClientHandle* a_Client ) { for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) { - LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); + /* + // DEBUG: + LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", + m_PosX, m_PosZ, + (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() + ); + */ a_Client->SendDestroyEntity(*(*itr)); } } diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 7d727a4d4..d1288994c 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -10,6 +10,7 @@ #pragma once #include "Vector3i.h" +#include "BiomeDef.h" @@ -57,97 +58,6 @@ typedef unsigned char HEIGHTTYPE; - - -// tolua_begin -/** Biome IDs -The first batch corresponds to the clientside biomes, used by MineCraft. -BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client -*/ -enum EMCSBiome -{ - biOcean = 0, - biPlains = 1, - biDesert = 2, - biExtremeHills = 3, - biForest = 4, - biTaiga = 5, - biSwampland = 6, - biRiver = 7, - biHell = 8, // same as Nether - biNether = 8, - biSky = 9, // same as biEnd - biEnd = 9, - biFrozenOcean = 10, - biFrozenRiver = 11, - biIcePlains = 12, - biTundra = 12, // same as Ice Plains - biIceMountains = 13, - biMushroomIsland = 14, - biMushroomShore = 15, - biBeach = 16, - biDesertHills = 17, - biForestHills = 18, - biTaigaHills = 19, - biExtremeHillsEdge = 20, - biJungle = 21, - biJungleHills = 22, - - // Release 1.7 biomes: - biJungleEdge = 23, - biDeepOcean = 24, - biStoneBeach = 25, - biColdBeach = 26, - biBirchForest = 27, - biBirchForestHills = 28, - biRoofedForest = 29, - biColdTaiga = 30, - biColdTaigaHills = 31, - biMegaTaiga = 32, - biMegaTaigaHills = 33, - biExtremeHillsPlus = 34, - biSavanna = 35, - biSavannaPlateau = 36, - biMesa = 37, - biMesaPlateauF = 38, - biMesaPlateau = 39, - - // Automatically capture the maximum consecutive biome value into biMaxBiome: - biNumBiomes, // True number of biomes, since they are zero-based - biMaxBiome = biNumBiomes - 1, // The maximum biome value - - // Add this number to the biomes to get the variant - biVariant = 128, - - // Release 1.7 biome variants: - biSunflowerPlains = 129, - biDesertM = 130, - biExtremeHillsM = 131, - biFlowerForest = 132, - biTaigaM = 133, - biSwamplandM = 134, - biIcePlainsSpikes = 140, - biJungleM = 149, - biJungleEdgeM = 151, - biBirchForestM = 155, - biBirchForestHillsM = 156, - biRoofedForestM = 157, - biColdTaigaM = 158, - biMegaSpruceTaiga = 160, - biMegaSpruceTaigaHills = 161, - biExtremeHillsPlusM = 162, - biSavannaM = 163, - biSavannaPlateauM = 164, - biMesaBryce = 165, - biMesaPlateauFM = 166, - biMesaPlateauM = 167, -} ; - -// tolua_end - - - - /// Constants used throughout the code, useful typedefs and utility functions class cChunkDef { @@ -610,8 +520,10 @@ public: // Illegal in C++03: typedef std::list< cCoordWithData<X> > cCoordWithDataList<X>; typedef cCoordWithData<int> cCoordWithInt; +typedef cCoordWithData<BLOCKTYPE> cCoordWithBlock; typedef std::list<cCoordWithInt> cCoordWithIntList; typedef std::vector<cCoordWithInt> cCoordWithIntVector; +typedef std::vector<cCoordWithBlock> cCoordWithBlockVector; diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 8125acb47..c8513d516 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -629,6 +629,17 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch return; } + case DIG_STATUS_DROP_STACK: + { + if (PlgMgr->CallHookPlayerTossingItem(*m_Player)) + { + // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch) + return; + } + m_Player->TossItem(false, 64); // Toss entire slot - if there aren't enough items, the maximum will be ejected + return; + } + default: { ASSERT(!"Unhandled DIG_STATUS"); @@ -1026,7 +1037,7 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO return; } - m_Player->SetRotation (a_Rotation); + m_Player->SetYaw (a_Rotation); m_Player->SetHeadYaw (a_Rotation); m_Player->SetPitch (a_Pitch); m_Player->SetTouchGround(a_IsOnGround); @@ -1058,7 +1069,7 @@ void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_ m_Player->SetStance (a_Stance); m_Player->SetTouchGround(a_IsOnGround); m_Player->SetHeadYaw (a_Rotation); - m_Player->SetRotation (a_Rotation); + m_Player->SetYaw (a_Rotation); m_Player->SetPitch (a_Pitch); } @@ -1978,6 +1989,15 @@ void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) +void cClientHandle::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +{ + m_Protocol->SendPluginMessage(a_Channel, a_Message); +} + + + + + void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) { m_Protocol->SendRemoveEntityEffect(a_Entity, a_EffectID); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index bc17df780..da2704b72 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -120,6 +120,7 @@ public: void SendPlayerMoveLook (void); void SendPlayerPosition (void); void SendPlayerSpawn (const cPlayer & a_Player); + void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID); void SendRespawn (void); void SendExperience (void); @@ -170,7 +171,13 @@ public: void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); void HandleDisconnect (const AString & a_Reason); void HandleEntityAction (int a_EntityID, char a_ActionID); + + /** 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 + */ bool HandleHandshake (const AString & a_Username); + void HandleKeepAlive (int a_KeepAliveID); void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); void HandlePing (void); diff --git a/src/Defines.h b/src/Defines.h index 534802d55..7a86f499e 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -41,8 +41,8 @@ extern bool g_BlockRequiresSpecialTool[256]; /// Is this block solid (player cannot walk through)? extern bool g_BlockIsSolid[256]; -/// Can torches be placed on this block? -extern bool g_BlockIsTorchPlaceable[256]; +/// Does this block fully occupy it's voxel - is it a 'full' block? +extern bool g_BlockFullyOccupiesVoxel[256]; /// Experience Orb setup enum @@ -85,6 +85,7 @@ enum DIG_STATUS_STARTED = 0, DIG_STATUS_CANCELLED = 1, DIG_STATUS_FINISHED = 2, + DIG_STATUS_DROP_STACK= 3, DIG_STATUS_DROP_HELD = 4, DIG_STATUS_SHOOT_EAT = 5, } ; @@ -563,34 +564,6 @@ namespace ItemCategory } } - - - - -/// Returns true if the biome has no downfall - deserts and savannas -inline bool IsBiomeNoDownfall(EMCSBiome a_Biome) -{ - switch (a_Biome) - { - case biDesert: - case biDesertHills: - case biDesertM: - case biSavanna: - case biSavannaM: - case biSavannaPlateau: - case biSavannaPlateauM: - case biNether: - case biEnd: - { - return true; - } - default: - { - return false; - } - } -} - // tolua_end diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 8a74c9da4..565c78dfd 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -75,13 +75,16 @@ cEntity::~cEntity() { ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world + /* + // DEBUG: LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p", m_UniqueID, m_Pos.x, m_Pos.y, m_Pos.z, (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), this ); - + */ + if (m_AttachedTo != NULL) { Detach(); @@ -138,9 +141,13 @@ bool cEntity::Initialize(cWorld * a_World) return false; } + /* + // DEBUG: LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}", m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z ); + */ + m_IsInitialized = true; m_World = a_World; m_World->AddEntity(this); @@ -256,16 +263,16 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R -void cEntity::SetRotationFromSpeed(void) +void cEntity::SetYawFromSpeed(void) { const double EPS = 0.0000001; if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) { // atan2() may overflow or is undefined, pick any number - SetRotation(0); + SetYaw(0); return; } - SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI); + SetYaw(atan2(m_Speed.x, m_Speed.z) * 180 / PI); } @@ -321,7 +328,10 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) m_Health = 0; } - AddSpeed(a_TDI.Knockback * 2); + if (IsMob() || IsPlayer()) // Knockback for only players and mobs + { + AddSpeed(a_TDI.Knockback * 2); + } m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); @@ -614,9 +624,12 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) m_bOnGround = true; + /* + // DEBUG: LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", m_UniqueID, GetClass(), BlockX, BlockY, BlockZ ); + */ } if (!m_bOnGround) @@ -626,11 +639,6 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. } - else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts - { - fallspeed = 0; - m_bOnGround = true; - } else if (BlockIn == E_BLOCK_COBWEB) { NextSpeed.y *= 0.05; // Reduce overall falling speed @@ -645,41 +653,18 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) } else { - if (IsMinecart()) + // Friction + if (NextSpeed.SqrLength() > 0.0004f) { - if (!IsBlockRail(BlockBelow)) + NextSpeed.x *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.x) < 0.05) { - // Friction if minecart is off track, otherwise, Minecart.cpp handles this - if (NextSpeed.SqrLength() > 0.0004f) - { - NextSpeed.x *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.x) < 0.05) - { - NextSpeed.x = 0; - } - NextSpeed.z *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.z) < 0.05) - { - NextSpeed.z = 0; - } - } + NextSpeed.x = 0; } - } - else - { - // Friction for non-minecarts - if (NextSpeed.SqrLength() > 0.0004f) + NextSpeed.z *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.z) < 0.05) { - NextSpeed.x *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.x) < 0.05) - { - NextSpeed.x = 0; - } - NextSpeed.z *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.z) < 0.05) - { - NextSpeed.z = 0; - } + NextSpeed.z = 0; } } } @@ -1104,9 +1089,11 @@ void cEntity::AttachTo(cEntity * a_AttachTo) // Already attached to that entity, nothing to do here return; } - - // Detach from any previous entity: - Detach(); + if (m_AttachedTo != NULL) + { + // Detach from any previous entity: + Detach(); + } // Attach to the new entity: m_AttachedTo = a_AttachTo; diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 2ba1b303d..91463bfd6 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -154,8 +154,7 @@ public: double GetPosX (void) const { return m_Pos.x; } double GetPosY (void) const { return m_Pos.y; } double GetPosZ (void) const { return m_Pos.z; } - const Vector3d & GetRot (void) const { return m_Rot; } - double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead + const Vector3d & GetRot (void) const { return m_Rot; } // OBSOLETE, use individual GetYaw(), GetPitch, GetRoll() components double GetYaw (void) const { return m_Rot.x; } double GetPitch (void) const { return m_Rot.y; } double GetRoll (void) const { return m_Rot.z; } @@ -177,8 +176,7 @@ public: void SetPosZ (double a_PosZ); void SetPosition(double a_PosX, double a_PosY, double a_PosZ); void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); } - void SetRot (const Vector3f & a_Rot); - void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead + void SetRot (const Vector3f & a_Rot); // OBSOLETE, use individual SetYaw(), SetPitch(), SetRoll() components void SetYaw (double a_Yaw); void SetPitch (double a_Pitch); void SetRoll (double a_Roll); @@ -223,7 +221,7 @@ public: void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } /// Sets the rotation to match the speed vector (entity goes "face-forward") - void SetRotationFromSpeed(void); + void SetYawFromSpeed(void); /// Sets the pitch to match the speed vector (entity gies "face-forward") void SetPitchFromSpeed(void); @@ -327,7 +325,7 @@ public: void AttachTo(cEntity * a_AttachTo); /// Detaches from the currently attached entity, if any - void Detach(void); + virtual void Detach(void); /// Makes sure head yaw is not over the specified range. void WrapHeadYaw(); diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index f75e23d8b..d0d384481 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -2,6 +2,7 @@ // Minecart.cpp // Implements the cMinecart class representing a minecart in the world +// Handles physics when a minecart is on any type of rail (overrides simulator in Entity.cpp) // Indiana Jones! #include "Globals.h" @@ -10,6 +11,10 @@ #include "../ClientHandle.h" #include "../Chunk.h" #include "Player.h" +#include "../BoundingBox.h" + +#define MAX_SPEED 8 +#define MAX_SPEED_NEGATIVE -MAX_SPEED @@ -18,11 +23,15 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), m_Payload(a_Payload), - m_LastDamage(0) + m_LastDamage(0), + m_DetectorRailPosition(0, 0, 0), + m_bIsOnDetectorRail(false) { SetMass(20.f); SetMaxHealth(6); SetHealth(6); + SetWidth(1.2); + SetHeight(0.9); } @@ -45,6 +54,7 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) } } a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart + a_ClientHandle.SendEntityMetadata(*this); } @@ -53,6 +63,11 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) { + if (IsDestroyed()) // Mainly to stop detector rails triggering again after minecart is dead + { + return; + } + int PosY = (int)floor(GetPosY()); if ((PosY <= 0) || (PosY >= cChunkDef::Height)) { @@ -70,286 +85,513 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) // Inside an unloaded chunk, bail out all processing return; } - BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ); - BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ); - if (IsBlockRail(BelowType)) + BLOCKTYPE InsideType; + NIBBLETYPE InsideMeta; + Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta); + + if (!IsBlockRail(InsideType)) { - HandleRailPhysics(a_Dt, *Chunk); + 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 } - else + + if (IsBlockRail(InsideType)) { - if (IsBlockRail(InsideType)) + bool WasDetectorRail = false; + SnapToRail(InsideMeta); + + switch (InsideType) { - SetPosY(PosY + 1); - HandleRailPhysics(a_Dt, *Chunk); + case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break; + case E_BLOCK_ACTIVATOR_RAIL: break; + case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break; + case E_BLOCK_DETECTOR_RAIL: + { + HandleDetectorRailPhysics(InsideMeta, a_Dt); + WasDetectorRail = true; + break; + } + default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break; } - else + + AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp + + if (m_bIsOnDetectorRail && !WasDetectorRail) { - super::HandlePhysics(a_Dt, *Chunk); - BroadcastMovementUpdate(); + m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07); + m_bIsOnDetectorRail = false; } } + else + { + // Not on rail, default physics + SetPosY(floor(GetPosY()) + 0.35); // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail + super::HandlePhysics(a_Dt, *Chunk); + } + + // Broadcast positioning changes to client + BroadcastMovementUpdate(); } -static const double MAX_SPEED = 8; -static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); - -void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) +void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) { - - super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling - /* NOTE: Please bear in mind that taking away from negatives make them even more negative, adding to negatives make them positive, etc. */ - // Get block meta below the cart - int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; - NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ); - double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed - - switch (BelowMeta) + switch (a_RailMeta) { case E_META_RAIL_ZM_ZP: // NORTHSOUTH { - SetRotation(270); - SpeedY = 0; // Don't move vertically as on ground - SpeedX = 0; // Correct diagonal movement from curved rails + SetYaw(270); + SetPosY(floor(GetPosY()) + 0.55); + SetSpeedY(0); // Don't move vertically as on ground + SetSpeedX(0); // Correct diagonal movement from curved rails + + if (TestBlockCollision(a_RailMeta)) return; - if (SpeedZ != 0) // Don't do anything if cart is stationary + if (GetSpeedZ() != 0) // Don't do anything if cart is stationary { - if (SpeedZ > 0) + if (GetSpeedZ() > 0) { // Going SOUTH, slow down - SpeedZ = SpeedZ - 0.1; + AddSpeedZ(-0.1); } else { // Going NORTH, slow down - SpeedZ = SpeedZ + 0.1; + AddSpeedZ(0.1); } } break; } - case E_META_RAIL_XM_XP: // EASTWEST { - SetRotation(180); - SpeedY = 0; - SpeedZ = 0; + SetYaw(180); + SetPosY(floor(GetPosY()) + 0.55); + SetSpeedY(0); + SetSpeedZ(0); + + if (TestBlockCollision(a_RailMeta)) return; - if (SpeedX != 0) + if (GetSpeedX() != 0) { - if (SpeedX > 0) + if (GetSpeedX() > 0) { - SpeedX = SpeedX - 0.1; + AddSpeedX(-0.1); } else { - SpeedX = SpeedX + 0.1; + AddSpeedX(0.1); } } break; } - case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH { - SetRotation(270); - SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/ - SpeedX = 0; + SetYaw(270); + SetSpeedX(0); - if (SpeedZ >= 0) + if (GetSpeedZ() >= 0) { // SpeedZ POSITIVE, going SOUTH - if (SpeedZ <= MAX_SPEED) // Speed limit + if (GetSpeedZ() <= MAX_SPEED) // Speed limit { - SpeedZ = SpeedZ + 0.5; // Speed up - SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative) - } - else - { - SpeedZ = MAX_SPEED; // Enforce speed limit - SpeedY = (0 - SpeedZ); + AddSpeedZ(0.5); // Speed up + SetSpeedY(-GetSpeedZ()); // Downward movement is negative (0 minus positive numbers is negative) } } else { // SpeedZ NEGATIVE, going NORTH - SpeedZ = SpeedZ + 0.4; // Slow down - SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number) + AddSpeedZ(1); // Slow down + SetSpeedY(-GetSpeedZ()); // Upward movement is positive (0 minus negative number is positive number) } break; } - case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH { - SetRotation(270); - SetPosY(floor(GetPosY()) + 0.2); - SpeedX = 0; + SetYaw(270); + SetSpeedX(0); - if (SpeedZ > 0) + if (GetSpeedZ() > 0) { // SpeedZ POSITIVE, going SOUTH - SpeedZ = SpeedZ - 0.4; // Slow down - SpeedY = SpeedZ; // Upward movement positive + AddSpeedZ(-1); // Slow down + SetSpeedY(GetSpeedZ()); // Upward movement positive } else { - if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit + if (GetSpeedZ() >= MAX_SPEED_NEGATIVE) // Speed limit { // SpeedZ NEGATIVE, going NORTH - SpeedZ = SpeedZ - 0.5; // Speed up - SpeedY = SpeedZ; // Downward movement negative - } - else - { - SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit - SpeedY = SpeedZ; + AddSpeedZ(-0.5); // Speed up + SetSpeedY(GetSpeedZ()); // Downward movement negative } } break; } - case E_META_RAIL_ASCEND_XM: // ASCEND EAST { - SetRotation(180); - SetPosY(floor(GetPosY()) + 0.2); - SpeedZ = 0; + SetYaw(180); + SetSpeedZ(0); - if (SpeedX >= 0) + if (GetSpeedX() >= 0) { - if (SpeedX <= MAX_SPEED) - { - SpeedX = SpeedX + 0.5; - SpeedY = (0 - SpeedX); - } - else + if (GetSpeedX() <= MAX_SPEED) { - SpeedX = MAX_SPEED; - SpeedY = (0 - SpeedX); + AddSpeedX(0.5); + SetSpeedY(-GetSpeedX()); } } else { - SpeedX = SpeedX + 0.4; - SpeedY = (0 - SpeedX); + AddSpeedX(1); + SetSpeedY(-GetSpeedX()); } break; } - case E_META_RAIL_ASCEND_XP: // ASCEND WEST { - SetRotation(180); - SetPosY(floor(GetPosY()) + 0.2); - SpeedZ = 0; + SetYaw(180); + SetSpeedZ(0); - if (SpeedX > 0) + if (GetSpeedX() > 0) { - SpeedX = SpeedX - 0.4; - SpeedY = SpeedX; + AddSpeedX(-1); + SetSpeedY(GetSpeedX()); } else { - if (SpeedX >= MAX_SPEED_NEGATIVE) + if (GetSpeedX() >= MAX_SPEED_NEGATIVE) { - SpeedX = SpeedX - 0.5; - SpeedY = SpeedX; - } - else - { - SpeedX = MAX_SPEED_NEGATIVE; - SpeedY = SpeedX; + AddSpeedX(-0.5); + SetSpeedY(GetSpeedX()); } } break; } - case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST { - SetRotation(315); // Set correct rotation server side - SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart + SetYaw(315); // Set correct rotation server side + SetPosY(floor(GetPosY()) + 0.55); // Levitate dat cart + + if (TestBlockCollision(a_RailMeta)) return; - if (SpeedZ > 0) // Cart moving south + if (GetSpeedZ() > 0) // Cart moving south { - SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail) + int OldX = (int)floor(GetPosX()); + AddSpeedX(-GetSpeedZ() + 0.5); // See below + AddPosX(-GetSpeedZ() * (a_Dt / 1000)); // Diagonally move southwest (which will make cart hit a southwest rail) + // If we are already at southwest rail, set Z speed to zero as we can be moving so fast, MCS doesn't tick fast enough to active the handle for the rail... + // ...and so we derail unexpectedly. + if (GetPosX() <= OldX - 1) SetSpeedZ(0); } - else if (SpeedX > 0) // Cart moving east + else if (GetSpeedX() > 0) // Cart moving east { - SpeedZ = (0 - SpeedX); // Diagonally move northeast + int OldZ = (int)floor(GetPosZ()); + AddSpeedZ(-GetSpeedX() + 0.5); + AddPosZ(-GetSpeedX() * (a_Dt / 1000)); // Diagonally move northeast + if (GetPosZ() <= OldZ - 1) SetSpeedX(0); } break; } - case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST { - SetRotation(225); - SetPosY(floor(GetPosY()) + 0.2); + SetYaw(225); + SetPosY(floor(GetPosY()) + 0.55); + + if (TestBlockCollision(a_RailMeta)) return; - if (SpeedZ > 0) + if (GetSpeedZ() > 0) { - SpeedX = SpeedZ; + int OldX = (int)floor(GetPosX()); + AddSpeedX(GetSpeedZ() - 0.5); + AddPosX(GetSpeedZ() * (a_Dt / 1000)); + if (GetPosX() >= OldX + 1) SetSpeedZ(0); } - else if (SpeedX < 0) + else if (GetSpeedX() < 0) { - SpeedZ = SpeedX; + int OldZ = (int)floor(GetPosZ()); + AddSpeedZ(GetSpeedX() + 0.5); + AddPosZ(GetSpeedX() * (a_Dt / 1000)); + if (GetPosZ() <= OldZ - 1) SetSpeedX(0); } break; } - case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST { - SetRotation(135); - SetPosY(floor(GetPosY()) + 0.2); + SetYaw(135); + SetPosY(floor(GetPosY()) + 0.55); - if (SpeedZ < 0) + if (TestBlockCollision(a_RailMeta)) return; + + if (GetSpeedZ() < 0) { - SpeedX = SpeedZ; + int OldX = (int)floor(GetPosX()); + AddSpeedX(GetSpeedZ() + 0.5); + AddPosX(GetSpeedZ() * (a_Dt / 1000)); + if (GetPosX() <= OldX - 1) SetSpeedZ(0); } - else if (SpeedX > 0) + else if (GetSpeedX() > 0) { - SpeedZ = SpeedX; + int OldZ = (int)floor(GetPosZ()); + AddSpeedZ(GetSpeedX() - 0.5); + AddPosZ(GetSpeedX() * (a_Dt / 1000)); + if (GetPosZ() >= OldZ + 1) SetSpeedX(0); } break; } - case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST { - SetRotation(45); - SetPosY(floor(GetPosY()) + 0.2); + SetYaw(45); + SetPosY(floor(GetPosY()) + 0.55); + + if (TestBlockCollision(a_RailMeta)) return; - if (SpeedZ < 0) + if (GetSpeedZ() < 0) { - SpeedX = (0 - SpeedZ); + int OldX = (int)floor(GetPosX()); + AddSpeedX(-GetSpeedZ() - 0.5); + AddPosX(-GetSpeedZ() * (a_Dt / 1000)); + if (GetPosX() >= OldX + 1) SetSpeedZ(0); } - else if (SpeedX < 0) + else if (GetSpeedX() < 0) { - SpeedZ = (0 - SpeedX); + int OldZ = (int)floor(GetPosZ()); + AddSpeedZ(-GetSpeedX() - 0.5); + AddPosZ(-GetSpeedX() * (a_Dt / 1000)); + if (GetPosZ() >= OldZ + 1) SetSpeedX(0); } break; } - default: { ASSERT(!"Unhandled rail meta!"); // Dun dun DUN! break; } } +} - // Set speed to speed variables - SetSpeedX(SpeedX); - SetSpeedY(SpeedY); - SetSpeedZ(SpeedZ); - // Broadcast position to client - BroadcastMovementUpdate(); + +void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta) +{ + // Initialise to 'slow down' values + int AccelDecelSpeed = -1; + int AccelDecelNegSpeed = 1; + + if ((a_RailMeta & 0x8) == 0x8) + { + // Rail powered - set variables to 'speed up' values + AccelDecelSpeed = 1; + AccelDecelNegSpeed = -1; + } + + switch (a_RailMeta & 0x07) + { + case E_META_RAIL_ZM_ZP: // NORTHSOUTH + { + SetYaw(270); + SetPosY(floor(GetPosY()) + 0.55); + SetSpeedY(0); + SetSpeedX(0); + + if (TestBlockCollision(a_RailMeta)) return; + + if (GetSpeedZ() != 0) + { + if (GetSpeedZ() > 0) + { + AddSpeedZ(AccelDecelNegSpeed); + } + else + { + AddSpeedZ(AccelDecelSpeed); + } + } + break; + } + case E_META_RAIL_XM_XP: // EASTWEST + { + SetYaw(180); + SetPosY(floor(GetPosY()) + 0.55); + SetSpeedY(0); + SetSpeedZ(0); + + if (TestBlockCollision(a_RailMeta)) return; + + if (GetSpeedX() != 0) + { + if (GetSpeedX() > 0) + { + AddSpeedX(AccelDecelSpeed); + } + else + { + AddSpeedX(AccelDecelNegSpeed); + } + } + break; + } + } +} + + + + + +void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +{ + m_bIsOnDetectorRail = true; + m_DetectorRailPosition = Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())); + + m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08); + + HandleRailPhysics(a_RailMeta & 0x07, a_Dt); +} + + + + + +void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta) +{ + switch (a_RailMeta) + { + case E_META_RAIL_ASCEND_XM: + case E_META_RAIL_ASCEND_XP: + case E_META_RAIL_XM_XP: + { + SetSpeedZ(0); + SetPosZ(floor(GetPosZ()) + 0.5); + break; + } + case E_META_RAIL_ASCEND_ZM: + case E_META_RAIL_ASCEND_ZP: + case E_META_RAIL_ZM_ZP: + { + SetSpeedX(0); + SetPosX(floor(GetPosX()) + 0.5); + break; + } + default: break; + } +} + + + + + +bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta) +{ + switch (a_RailMeta) + { + case E_META_RAIL_ZM_ZP: + { + if (GetSpeedZ() > 0) + { + BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ())); + if (!IsBlockRail(Block) && g_BlockIsSolid[Block]) + { + // We could try to detect a block in front based purely on coordinates, but xoft made a bounding box system - why not use? :P + cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ())), 0.5, 1); + cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight()); + + if (bbBlock.DoesIntersect(bbMinecart)) + { + SetSpeed(0, 0, 0); + SetPosZ(floor(GetPosZ()) + 0.4); + return true; + } + } + } + else + { + BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1); + if (!IsBlockRail(Block) && g_BlockIsSolid[Block]) + { + cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1), 0.5, 1); + cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ() - 1), GetWidth() / 2, GetHeight()); + + if (bbBlock.DoesIntersect(bbMinecart)) + { + SetSpeed(0, 0, 0); + SetPosZ(floor(GetPosZ()) + 0.65); + return true; + } + } + } + break; + } + case E_META_RAIL_XM_XP: + { + if (GetSpeedX() > 0) + { + BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())); + if (!IsBlockRail(Block) && g_BlockIsSolid[Block]) + { + cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1); + cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight()); + + if (bbBlock.DoesIntersect(bbMinecart)) + { + SetSpeed(0, 0, 0); + SetPosX(floor(GetPosX()) + 0.4); + return true; + } + } + } + else + { + BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ())); + if (!IsBlockRail(Block) && g_BlockIsSolid[Block]) + { + cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1); + cBoundingBox bbMinecart(Vector3d(GetPosX() - 1, floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight()); + + if (bbBlock.DoesIntersect(bbMinecart)) + { + SetSpeed(0, 0, 0); + SetPosX(floor(GetPosX()) + 0.65); + return true; + } + } + } + break; + } + case E_META_RAIL_CURVED_ZM_XM: + case E_META_RAIL_CURVED_ZM_XP: + case E_META_RAIL_CURVED_ZP_XM: + case E_META_RAIL_CURVED_ZP_XP: + { + BLOCKTYPE BlockXM = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ())); + BLOCKTYPE BlockXP = m_World->GetBlock((int)floor(GetPosX()) + 1, (int)floor(GetPosY()), (int)floor(GetPosZ())); + BLOCKTYPE BlockZM = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1); + BLOCKTYPE BlockZP = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1); + if ( + (!IsBlockRail(BlockXM) && g_BlockIsSolid[BlockXM]) || + (!IsBlockRail(BlockXP) && g_BlockIsSolid[BlockXP]) || + (!IsBlockRail(BlockZM) && g_BlockIsSolid[BlockZM]) || + (!IsBlockRail(BlockZP) && g_BlockIsSolid[BlockZP]) + ) + { + SetSpeed(0, 0, 0); + SetPosition((int)floor(GetPosX()) + 0.5, GetPosY(), (int)floor(GetPosZ()) + 0.5); + return true; + } + break; + } + default: break; + } + return false; } @@ -358,6 +600,14 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) { + if (TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative()) + { + Destroy(); + TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative + super::DoTakeDamage(TDI); + return; // No drops for creative + } + m_LastDamage = TDI.FinalDamage; super::DoTakeDamage(TDI); @@ -365,7 +615,7 @@ void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) if (GetHealth() <= 0) { - Destroy(true); + Destroy(); cItems Drops; switch (m_Payload) @@ -410,11 +660,25 @@ void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) +void cMinecart::Destroyed() +{ + if (m_bIsOnDetectorRail) + { + m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07); + } +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cEmptyMinecart: +// cRideableMinecart: -cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : - super(mpNone, a_X, a_Y, a_Z) +cRideableMinecart::cRideableMinecart(double a_X, double a_Y, double a_Z, const cItem & a_Content, int a_Height) : + super(mpNone, a_X, a_Y, a_Z), + m_Content(a_Content), + m_Height(a_Height) { } @@ -422,7 +686,7 @@ cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : -void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) +void cRideableMinecart::OnRightClicked(cPlayer & a_Player) { if (m_Attachee != NULL) { @@ -489,7 +753,8 @@ void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : super(mpFurnace, a_X, a_Y, a_Z), - m_IsFueled(false) + m_IsFueled(false), + m_FueledTimeLeft(-1) { } @@ -505,8 +770,12 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) { a_Player.GetInventory().RemoveOneEquippedItem(); } - + if (!m_IsFueled) // We don't want to change the direction by right clicking it. + { + AddSpeed(a_Player.GetLookVector().x, 0, a_Player.GetLookVector().z); + } m_IsFueled = true; + m_FueledTimeLeft = m_FueledTimeLeft + 600; // The minecart will be active 600 more ticks. m_World->BroadcastEntityMetadata(*this); } } @@ -515,6 +784,32 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) +void cMinecartWithFurnace::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (m_IsFueled) + { + m_FueledTimeLeft--; + if (m_FueledTimeLeft < 0) + { + m_IsFueled = false; + m_World->BroadcastEntityMetadata(*this); + return; + } + + if (GetSpeed().Length() > 6) + { + return; + } + AddSpeed(GetSpeed() / 4); + } +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cMinecartWithTNT: diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index b1b48be4e..874d0204e 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -51,17 +51,38 @@ public: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual void Destroyed() override; int LastDamage(void) const { return m_LastDamage; } - void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); ePayload GetPayload(void) const { return m_Payload; } protected: ePayload m_Payload; + int m_LastDamage; + Vector3i m_DetectorRailPosition; + bool m_bIsOnDetectorRail; cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); - int m_LastDamage; + /** Handles physics on normal rails + For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails + */ + void HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + + /** Handles powered rail physics + Each tick, speed up or slow down cart, depending on metadata of rail (powered or not) + */ + void HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta); + + /** Handles detector rail activation + Activates detector rails when a minecart is on them. Calls HandleRailPhysics() for physics simulations + */ + void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + + /** Snaps a minecart to a rail's axis, resetting its speed */ + void SnapToRail(NIBBLETYPE a_RailMeta); + /** Tests is a solid block is in front of a cart, and stops the cart (and returns true) if so; returns false if no obstruction*/ + bool TestBlockCollision(NIBBLETYPE a_RailMeta); } ; @@ -69,18 +90,24 @@ protected: -class cEmptyMinecart : +class cRideableMinecart : public cMinecart { typedef cMinecart super; public: - CLASS_PROTODEF(cEmptyMinecart); + CLASS_PROTODEF(cRideableMinecart); - cEmptyMinecart(double a_X, double a_Y, double a_Z); + cRideableMinecart(double a_X, double a_Y, double a_Z, const cItem & a_Content, int a_Height); + const cItem & GetContent(void) const {return m_Content;} + int GetBlockHeight(void) const {return m_Height;} // cEntity overrides: virtual void OnRightClicked(cPlayer & a_Player) override; +protected: + + cItem m_Content; + int m_Height; } ; @@ -130,10 +157,18 @@ public: // cEntity overrides: virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsFueled (void) const { return m_IsFueled; } + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + // Set functions. + void SetIsFueled(bool a_IsFueled, int a_FueledTimeLeft = -1) {m_IsFueled = a_IsFueled; m_FueledTimeLeft = a_FueledTimeLeft;} + + // Get functions. + int GetFueledTimeLeft(void) const {return m_FueledTimeLeft; } + bool IsFueled (void) const {return m_IsFueled;} private: + int m_FueledTimeLeft; bool m_IsFueled; } ; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index bc92790aa..c1f2456eb 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1382,16 +1382,21 @@ void cPlayer::TossItem( cItem DroppedItem(GetInventory().GetEquippedItem()); if (!DroppedItem.IsEmpty()) { - if (GetInventory().RemoveOneEquippedItem()) + char NewAmount = a_Amount; + if (NewAmount > GetInventory().GetEquippedItem().m_ItemCount) { - DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again - Drops.push_back(DroppedItem); + NewAmount = GetInventory().GetEquippedItem().m_ItemCount; // Drop only what's there } + + GetInventory().GetHotbarGrid().ChangeSlotCount(GetInventory().GetEquippedSlotNum() /* Returns hotbar subslot, which HotbarGrid takes */, -a_Amount); + + DroppedItem.m_ItemCount = NewAmount; + Drops.push_back(DroppedItem); } } } double vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); + EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY); vY = -vY * 2 + 1.f; m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player } @@ -1523,7 +1528,7 @@ bool cPlayer::LoadFromDisk() Json::Value & JSON_PlayerRotation = root["rotation"]; if (JSON_PlayerRotation.size() == 3) { - SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); + SetYaw ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); } @@ -1586,7 +1591,7 @@ bool cPlayer::SaveToDisk() JSON_PlayerPosition.append(Json::Value(GetPosZ())); Json::Value JSON_PlayerRotation; - JSON_PlayerRotation.append(Json::Value(GetRotation())); + JSON_PlayerRotation.append(Json::Value(GetYaw())); JSON_PlayerRotation.append(Json::Value(GetPitch())); JSON_PlayerRotation.append(Json::Value(GetRoll())); @@ -1884,3 +1889,33 @@ void cPlayer::ApplyFoodExhaustionFromMovement() + +void cPlayer::Detach() +{ + super::Detach(); + int PosX = (int)floor(GetPosX()); + int PosY = (int)floor(GetPosY()); + int PosZ = (int)floor(GetPosZ()); + + // Search for a position within an area to teleport player after detachment + // Position must be solid land, and occupied by a nonsolid block + // If nothing found, player remains where they are + for (int x = PosX - 2; x <= (PosX + 2); ++x) + { + for (int y = PosY; y <= (PosY + 3); ++y) + { + for (int z = PosZ - 2; z <= (PosZ + 2); ++z) + { + if (!g_BlockIsSolid[m_World->GetBlock(x, y, z)] && g_BlockIsSolid[m_World->GetBlock(x, y - 1, z)]) + { + TeleportToCoords(x, y, z); + return; + } + } + } + } +} + + + + diff --git a/src/Entities/Player.h b/src/Entities/Player.h index f9ce950ba..bf3ca08e8 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -350,6 +350,8 @@ public: virtual bool IsCrouched (void) const { return m_IsCrouched; } virtual bool IsSprinting(void) const { return m_IsSprinting; } virtual bool IsRclking (void) const { return IsEating(); } + + virtual void Detach(void); protected: typedef std::map< std::string, bool > PermissionMap; diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 9e5069ba6..12ce9a303 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -206,7 +206,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve m_IsInGround(false) { SetSpeed(a_Speed); - SetRotationFromSpeed(); + SetYawFromSpeed(); SetPitchFromSpeed(); } @@ -350,7 +350,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) NewSpeed.y += m_Gravity / 20; NewSpeed *= TracerCallback.GetSlowdownCoeff(); SetSpeed(NewSpeed); - SetRotationFromSpeed(); + SetYawFromSpeed(); SetPitchFromSpeed(); // DEBUG: @@ -358,7 +358,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) m_UniqueID, GetPosX(), GetPosY(), GetPosZ(), GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetRotation(), GetPitch() + GetYaw(), GetPitch() ); } @@ -369,7 +369,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) void cProjectileEntity::SpawnOn(cClientHandle & a_Client) { // Default spawning - use the projectile kind to spawn an object: - a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch())); + a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetYaw()), ANGLE_TO_PROTO(GetPitch())); a_Client.SendEntityMetadata(*this); } @@ -402,11 +402,11 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a { SetSpeed(a_Speed); SetMass(0.1); - SetRotationFromSpeed(); + SetYawFromSpeed(); SetPitchFromSpeed(); LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetRotation(), GetPitch() + GetYaw(), GetPitch() ); } diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 126a896af..baa5b76b8 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -2,13 +2,11 @@ #include "Globals.h" #include "ChunkGenerator.h" -#include "../World.h" #include "inifile/iniFile.h" -#include "../Root.h" -#include "../Bindings/PluginManager.h" #include "ChunkDesc.h" #include "ComposableGenerator.h" #include "Noise3DGenerator.h" +#include "../MersenneTwister.h" @@ -29,8 +27,9 @@ const unsigned int QUEUE_SKIP_LIMIT = 500; cChunkGenerator::cChunkGenerator(void) : super("cChunkGenerator"), - m_World(NULL), - m_Generator(NULL) + m_Generator(NULL), + m_PluginInterface(NULL), + m_ChunkSink(NULL) { } @@ -47,10 +46,12 @@ cChunkGenerator::~cChunkGenerator() -bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile) +bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile) { + m_PluginInterface = &a_PluginInterface; + m_ChunkSink = &a_ChunkSink; + MTRand rnd; - m_World = a_World; m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt()); AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable"); @@ -73,7 +74,7 @@ bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile) return false; } - m_Generator->Initialize(a_World, a_IniFile); + m_Generator->Initialize(a_IniFile); return super::Start(); } @@ -237,14 +238,14 @@ void cChunkGenerator::Execute(void) } // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set - if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) + if ((coords.m_ChunkY == 0) && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) { LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); // Already generated, ignore request continue; } - if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) + if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) { LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); continue; @@ -253,9 +254,6 @@ void cChunkGenerator::Execute(void) LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); - // Save the chunk right after generating, so that we don't have to generate it again on next run - m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); - NumChunksGenerated++; } // while (!bStop) } @@ -265,27 +263,20 @@ void cChunkGenerator::Execute(void) void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { + ASSERT(m_PluginInterface != NULL); + ASSERT(m_ChunkSink != NULL); + cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ); - cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); + m_PluginInterface->CallHookChunkGenerating(ChunkDesc); m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc); - cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); + m_PluginInterface->CallHookChunkGenerated(ChunkDesc); #ifdef _DEBUG // Verify that the generator has produced valid data: ChunkDesc.VerifyHeightmap(); #endif - cChunkDef::BlockNibbles BlockMetas; - ChunkDesc.CompressBlockMetas(BlockMetas); - - m_World->SetChunkData( - a_ChunkX, a_ChunkZ, - ChunkDesc.GetBlockTypes(), BlockMetas, - NULL, NULL, // We don't have lighting, chunk will be lighted when needed - &ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(), - ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(), - true - ); + m_ChunkSink->OnChunkGenerated(ChunkDesc); } @@ -304,9 +295,8 @@ cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) : -void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +void cChunkGenerator::cGenerator::Initialize(cIniFile & a_IniFile) { - m_World = a_World; UNUSED(a_IniFile); } @@ -319,7 +309,7 @@ EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) cChunkDef::BiomeMap Biomes; int Y = 0; int ChunkX, ChunkZ; - cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ); + cChunkDef::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, ChunkZ); GenerateBiomes(ChunkX, ChunkZ, Biomes); return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ); } diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h index 2d3bb8082..9b2d9eb3c 100644 --- a/src/Generating/ChunkGenerator.h +++ b/src/Generating/ChunkGenerator.h @@ -26,7 +26,6 @@ If the generator queue is overloaded, the generator skips chunks with no clients // fwd: -class cWorld; class cIniFile; class cChunkDesc; @@ -40,7 +39,7 @@ class cChunkGenerator : typedef cIsThread super; public: - /// The interface that a class has to implement to become a generator + /** The interface that a class has to implement to become a generator */ class cGenerator { public: @@ -48,7 +47,7 @@ public: virtual ~cGenerator() {} ; // Force a virtual destructor /// Called to initialize the generator on server startup. - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile); + virtual void Initialize(cIniFile & a_IniFile); /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; @@ -61,14 +60,59 @@ public: protected: cChunkGenerator & m_ChunkGenerator; - cWorld * m_World; + } ; + + + /** The interface through which the plugins are called for their OnChunkGenerating / OnChunkGenerated hooks. */ + class cPluginInterface + { + public: + // Force a virtual destructor + virtual ~cPluginInterface() {} + + /** Called when the chunk is about to be generated. + The generator may be partly or fully overriden by the implementation + */ + virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) = 0; + + /** Called after the chunk is generated, before it is handed to the chunk sink. + a_ChunkDesc contains the generated chunk data. Implementation may modify this data. + */ + virtual void CallHookChunkGenerated(cChunkDesc & a_ChunkDesc) = 0; + } ; + + + /** The interface through which the generated chunks are handed to the cWorld or whoever created us. */ + class cChunkSink + { + public: + // Force a virtual destructor + virtual ~cChunkSink() {} + + /** Called after the chunk has been generated + The interface may store the chunk, send it over network, whatever. + The chunk is not expected to be modified, but the generator will survive if the implementation + changes the data within. All changes are ignored, though. + */ + virtual void OnChunkGenerated(cChunkDesc & a_ChunkDesc) = 0; + + /** Called just before the chunk generation is started, + to verify that it hasn't been generated in the meantime. + If this callback returns true, the chunk is not generated. + */ + virtual bool IsChunkValid(int a_ChunkX, int a_ChunkZ) = 0; + + /** Called when the generator is overloaded to skip chunks that are no longer needed. + If this callback returns false, the chunk is not generated. + */ + virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) = 0; } ; cChunkGenerator (void); ~cChunkGenerator(); - bool Start(cWorld * a_World, cIniFile & a_IniFile); + bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile); void Stop(void); /// Queues the chunk for generation; removes duplicate requests @@ -91,8 +135,6 @@ public: private: - cWorld * m_World; - int m_Seed; cCriticalSection m_CS; @@ -101,6 +143,13 @@ private: cEvent m_evtRemoved; ///< Set when an item is removed from the queue cGenerator * m_Generator; ///< The actual generator engine used to generate chunks + + /** The plugin interface that may modify the generated chunks */ + cPluginInterface * m_PluginInterface; + + /** The destination where the generated chunks are sent */ + cChunkSink * m_ChunkSink; + // cIsThread override: virtual void Execute(void) override; diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 9817b6654..b01f84627 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -28,11 +28,88 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cTerrainCompositionGen: +cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed) +{ + AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); + if (CompoGenName.empty()) + { + LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\"."); + CompoGenName = "Biomal"; + a_IniFile.SetValue("Generator", "CompositionGen", CompoGenName); + } + + cTerrainCompositionGen * res = NULL; + if (NoCaseCompare(CompoGenName, "sameblock") == 0) + { + res = new cCompoGenSameBlock; + } + else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) + { + res = new cCompoGenDebugBiomes; + } + else if (NoCaseCompare(CompoGenName, "classic") == 0) + { + res = new cCompoGenClassic; + } + else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) + { + res = new cDistortedHeightmap(a_Seed, a_BiomeGen); + } + else if (NoCaseCompare(CompoGenName, "end") == 0) + { + res = new cEndGen(a_Seed); + } + else if (NoCaseCompare(CompoGenName, "nether") == 0) + { + res = new cCompoGenNether(a_Seed); + } + else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) + { + res = new cNoise3DComposable(a_Seed); + } + else if (NoCaseCompare(CompoGenName, "biomal") == 0) + { + res = new cCompoGenBiomal(a_Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cCompoGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDesc Desc(200 + x * 8, 200 + x * 8); + a_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); + a_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); + res->ComposeTerrain(Desc); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + else + { + LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str()); + a_IniFile.DeleteValue("Generator", "CompositionGen"); + a_IniFile.SetValue("Generator", "CompositionGen", "Biomal"); + return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed); + } + ASSERT(res != NULL); + + // Read the settings from the ini file: + res->InitializeCompoGen(a_IniFile); + + return res; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cComposableGenerator: + cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : super(a_ChunkGenerator), m_BiomeGen(NULL), @@ -80,9 +157,9 @@ cComposableGenerator::~cComposableGenerator() -void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +void cComposableGenerator::Initialize(cIniFile & a_IniFile) { - super::Initialize(a_World, a_IniFile); + super::Initialize(a_IniFile); InitBiomeGen(a_IniFile); InitHeightGen(a_IniFile); @@ -173,60 +250,8 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) { - AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); - if (HeightGenName.empty()) - { - LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); - HeightGenName = "Biomal"; - } - - int Seed = m_ChunkGenerator.GetSeed(); bool CacheOffByDefault = false; - if (NoCaseCompare(HeightGenName, "flat") == 0) - { - m_HeightGen = new cHeiGenFlat; - CacheOffByDefault = true; // We're generating faster than a cache would retrieve data - } - else if (NoCaseCompare(HeightGenName, "classic") == 0) - { - m_HeightGen = new cHeiGenClassic(Seed); - } - else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) - { - m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - } - else if (NoCaseCompare(HeightGenName, "End") == 0) - { - m_HeightGen = new cEndGen(Seed); - } - else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) - { - m_HeightGen = new cNoise3DComposable(Seed); - } - else // "biomal" or <not found> - { - if (NoCaseCompare(HeightGenName, "biomal") != 0) - { - LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); - } - m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cHeiGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDef::HeightMap Heights; - m_HeightGen->GenHeightMap(x * 5, x * 5, Heights); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Read the settings: - m_HeightGen->InitializeHeightGen(a_IniFile); + m_HeightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, *m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault); // Add a cache, if requested: int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); @@ -251,67 +276,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) { - int Seed = m_ChunkGenerator.GetSeed(); - AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); - if (CompoGenName.empty()) - { - LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\"."); - CompoGenName = "Biomal"; - } - if (NoCaseCompare(CompoGenName, "sameblock") == 0) - { - m_CompositionGen = new cCompoGenSameBlock; - } - else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) - { - m_CompositionGen = new cCompoGenDebugBiomes; - } - else if (NoCaseCompare(CompoGenName, "classic") == 0) - { - m_CompositionGen = new cCompoGenClassic; - } - else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) - { - m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - } - else if (NoCaseCompare(CompoGenName, "end") == 0) - { - m_CompositionGen = new cEndGen(Seed); - } - else if (NoCaseCompare(CompoGenName, "nether") == 0) - { - m_CompositionGen = new cCompoGenNether(Seed); - } - else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) - { - m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); - } - else - { - if (NoCaseCompare(CompoGenName, "biomal") != 0) - { - LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); - } - m_CompositionGen = new cCompoGenBiomal(Seed); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cCompoGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDesc Desc(200 + x * 8, 200 + x * 8); - m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); - m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); - m_CompositionGen->ComposeTerrain(Desc); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Read the settings from the ini file: - m_CompositionGen->InitializeCompoGen(a_IniFile); + m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, *m_BiomeGen, *m_HeightGen, m_ChunkGenerator.GetSeed()); int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); if (CompoGenCacheSize > 1) @@ -402,15 +367,16 @@ void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) { int Seed = m_ChunkGenerator.GetSeed(); - AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); + eDimension Dimension = StringToDimension(a_IniFile.GetValue("General", "Dimension", "Overworld")); + AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); AStringVector Str = StringSplitAndTrim(Finishers, ","); for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) { // Finishers, alpha-sorted: if (NoCaseCompare(*itr, "BottomLava") == 0) { - int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10; + int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10; int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel)); } @@ -424,7 +390,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) } else if (NoCaseCompare(*itr, "LavaSprings") == 0) { - m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World)); + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, Dimension)); } else if (NoCaseCompare(*itr, "Lilypads") == 0) { @@ -448,7 +414,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) } else if (NoCaseCompare(*itr, "WaterSprings") == 0) { - m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World)); + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, Dimension)); } } // for itr - Str[] } diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h index 732f64303..29add0636 100644 --- a/src/Generating/ComposableGenerator.h +++ b/src/Generating/ComposableGenerator.h @@ -50,7 +50,7 @@ public: virtual void InitializeBiomeGen(cIniFile & a_IniFile) {} /// Creates the correct BiomeGen descendant based on the ini file settings and the seed provided. - /// a_CacheOffByDefault gets set to whether the cache should be enabled by default + /// a_CacheOffByDefault gets set to whether the cache should be disabled by default /// Used in BiomeVisualiser, too. /// Implemented in BioGen.cpp! static cBiomeGen * CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & a_CacheOffByDefault); @@ -77,6 +77,13 @@ public: /// Reads parameters from the ini file, prepares generator for use. virtual void InitializeHeightGen(cIniFile & a_IniFile) {} + + /** Creates the correct TerrainHeightGen descendant based on the ini file settings and the seed provided. + a_BiomeGen is the underlying biome generator, some height generators may depend on it to generate more biomes + a_CacheOffByDefault gets set to whether the cache should be disabled by default + Implemented in HeiGen.cpp! + */ + static cTerrainHeightGen * CreateHeightGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault); } ; @@ -97,6 +104,12 @@ public: /// Reads parameters from the ini file, prepares generator for use. virtual void InitializeCompoGen(cIniFile & a_IniFile) {} + + /** Creates the correct TerrainCompositionGen descendant based on the ini file settings and the seed provided. + a_BiomeGen is the underlying biome generator, some composition generators may depend on it to generate more biomes + a_HeightGen is the underlying height generator, some composition generators may depend on it providing additional values + */ + static cTerrainCompositionGen * CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed); } ; @@ -149,7 +162,7 @@ public: cComposableGenerator(cChunkGenerator & a_ChunkGenerator); virtual ~cComposableGenerator(); - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void Initialize(cIniFile & a_IniFile) override; virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 4ac6ff0f3..02045f76a 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -72,7 +72,7 @@ void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc) PosX = (int) floor(16 / Val1); } - // Calculate PosZ + // Calculate PosZ if (Val2 <= 1) { PosZ = (int) floor(Val2 * 16); @@ -640,7 +640,7 @@ void cFinishGenPreSimulator::StationarizeFluid( /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cFinishGenFluidSprings: -cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) : +cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension) : m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other m_HeightDistribution(255), m_Fluid(a_Fluid) @@ -649,7 +649,7 @@ cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cI AString SectionName = IsWater ? "WaterSprings" : "LavaSprings"; AString DefaultHeightDistribution; int DefaultChance = 0; - switch (a_World.GetDimension()) + switch (a_Dimension) { case dimNether: { diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index a444721f8..2e5732929 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -51,9 +51,9 @@ class cFinishGenNetherClumpFoliage : public cFinishGen { public: - cFinishGenNetherClumpFoliage(int a_Seed) : + cFinishGenNetherClumpFoliage(int a_Seed) : m_Noise(a_Seed), - m_Seed(a_Seed) + m_Seed(a_Seed) { } @@ -187,7 +187,7 @@ class cFinishGenFluidSprings : public cFinishGen { public: - cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World); + cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension); protected: diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 2bf641089..10710b4a1 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -7,6 +7,9 @@ #include "HeiGen.h" #include "../LinearUpscale.h" #include "inifile/iniFile.h" +#include "DistortedHeightmap.h" +#include "EndGen.h" +#include "Noise3DGenerator.h" @@ -14,6 +17,77 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cTerrainHeightGen: + +cTerrainHeightGen * cTerrainHeightGen::CreateHeightGen(cIniFile &a_IniFile, cBiomeGen & a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault) +{ + AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); + if (HeightGenName.empty()) + { + LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); + HeightGenName = "Biomal"; + } + + a_CacheOffByDefault = false; + cTerrainHeightGen * res = NULL; + if (NoCaseCompare(HeightGenName, "flat") == 0) + { + res = new cHeiGenFlat; + a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data + } + else if (NoCaseCompare(HeightGenName, "classic") == 0) + { + res = new cHeiGenClassic(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) + { + res = new cDistortedHeightmap(a_Seed, a_BiomeGen); + } + else if (NoCaseCompare(HeightGenName, "End") == 0) + { + res = new cEndGen(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) + { + res = new cNoise3DComposable(a_Seed); + } + else if (NoCaseCompare(HeightGenName, "biomal") == 0) + { + res = new cHeiGenBiomal(a_Seed, a_BiomeGen); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cHeiGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDef::HeightMap Heights; + res->GenHeightMap(x * 5, x * 5, Heights); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + else + { + // No match found, force-set the default and retry + LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); + a_IniFile.DeleteValue("Generator", "HeightGen"); + a_IniFile.SetValue("Generator", "HeightGen", "Biomal"); + return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault); + } + + // Read the settings: + res->InitializeHeightGen(a_IniFile); + + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenFlat: void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 2511bb656..afa40c647 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -150,10 +150,8 @@ cNoise3DGenerator::~cNoise3DGenerator() -void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +void cNoise3DGenerator::Initialize(cIniFile & a_IniFile) { - m_World = a_World; - // Params: m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h index 0d211cddc..42f61a854 100644 --- a/src/Generating/Noise3DGenerator.h +++ b/src/Generating/Noise3DGenerator.h @@ -24,7 +24,7 @@ public: cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator); virtual ~cNoise3DGenerator(); - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void Initialize(cIniFile & a_IniFile) override; virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; diff --git a/src/Globals.h b/src/Globals.h index f886ba2d0..d2080b8eb 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -233,6 +233,7 @@ public: // Common headers (part 2, with macros): #include "ChunkDef.h" +#include "BiomeDef.h" #include "BlockID.h" #include "Entities/Effects.h" diff --git a/src/Inventory.cpp b/src/Inventory.cpp index a7f77cf6d..0e1cedc85 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -83,7 +83,7 @@ int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int { NumLeft -= MaxStack; } - else if (Slot.IsStackableWith(a_ItemStack)) + else if (Slot.IsEqual(a_ItemStack)) { NumLeft -= MaxStack - Slot.m_ItemCount; } diff --git a/src/Item.cpp b/src/Item.cpp index a44515019..9170006b6 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -91,28 +91,6 @@ bool cItem::DamageItem(short a_Amount) -bool cItem::IsStackableWith(const cItem & a_OtherStack) const -{ - if (a_OtherStack.m_ItemType != m_ItemType) - { - return false; - } - if (a_OtherStack.m_ItemDamage != m_ItemDamage) - { - return false; - } - if (a_OtherStack.m_Enchantments != m_Enchantments) - { - return false; - } - - return true; -} - - - - - bool cItem::IsFullStack(void) const { return (m_ItemCount >= ItemHandler(m_ItemType)->GetMaxStackSize()); @@ -153,6 +131,14 @@ void cItem::GetJson(Json::Value & a_OutValue) const { a_OutValue["ench"] = Enchantments; } + if (!IsCustomNameEmpty()) + { + a_OutValue["Name"] = m_CustomName; + } + if (!IsLoreEmpty()) + { + a_OutValue["Lore"] = m_Lore; + } } } @@ -169,6 +155,8 @@ void cItem::FromJson(const Json::Value & a_Value) m_ItemDamage = (short)a_Value.get("Health", -1 ).asInt(); m_Enchantments.Clear(); m_Enchantments.AddFromString(a_Value.get("ench", "").asString()); + m_CustomName = a_Value.get("Name", "").asString(); + m_Lore = a_Value.get("Lore", "").asString(); } } diff --git a/src/Item.h b/src/Item.h index 64a30ade1..727965112 100644 --- a/src/Item.h +++ b/src/Item.h @@ -36,7 +36,9 @@ public: cItem(void) : m_ItemType(E_ITEM_EMPTY), m_ItemCount(0), - m_ItemDamage(0) + m_ItemDamage(0), + m_CustomName(""), + m_Lore("") { } @@ -46,12 +48,16 @@ public: short a_ItemType, char a_ItemCount = 1, short a_ItemDamage = 0, - const AString & a_Enchantments = "" + const AString & a_Enchantments = "", + const AString & a_CustomName = "", + const AString & a_Lore = "" ) : m_ItemType (a_ItemType), m_ItemCount (a_ItemCount), m_ItemDamage (a_ItemDamage), - m_Enchantments(a_Enchantments) + m_Enchantments(a_Enchantments), + m_CustomName (a_CustomName), + m_Lore (a_Lore) { if (!IsValidItem(m_ItemType)) { @@ -69,7 +75,9 @@ public: m_ItemType (a_CopyFrom.m_ItemType), m_ItemCount (a_CopyFrom.m_ItemCount), m_ItemDamage (a_CopyFrom.m_ItemDamage), - m_Enchantments(a_CopyFrom.m_Enchantments) + m_Enchantments(a_CopyFrom.m_Enchantments), + m_CustomName (a_CopyFrom.m_CustomName), + m_Lore (a_CopyFrom.m_Lore) { } @@ -80,6 +88,8 @@ public: m_ItemCount = 0; m_ItemDamage = 0; m_Enchantments.Clear(); + m_CustomName = ""; + m_Lore = ""; } @@ -96,13 +106,16 @@ public: return ((m_ItemType <= 0) || (m_ItemCount <= 0)); } - + /* Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored! + */ bool IsEqual(const cItem & a_Item) const { return ( IsSameType(a_Item) && (m_ItemDamage == a_Item.m_ItemDamage) && - (m_Enchantments == a_Item.m_Enchantments) + (m_Enchantments == a_Item.m_Enchantments) && + (m_CustomName == a_Item.m_CustomName) && + (m_Lore == a_Item.m_Lore) ); } @@ -111,7 +124,16 @@ public: { return (m_ItemType == a_Item.m_ItemType) || (IsEmpty() && a_Item.IsEmpty()); } - + + + bool IsBothNameAndLoreEmpty(void) const + { + return (m_CustomName.empty() && m_Lore.empty()); + } + + + bool IsCustomNameEmpty(void) const { return (m_CustomName.empty()); } + bool IsLoreEmpty(void) const { return (m_Lore.empty()); } /// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items cItem CopyOne(void) const; @@ -127,9 +149,6 @@ public: inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); } - /// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored! - bool IsStackableWith(const cItem & a_OtherStack) const; - /// Returns true if the item is stacked up to its maximum stacking. bool IsFullStack(void) const; @@ -155,6 +174,8 @@ public: short m_ItemType; char m_ItemCount; short m_ItemDamage; + AString m_CustomName; + AString m_Lore; cEnchantments m_Enchantments; }; // tolua_end diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index d2e6b1c69..e8b58695f 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -226,7 +226,7 @@ int cItemGrid::HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks) NumLeft -= MaxStack; } } - else if (m_Slots[i].IsStackableWith(a_ItemStack)) + else if (m_Slots[i].IsEqual(a_ItemStack)) { NumLeft -= MaxStack - m_Slots[i].m_ItemCount; } @@ -275,7 +275,7 @@ int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_Priorit (a_PrioritarySlot != -1) && ( m_Slots[a_PrioritarySlot].IsEmpty() || - m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack) + m_Slots[a_PrioritarySlot].IsEqual(a_ItemStack) ) ) { @@ -285,7 +285,7 @@ int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_Priorit // Scan existing stacks: for (int i = m_NumSlots - 1; i >= 0; i--) { - if (m_Slots[i].IsStackableWith(a_ItemStack)) + if (m_Slots[i].IsEqual(a_ItemStack)) { NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); } @@ -438,7 +438,7 @@ int cItemGrid::HowManyItems(const cItem & a_Item) int res = 0; for (int i = 0; i < m_NumSlots; i++) { - if (m_Slots[i].IsStackableWith(a_Item)) + if (m_Slots[i].IsEqual(a_Item)) { res += m_Slots[i].m_ItemCount; } diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h index ab4182eea..9b7c8bff8 100644 --- a/src/Items/ItemBed.h +++ b/src/Items/ItemBed.h @@ -37,7 +37,7 @@ public: return false; } - a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw()); // Check if there is empty space for the foot section: Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); diff --git a/src/Items/ItemComparator.h b/src/Items/ItemComparator.h index 3fbb7603d..3a5d1d200 100644 --- a/src/Items/ItemComparator.h +++ b/src/Items/ItemComparator.h @@ -30,7 +30,7 @@ public: ) override { a_BlockType = E_BLOCK_INACTIVE_COMPARATOR; - a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetYaw()); return true; } } ; diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h index f8eb31a49..4071f8c60 100644 --- a/src/Items/ItemMinecart.h +++ b/src/Items/ItemMinecart.h @@ -60,7 +60,7 @@ public: cMinecart * Minecart = NULL; switch (m_ItemType) { - case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break; + case E_ITEM_MINECART: Minecart = new cRideableMinecart (x, y, z, cItem(), 1); break; case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break; case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (x, y, z); break; case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (x, y, z); break; diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h index 38bf00ba6..de90c8075 100644 --- a/src/Items/ItemRedstoneDust.h +++ b/src/Items/ItemRedstoneDust.h @@ -27,7 +27,7 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) override { - if (!g_BlockIsTorchPlaceable[a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)]) // Some solid blocks, such as cocoa beans, are not suitable for dust + if (!g_BlockFullyOccupiesVoxel[a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)]) // Some solid blocks, such as cocoa beans, are not suitable for dust { return false; } diff --git a/src/Items/ItemRedstoneRepeater.h b/src/Items/ItemRedstoneRepeater.h index f69f24eb8..e71c8e672 100644 --- a/src/Items/ItemRedstoneRepeater.h +++ b/src/Items/ItemRedstoneRepeater.h @@ -30,7 +30,7 @@ public: ) override { a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF; - a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetYaw()); return true; } } ; diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h index 5ccd79e29..8c134ab83 100644 --- a/src/Items/ItemSign.h +++ b/src/Items/ItemSign.h @@ -34,7 +34,7 @@ public: { if (a_BlockFace == BLOCK_FACE_TOP) { - a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation()); + a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetYaw()); a_BlockType = E_BLOCK_SIGN_POST; } else diff --git a/src/Log.cpp b/src/Log.cpp index a0de4531b..2d6be0f59 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -147,11 +147,11 @@ void cLog::Log(const char * a_Format, va_list argList) -void cLog::Log(const char* a_Format, ...) +void cLog::Log(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); - Log( a_Format, argList ); + Log(a_Format, argList); va_end(argList); } @@ -159,9 +159,9 @@ void cLog::Log(const char* a_Format, ...) -void cLog::SimpleLog(const char* a_String) +void cLog::SimpleLog(const char * a_String) { - Log("%s", a_String ); + Log("%s", a_String); } @@ -14,11 +14,11 @@ private: public: cLog(const AString & a_FileName); ~cLog(); - void Log(const char* a_Format, va_list argList ); - void Log(const char* a_Format, ...); + void Log(const char * a_Format, va_list argList); + void Log(const char * a_Format, ...); // tolua_begin - void SimpleLog(const char* a_String); - void OpenLog( const char* a_FileName ); + void SimpleLog(const char * a_String); + void OpenLog(const char * a_FileName); void CloseLog(); void ClearLog(); static cLog* GetInstance(); diff --git a/src/MCServer.vcproj.user b/src/MCServer.vcproj.user new file mode 100644 index 000000000..b17909f71 --- /dev/null +++ b/src/MCServer.vcproj.user @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="UTF-8"?> +<VisualStudioUserFile + ProjectType="Visual C++" + Version="9,00" + ShowAllFiles="false" + > + <Configurations> + <Configuration + Name="Debug|Win32" + > + <DebugSettings + Command="$(TargetPath)" + WorkingDirectory="..\MCServer" + CommandArguments="" + Attach="false" + DebuggerType="3" + RemoteCommand="" + HttpUrl="" + PDBPath="" + SQLDebugging="" + Environment="" + EnvironmentMerge="true" + DebuggerFlavor="0" + MPIRunCommand="" + MPIRunArguments="" + MPIRunWorkingDirectory="" + ApplicationCommand="" + ApplicationArguments="" + ShimCommand="" + MPIAcceptMode="" + MPIAcceptFilter="" + /> + </Configuration> + <Configuration + Name="DebugProfile|Win32" + > + <DebugSettings + Command="$(TargetPath)" + WorkingDirectory="..\MCServer" + CommandArguments="" + Attach="false" + DebuggerType="3" + RemoteCommand="" + HttpUrl="" + PDBPath="" + SQLDebugging="" + Environment="" + EnvironmentMerge="true" + DebuggerFlavor="0" + MPIRunCommand="" + MPIRunArguments="" + MPIRunWorkingDirectory="" + ApplicationCommand="" + ApplicationArguments="" + ShimCommand="" + MPIAcceptMode="" + MPIAcceptFilter="" + /> + </Configuration> + <Configuration + Name="MinSizeRel|Win32" + > + <DebugSettings + Command="$(TargetPath)" + WorkingDirectory="..\MCServer" + CommandArguments="" + Attach="false" + DebuggerType="3" + RemoteCommand="" + HttpUrl="" + PDBPath="" + SQLDebugging="" + Environment="" + EnvironmentMerge="true" + DebuggerFlavor="0" + MPIRunCommand="" + MPIRunArguments="" + MPIRunWorkingDirectory="" + ApplicationCommand="" + ApplicationArguments="" + ShimCommand="" + MPIAcceptMode="" + MPIAcceptFilter="" + /> + </Configuration> + <Configuration + Name="Release|Win32" + > + <DebugSettings + Command="$(TargetPath)" + WorkingDirectory="..\MCServer" + CommandArguments="" + Attach="false" + DebuggerType="3" + RemoteCommand="" + HttpUrl="" + PDBPath="" + SQLDebugging="" + Environment="" + EnvironmentMerge="true" + DebuggerFlavor="0" + MPIRunCommand="" + MPIRunArguments="" + MPIRunWorkingDirectory="" + ApplicationCommand="" + ApplicationArguments="" + ShimCommand="" + MPIAcceptMode="" + MPIAcceptFilter="" + /> + </Configuration> + <Configuration + Name="ReleaseProfile|Win32" + > + <DebugSettings + Command="$(TargetPath)" + WorkingDirectory="..\MCServer" + CommandArguments="" + Attach="false" + DebuggerType="3" + Remote="1" + RemoteMachine="ASAGA" + RemoteCommand="" + HttpUrl="" + PDBPath="" + SQLDebugging="" + Environment="" + EnvironmentMerge="true" + DebuggerFlavor="0" + MPIRunCommand="" + MPIRunArguments="" + MPIRunWorkingDirectory="" + ApplicationCommand="" + ApplicationArguments="" + ShimCommand="" + MPIAcceptMode="" + MPIAcceptFilter="" + /> + </Configuration> + <Configuration + Name="RelWithDebInfo|Win32" + > + <DebugSettings + Command="$(TargetPath)" + WorkingDirectory="..\MCServer" + CommandArguments="" + Attach="false" + DebuggerType="3" + RemoteCommand="" + HttpUrl="" + PDBPath="" + SQLDebugging="" + Environment="" + EnvironmentMerge="true" + DebuggerFlavor="0" + MPIRunCommand="" + MPIRunArguments="" + MPIRunWorkingDirectory="" + ApplicationCommand="" + ApplicationArguments="" + ShimCommand="" + MPIAcceptMode="" + MPIAcceptFilter="" + /> + </Configuration> + </Configurations> +</VisualStudioUserFile> diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 76df76633..98b6c1d28 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -208,7 +208,7 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) Distance.Normalize(); VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch ); SetHeadYaw (Rotation); - SetRotation( Rotation ); + SetYaw( Rotation ); SetPitch( -Pitch ); } diff --git a/src/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp index 55454a4b5..08aec0c65 100644 --- a/src/OSSupport/BlockingTCPLink.cpp +++ b/src/OSSupport/BlockingTCPLink.cpp @@ -7,17 +7,6 @@ -#ifdef _WIN32 - #define MSG_NOSIGNAL (0) -#endif -#ifdef __MACH__ - #define MSG_NOSIGNAL (0) -#endif - - - - - cBlockingTCPLink::cBlockingTCPLink(void) { } diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp index f25f800c2..8ea5d8320 100644 --- a/src/OSSupport/Socket.cpp +++ b/src/OSSupport/Socket.cpp @@ -72,10 +72,6 @@ void cSocket::CloseSocket() #else // _WIN32 - if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH); - { - LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); - } if (close(m_Socket) != 0) { LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); @@ -368,7 +364,7 @@ int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags int cSocket::Send(const char * a_Buffer, unsigned int a_Length) { - return send(m_Socket, a_Buffer, a_Length, 0); + return send(m_Socket, a_Buffer, a_Length, MSG_NOSIGNAL); } diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h index 81bfd28fc..b86560de8 100644 --- a/src/OSSupport/Socket.h +++ b/src/OSSupport/Socket.h @@ -5,6 +5,18 @@ +// Windows and MacOSX don't have the MSG_NOSIGNAL flag +#if ( \ + defined(_WIN32) || \ + (defined(__APPLE__) && defined(__MACH__)) \ +) + #define MSG_NOSIGNAL (0) +#endif + + + + + class cSocket { public: diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index fdbffb3e9..3293da32c 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -87,6 +87,7 @@ public: virtual void SendPlayerMoveLook (void) = 0; virtual void SendPlayerPosition (void) = 0; virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; + virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0; virtual void SendRespawn (void) = 0; virtual void SendExperience (void) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index e49dd43ff..323a13992 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -354,8 +354,8 @@ void cProtocol125::SendEntityLook(const cEntity & a_Entity) cCSLock Lock(m_CSPacket); WriteByte(PACKET_ENT_LOOK); WriteInt (a_Entity.GetUniqueID()); - WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetYaw() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); Flush(); } @@ -423,8 +423,8 @@ void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, WriteByte(a_RelX); WriteByte(a_RelY); WriteByte(a_RelZ); - WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetYaw() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); Flush(); } @@ -664,7 +664,7 @@ void cProtocol125::SendPlayerMoveLook(void) WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block WriteDouble(Player->GetPosZ()); - WriteFloat ((float)(Player->GetRotation())); + WriteFloat ((float)(Player->GetYaw())); WriteFloat ((float)(Player->GetPitch())); WriteBool (Player->IsOnGround()); Flush(); @@ -694,8 +694,8 @@ void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) WriteInt ((int)(a_Player.GetPosX() * 32)); WriteInt ((int)(a_Player.GetPosY() * 32)); WriteInt ((int)(a_Player.GetPosZ() * 32)); - WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); - WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteByte ((char)((a_Player.GetYaw() / 360.f) * 256)); + WriteByte ((char)((a_Player.GetPitch() / 360.f) * 256)); WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); Flush(); } @@ -704,6 +704,20 @@ void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) +void cProtocol125::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_PLUGIN_MESSAGE); + WriteString(a_Channel); + WriteShort((short)a_Message.size()); + SendData(a_Message.data(), a_Message.size()); + Flush(); +} + + + + + void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) { cCSLock Lock(m_CSPacket); @@ -850,7 +864,7 @@ void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp WriteInt ((int)(a_Vehicle.GetPosY() * 32)); WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetYaw() / 360.f) * 256)); WriteInt (a_VehicleSubType); if (a_VehicleSubType != 0) { @@ -883,7 +897,7 @@ void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) WriteInt ((int)(floor(a_Entity.GetPosX() * 32))); WriteInt ((int)(floor(a_Entity.GetPosY() * 32))); WriteInt ((int)(floor(a_Entity.GetPosZ() * 32))); - WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte ((char)((a_Entity.GetYaw() / 360.f) * 256)); WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); Flush(); } diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 0b32137d8..d0e5c9428 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -63,6 +63,7 @@ public: virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendRespawn (void) override; virtual void SendExperience (void) override; diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 302d1298c..29fbb4bba 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -367,8 +367,8 @@ void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) WriteInt ((int)(a_Player.GetPosX() * 32)); WriteInt ((int)(a_Player.GetPosY() * 32)); WriteInt ((int)(a_Player.GetPosZ() * 32)); - WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); - WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteByte ((char)((a_Player.GetYaw() / 360.f) * 256)); + WriteByte ((char)((a_Player.GetPitch() / 360.f) * 256)); WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: WriteByte (0); // Index 0, byte (flags) @@ -421,8 +421,8 @@ void cProtocol132::SendSpawnMob(const cMonster & a_Mob) WriteInt (a_Mob.GetUniqueID()); WriteByte (a_Mob.GetMobType()); WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); - WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); - WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetYaw() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); WriteShort ((short)(a_Mob.GetSpeedX() * 400)); WriteShort ((short)(a_Mob.GetSpeedY() * 400)); diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp index 926fe6ee8..127ce9d4b 100644 --- a/src/Protocol/Protocol14x.cpp +++ b/src/Protocol/Protocol14x.cpp @@ -250,7 +250,7 @@ void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp WriteInt ((int)(a_Vehicle.GetPosY() * 32)); WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetYaw() / 360.f) * 256)); WriteInt (a_VehicleSubType); if (a_VehicleSubType != 0) { diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 8ec5dec29..5b3a79555 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -121,7 +121,7 @@ void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, cha void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) { - cPacketizer Pkt(*this, 0x24); // Block Break Animation packet + cPacketizer Pkt(*this, 0x25); // Block Break Animation packet Pkt.WriteInt(a_EntityID); Pkt.WriteInt(a_BlockX); Pkt.WriteInt(a_BlockY); @@ -216,8 +216,23 @@ void cProtocol172::SendDestroyEntity(const cEntity & a_Entity) void cProtocol172::SendDisconnect(const AString & a_Reason) { - cPacketizer Pkt(*this, 0x40); - Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str())); + switch (m_State) + { + case 2: + { + // During login: + cPacketizer Pkt(*this, 0); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str())); + break; + } + case 3: + { + // In-game: + cPacketizer Pkt(*this, 0x40); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str())); + break; + } + } } @@ -628,6 +643,18 @@ void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player) +void cProtocol172::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +{ + cPacketizer Pkt(*this, 0x3f); + Pkt.WriteString(a_Channel); + Pkt.WriteShort((short)a_Message.size()); + Pkt.WriteBuf(a_Message.data(), a_Message.size()); +} + + + + + void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) { cPacketizer Pkt(*this, 0x1E); @@ -1010,11 +1037,31 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) return; } - HandlePacket(bb, PacketType); + if (!HandlePacket(bb, PacketType)) + { + // Unknown packet, already been reported, but without the length. Log the length here: + LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, PacketLen); + + #ifdef _DEBUG + // Dump the packet contents into the log: + bb.ResetRead(); + AString Packet; + bb.ReadAll(Packet); + Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection + AString Out; + CreateHexDump(Out, Packet.data(), (int)Packet.size(), 24); + LOGD("Packet contents:\n%s", Out.c_str()); + #endif // _DEBUG + + return; + } if (bb.GetReadableSpace() != 1) { // Read more or less than packet length, report as error + LOGWARNING("Protocol 1.7: Wrong number of bytes read for packet 0x%x, state %d. Read %u bytes, packet contained %u bytes", + PacketType, m_State, bb.GetUsedSpace() - bb.GetReadableSpace(), PacketLen + ); ASSERT(!"Read wrong number of bytes!"); m_Client->PacketError(PacketType); } @@ -1024,7 +1071,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) -void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) +bool cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) { switch (m_State) { @@ -1033,8 +1080,8 @@ void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) // Status switch (a_PacketType) { - case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return; - case 0x01: HandlePacketStatusPing (a_ByteBuffer); return; + case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return true; + case 0x01: HandlePacketStatusPing (a_ByteBuffer); return true; } break; } @@ -1044,8 +1091,8 @@ void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) // Login switch (a_PacketType) { - case 0x00: HandlePacketLoginStart (a_ByteBuffer); return; - case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return; + case 0x00: HandlePacketLoginStart (a_ByteBuffer); return true; + case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return true; } break; } @@ -1055,36 +1102,54 @@ void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) // Game switch (a_PacketType) { - case 0x00: HandlePacketKeepAlive (a_ByteBuffer); return; - case 0x01: HandlePacketChatMessage (a_ByteBuffer); return; - case 0x02: HandlePacketUseEntity (a_ByteBuffer); return; - case 0x03: HandlePacketPlayer (a_ByteBuffer); return; - case 0x04: HandlePacketPlayerPos (a_ByteBuffer); return; - case 0x05: HandlePacketPlayerLook (a_ByteBuffer); return; - case 0x06: HandlePacketPlayerPosLook (a_ByteBuffer); return; - case 0x07: HandlePacketBlockDig (a_ByteBuffer); return; - case 0x08: HandlePacketBlockPlace (a_ByteBuffer); return; - case 0x09: HandlePacketSlotSelect (a_ByteBuffer); return; - case 0x0a: HandlePacketAnimation (a_ByteBuffer); return; - case 0x0b: HandlePacketEntityAction (a_ByteBuffer); return; - case 0x0c: HandlePacketSteerVehicle (a_ByteBuffer); return; - case 0x0d: HandlePacketWindowClose (a_ByteBuffer); return; - case 0x0e: HandlePacketWindowClick (a_ByteBuffer); return; + case 0x00: HandlePacketKeepAlive (a_ByteBuffer); return true; + case 0x01: HandlePacketChatMessage (a_ByteBuffer); return true; + case 0x02: HandlePacketUseEntity (a_ByteBuffer); return true; + case 0x03: HandlePacketPlayer (a_ByteBuffer); return true; + case 0x04: HandlePacketPlayerPos (a_ByteBuffer); return true; + case 0x05: HandlePacketPlayerLook (a_ByteBuffer); return true; + case 0x06: HandlePacketPlayerPosLook (a_ByteBuffer); return true; + case 0x07: HandlePacketBlockDig (a_ByteBuffer); return true; + case 0x08: HandlePacketBlockPlace (a_ByteBuffer); return true; + case 0x09: HandlePacketSlotSelect (a_ByteBuffer); return true; + case 0x0a: HandlePacketAnimation (a_ByteBuffer); return true; + case 0x0b: HandlePacketEntityAction (a_ByteBuffer); return true; + case 0x0c: HandlePacketSteerVehicle (a_ByteBuffer); return true; + case 0x0d: HandlePacketWindowClose (a_ByteBuffer); return true; + case 0x0e: HandlePacketWindowClick (a_ByteBuffer); return true; case 0x0f: // Confirm transaction - not used in MCS - case 0x10: HandlePacketCreativeInventoryAction(a_ByteBuffer); return; - case 0x12: HandlePacketUpdateSign (a_ByteBuffer); return; - case 0x13: HandlePacketPlayerAbilities (a_ByteBuffer); return; - case 0x14: HandlePacketTabComplete (a_ByteBuffer); return; - case 0x15: HandlePacketClientSettings (a_ByteBuffer); return; - case 0x16: HandlePacketClientStatus (a_ByteBuffer); return; - case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return; + case 0x10: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true; + case 0x12: HandlePacketUpdateSign (a_ByteBuffer); return true; + case 0x13: HandlePacketPlayerAbilities (a_ByteBuffer); return true; + case 0x14: HandlePacketTabComplete (a_ByteBuffer); return true; + case 0x15: HandlePacketClientSettings (a_ByteBuffer); return true; + case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true; + case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true; } break; } + default: + { + // Received a packet in an unknown state, report: + LOGWARNING("Received a packet in an unknown protocol state %d. Ignoring further packets.", m_State); + + // Cannot kick the client - we don't know this state and thus the packet number for the kick packet + + // Switch to a state when all further packets are silently ignored: + m_State = 255; + return false; + } + case 255: + { + // This is the state used for "not processing packets anymore" when we receive a bad packet from a client. + // Do not output anything (the caller will do that for us), just return failure + return false; + } } // switch (m_State) - // Unknown packet type, report to the client: + // Unknown packet type, report to the ClientHandle: m_Client->PacketUnknown(a_PacketType); + return false; } @@ -1144,6 +1209,12 @@ void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer) // TODO: Protocol encryption should be set up here if not localhost / auth + if (!m_Client->HandleHandshake(Username)) + { + // The client is not welcome here, they have been sent a Kick packet already + return; + } + // Send login success: { cPacketizer Pkt(*this, 0x02); // Login success packet @@ -1614,7 +1685,7 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) return; } - // Load enchantments from the NBT: + // Load enchantments and custom display names from the NBT data: for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) { if ( @@ -1627,6 +1698,27 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) { a_Item.m_Enchantments.ParseFromNBT(NBT, tag); } + else if ((NBT.GetType(tag) == TAG_Compound) && (NBT.GetName(tag) == "display")) // Custom name and lore tag + { + for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag)) + { + if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag + { + a_Item.m_CustomName = NBT.GetString(displaytag); + } + else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag + { + AString Lore; + + for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings + { + AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;) + } + + a_Item.m_Lore = Lore; + } + } + } } } @@ -1678,16 +1770,45 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) WriteByte (a_Item.m_ItemCount); WriteShort(a_Item.m_ItemDamage); - if (a_Item.m_Enchantments.IsEmpty()) + if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty()) { WriteShort(-1); return; } - // Send the enchantments: + // Send the enchantments and custom names: cFastNBTWriter Writer; - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + if (!a_Item.m_Enchantments.IsEmpty()) + { + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + } + if (!a_Item.IsBothNameAndLoreEmpty()) + { + Writer.BeginCompound("display"); + if (!a_Item.IsCustomNameEmpty()) + { + Writer.AddString("Name", a_Item.m_CustomName.c_str()); + } + if (!a_Item.IsLoreEmpty()) + { + Writer.BeginList("Lore", TAG_String); + + AStringVector Decls = StringSplit(a_Item.m_Lore, "`"); + for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr) + { + if (itr->empty()) + { + // The decl is empty (two `s), ignore + continue; + } + Writer.AddString("", itr->c_str()); + } + + Writer.EndList(); + } + Writer.EndCompound(); + } Writer.Finish(); AString Compressed; CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); @@ -1769,8 +1890,23 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) WriteInt(1); // Shaking direction, doesn't seem to affect anything WriteByte(0x73); WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer - - if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) + + if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpNone) + { + cRideableMinecart & RideableMinecart = ((cRideableMinecart &)a_Entity); + if (!RideableMinecart.GetContent().IsEmpty()) + { + WriteByte(0x54); + int Content = RideableMinecart.GetContent().m_ItemType; + Content |= RideableMinecart.GetContent().m_ItemDamage << 8; + WriteInt(Content); + WriteByte(0x55); + WriteInt(RideableMinecart.GetBlockHeight()); + WriteByte(0x56); + WriteByte(1); + } + } + else if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) { WriteByte(0x10); WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 23ff2365d..07dba834b 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -86,6 +86,7 @@ public: virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendRespawn (void) override; virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 @@ -221,8 +222,10 @@ protected: /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets void AddReceivedData(const char * a_Data, int a_Size); - /// Reads and handles the packet. The packet length and type have already been read. - void HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType); + /** Reads and handles the packet. The packet length and type have already been read. + Returns true if the packet was understood, false if it was an unknown packet + */ + bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType); // Packet handlers while in the Status state (m_State == 1): void HandlePacketStatusPing (cByteBuffer & a_ByteBuffer); diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index e2ea0e6e5..a21f4f042 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -476,6 +476,16 @@ void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) +void cProtocolRecognizer::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPluginMessage(a_Channel, a_Message); +} + + + + + void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) { ASSERT(m_Protocol != NULL); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index fbcf59f3b..e94f4cde8 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -98,6 +98,7 @@ public: virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendRespawn (void) override; virtual void SendExperience (void) override; diff --git a/src/Resources/MCServer.rc b/src/Resources/MCServer.rc new file mode 100644 index 000000000..e0fbbea5d --- /dev/null +++ b/src/Resources/MCServer.rc @@ -0,0 +1,17 @@ +// Generated by ResEdit 1.5.11 +// Copyright (C) 2006-2012 +// http://www.resedit.net + +#include <windows.h> +#include <commctrl.h> +#include <richedit.h> +#include "resource_MCServer.h" + + + + +// +// Icon resources +// +LANGUAGE 9, SUBLANG_DEFAULT +IDI_ICON1 ICON "icon.ico" diff --git a/src/Resources/icon.ico b/src/Resources/icon.ico Binary files differnew file mode 100644 index 000000000..4024523a1 --- /dev/null +++ b/src/Resources/icon.ico diff --git a/src/Resources/icon_128.png b/src/Resources/icon_128.png Binary files differnew file mode 100644 index 000000000..87d939a04 --- /dev/null +++ b/src/Resources/icon_128.png diff --git a/src/Resources/icon_256.png b/src/Resources/icon_256.png Binary files differnew file mode 100644 index 000000000..9a77a490f --- /dev/null +++ b/src/Resources/icon_256.png diff --git a/src/Resources/resource_MCServer.h b/src/Resources/resource_MCServer.h new file mode 100644 index 000000000..42f6c4eaf --- /dev/null +++ b/src/Resources/resource_MCServer.h @@ -0,0 +1,5 @@ +#ifndef IDC_STATIC +#define IDC_STATIC (-1) +#endif + +#define IDI_ICON1 101 diff --git a/src/Server.h b/src/Server.h index d79417fb0..703a7077e 100644 --- a/src/Server.h +++ b/src/Server.h @@ -107,7 +107,7 @@ public: // tolua_export /// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players void PlayerDestroying(const cPlayer * a_Player); - /* Returns base64 encoded favicon data (obtained from favicon.png) */ + /** Returns base64 encoded favicon data (obtained from favicon.png) */ const AString & GetFaviconData(void) const { return m_FaviconData; } CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index f65908729..469680098 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -44,83 +44,46 @@ void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChu int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + + BLOCKTYPE Block; + NIBBLETYPE Meta; + a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); - if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ))) - { - return; - } - - // Check for duplicates: - cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); - for (cRedstoneSimulatorChunkData::const_iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) - { - if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) - { - return; - } - } - - ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ)); -} - - - - - -void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) -{ - cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); - if (ChunkData.empty()) - { - return; - } - - int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; - int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; + // Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid + // Checking only when a block is changed, as opposed to every tick, also improves performance - // Check to see if PoweredBlocks have invalid items (source is air or unpowered) - for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end();) + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) { - int RelX = itr->a_SourcePos.x - a_ChunkX * cChunkDef::Width; - int RelZ = itr->a_SourcePos.z - a_ChunkZ * cChunkDef::Width; - int DestRelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width; - int DestRelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width; - - BLOCKTYPE SourceBlockType; - NIBBLETYPE SourceBlockMeta; - BLOCKTYPE DestBlockType; - if ( - !a_Chunk->UnboundedRelGetBlock(RelX, itr->a_SourcePos.y, RelZ, SourceBlockType, SourceBlockMeta) || - !a_Chunk->UnboundedRelGetBlockType(DestRelX, itr->a_BlockPos.y, DestRelZ, DestBlockType) - ) + if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } - if (SourceBlockType != itr->a_SourceBlock) + if (!IsPotentialSource(Block)) { - LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past block type mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); - itr = m_PoweredBlocks.erase(itr); + LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); + m_PoweredBlocks.erase(itr); + break; } else if ( // Changeable sources - ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) || - ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) || - ((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (SourceBlockMeta & 0x08) == 0x08) || - (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta))) || - (((SourceBlockType == E_BLOCK_STONE_PRESSURE_PLATE) || (SourceBlockType == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (SourceBlockMeta == 0)) + ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || + ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || + ((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0) || + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || + (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) ) { - LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); - itr = m_PoweredBlocks.erase(itr); + LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); + m_PoweredBlocks.erase(itr); + break; } - else if (SourceBlockType == E_BLOCK_DAYLIGHT_SENSOR) + else if (Block == E_BLOCK_DAYLIGHT_SENSOR) { if (!a_Chunk->IsLightValid()) { - m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); - ++itr; - continue; + m_World.QueueLightChunk(a_Chunk->GetPosX(), a_Chunk->GetPosZ()); + break; } else { @@ -130,126 +93,125 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c if (a_Chunk->GetTimeAlteredLight(SkyLight) <= 8) // Could use SkyLight - m_World.GetSkyDarkness(); { LOGD("cRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level"); - itr = m_PoweredBlocks.erase(itr); - } - else - { - ++itr; - continue; + m_PoweredBlocks.erase(itr); + break; } } } - else if ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (DestBlockType == E_BLOCK_REDSTONE_WIRE)) + } + + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + { + if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { - // It is simply not allowed that a wire powers another wire, presuming that data here is sane and a dest and source are beside each other - LOGD("cRedstoneSimulator: Erased redstone wire from powered blocks list because its source was also wire"); - itr = m_PoweredBlocks.erase(itr); + if (!IsPotentialSource(Block)) + { + LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); + m_LinkedPoweredBlocks.erase(itr); + break; + } + else if ( + // Things that can send power through a block but which depends on meta + ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || + ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) + ) + { + LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); + m_LinkedPoweredBlocks.erase(itr); + break; + } } - else + else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { - ++itr; + if (!IsViableMiddleBlock(Block)) + { + LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); + m_LinkedPoweredBlocks.erase(itr); + break; + } } } - // Check to see if LinkedPoweredBlocks have invalid items: source, block powered through, or power destination block has changed - for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end();) + for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) { - int RelX = itr->a_SourcePos.x - a_ChunkX * cChunkDef::Width; - int RelZ = itr->a_SourcePos.z - a_ChunkZ * cChunkDef::Width; - int MidRelX = itr->a_MiddlePos.x - a_ChunkX * cChunkDef::Width; - int MidRelZ = itr->a_MiddlePos.z - a_ChunkZ * cChunkDef::Width; - - BLOCKTYPE SourceBlockType; - NIBBLETYPE SourceBlockMeta; - BLOCKTYPE MiddleBlockType; - if ( - !a_Chunk->UnboundedRelGetBlock(RelX, itr->a_SourcePos.y, RelZ, SourceBlockType, SourceBlockMeta) || - !a_Chunk->UnboundedRelGetBlockType(MidRelX, itr->a_MiddlePos.y, MidRelZ, MiddleBlockType) - ) + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } - if (SourceBlockType != itr->a_SourceBlock) - { - LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past block type mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); - itr = m_LinkedPoweredBlocks.erase(itr); - } - else if (MiddleBlockType != itr->a_MiddleBlock) - { - LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past middle block mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); - itr = m_LinkedPoweredBlocks.erase(itr); - } - else if ( - // Things that can send power through a block but which depends on meta - ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) || - ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) || - (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta))) - ) + if (!IsAllowedBlock(Block)) { - LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str()); - itr = m_LinkedPoweredBlocks.erase(itr); - } - else - { - ++itr; + LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); + m_SimulatedPlayerToggleableBlocks.erase(itr); + break; } } - for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end();) + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) { - int RelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width; - int RelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width; - - BLOCKTYPE SourceBlockType; - if (!a_Chunk->UnboundedRelGetBlockType(RelX, itr->a_BlockPos.y, RelZ, SourceBlockType)) + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } - else if (!IsAllowedBlock(SourceBlockType)) + + if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF)) { - LOGD("cRedstoneSimulator: Erased block %s from toggleable simulated list as block is no longer redstone", ItemToFullString(SourceBlockType).c_str()); - itr = m_SimulatedPlayerToggleableBlocks.erase(itr); + m_RepeatersDelayList.erase(itr); + break; } - else + } + + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) + { + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block { - ++itr; + if (!IsAllowedBlock(Block)) + { + ChunkData.erase(itr); // The new blocktype is not redstone; it must be removed from this list + } + else + { + itr->Data = Block; // Update block information + } + return; } } - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end();) + if (!IsAllowedBlock(Block)) { - int RelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width; - int RelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width; + return; + } - BLOCKTYPE SourceBlockType; - if (!a_Chunk->UnboundedRelGetBlockType(RelX, itr->a_BlockPos.y, RelZ, SourceBlockType)) - { - continue; - } + ChunkData.push_back(cCoordWithBlock(RelX, a_BlockY, RelZ, Block)); +} - if ((SourceBlockType != E_BLOCK_REDSTONE_REPEATER_ON) && (SourceBlockType != E_BLOCK_REDSTONE_REPEATER_OFF)) - { - itr = m_RepeatersDelayList.erase(itr); - continue; - } - ++itr; - } - for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;) + + +void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + // We still attempt to simulate all blocks in the chunk every tick, because of outside influence that needs to be taken into account + // For example, repeaters need to be ticked, pressure plates checked for entities, daylight sensor checked for light changes, etc. + // The easiest way to make this more efficient is probably just to reduce code within the handlers that put too much strain on server, like getting or setting blocks + // A marking dirty system might be a TODO for later on, perhaps + + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + if (ChunkData.empty()) { - BLOCKTYPE BlockType = a_Chunk->GetBlock(dataitr->x, dataitr->y, dataitr->z); - if (!IsAllowedBlock(BlockType)) - { - dataitr = ChunkData.erase(dataitr); - continue; - } + return; + } + + int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; + int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; - // PoweredBlock and LinkedPoweredBlock list was fine, now to the actual handling + for (cRedstoneSimulatorChunkData::const_iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end(); ++dataitr) + { int a_X = BaseX + dataitr->x; int a_Z = BaseZ + dataitr->z; - switch (BlockType) + switch (dataitr->Data) { case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break; case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break; @@ -262,19 +224,19 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c case E_BLOCK_REDSTONE_TORCH_OFF: case E_BLOCK_REDSTONE_TORCH_ON: { - HandleRedstoneTorch(a_X, dataitr->y, a_Z, BlockType); + HandleRedstoneTorch(a_X, dataitr->y, a_Z, dataitr->Data); break; } case E_BLOCK_STONE_BUTTON: case E_BLOCK_WOODEN_BUTTON: { - HandleRedstoneButton(a_X, dataitr->y, a_Z, BlockType); + HandleRedstoneButton(a_X, dataitr->y, a_Z, dataitr->Data); break; } case E_BLOCK_REDSTONE_REPEATER_OFF: case E_BLOCK_REDSTONE_REPEATER_ON: { - HandleRedstoneRepeater(a_X, dataitr->y, a_Z, BlockType); + HandleRedstoneRepeater(a_X, dataitr->y, a_Z, dataitr->Data); break; } case E_BLOCK_PISTON: @@ -286,7 +248,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c case E_BLOCK_REDSTONE_LAMP_OFF: case E_BLOCK_REDSTONE_LAMP_ON: { - HandleRedstoneLamp(a_X, dataitr->y, a_Z, BlockType); + HandleRedstoneLamp(a_X, dataitr->y, a_Z, dataitr->Data); break; } case E_BLOCK_DISPENSER: @@ -305,18 +267,16 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c case E_BLOCK_DETECTOR_RAIL: case E_BLOCK_POWERED_RAIL: { - HandleRail(a_X, dataitr->y, a_Z, BlockType); + HandleRail(a_X, dataitr->y, a_Z, dataitr->Data); break; } case E_BLOCK_WOODEN_PRESSURE_PLATE: case E_BLOCK_STONE_PRESSURE_PLATE: { - HandlePressurePlate(a_X, dataitr->y, a_Z, BlockType); + HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data); break; } } - - ++dataitr; } } @@ -487,7 +447,7 @@ void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_Bl }; // Check to see if directly beside a power source - if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + if (IsWirePowered(a_BlockX, a_BlockY, a_BlockZ)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power } @@ -545,7 +505,7 @@ void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_Bl // transferring power to other wires around. // However, self not directly powered anymore, so source must have been removed, // therefore, self must be set to meta zero - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0); + m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE, 0); // SetMeta & WakeUpSims doesn't seem to work here, so SetBlock return; // No need to process block power sets because self not powered } else @@ -693,7 +653,7 @@ void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int // Apparently, incrementing ticks only works reliably here, and not in SimChunk; // With a world with lots of redstone, the repeaters simply do not delay // I am confounded to say why. Perhaps optimisation failure. - LOGD("Incremented a repeater @ %i %i %i | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); + LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); itr->a_ElapsedTicks++; } } @@ -781,48 +741,20 @@ void cRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ) void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ) { - if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { - // Block position is located at top half of door - // Is Y - 1 both within world boundaries, a door block, and the bottom half of a door? - // The bottom half stores the open/closed information - if ( - (a_BlockY - 1 >= 0) && - ((m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) || (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_IRON_DOOR)) && - (m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ & 0x08) == 0) - ) + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) { - if ((m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ) & 0x04) == 0) // Closed door? - { - if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open - { - cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); - } - } - else // Opened door - { - if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so - { - cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); - } - } + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } else { - if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x04) == 0) // Closed door? - { - if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open - { - cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); - } - } - else // Opened door + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) { - if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so - { - cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); - } + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } } @@ -970,6 +902,7 @@ void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_B else { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); + m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); } break; } @@ -1032,6 +965,7 @@ void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_B else { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); + m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); } break; } @@ -1188,6 +1122,33 @@ bool cRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, int a_Block +bool cRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } + + if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE) + { + return true; + } + } + + for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } + + if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE) + { + return true; + } + } + return false; // Source was in front of the piston's front face +} + + + + bool cRedstoneSimulator::AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered) { @@ -1333,11 +1294,6 @@ void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_Block // Don't set air, fixes some bugs (wires powering themselves) return; } - if ((Block == E_BLOCK_REDSTONE_WIRE) && (a_SourceBlock == E_BLOCK_REDSTONE_WIRE)) - { - // Wires cannot power themselves normally, instead, the wire handler will manually set meta - return; - } for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list { @@ -1354,7 +1310,6 @@ void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_Block sPoweredBlocks RC; RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); - RC.a_SourceBlock = a_SourceBlock; m_PoweredBlocks.push_back(RC); } @@ -1379,11 +1334,6 @@ void cRedstoneSimulator::SetBlockLinkedPowered( { return; } - if ((a_SourceBlock == E_BLOCK_REDSTONE_WIRE) && (DestBlock == E_BLOCK_REDSTONE_WIRE)) - { - // Wires cannot power another wire through a block - return; - } for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list { @@ -1402,8 +1352,6 @@ void cRedstoneSimulator::SetBlockLinkedPowered( RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ); RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); - RC.a_SourceBlock = a_SourceBlock; - RC.a_MiddleBlock = a_MiddleBlock; m_LinkedPoweredBlocks.push_back(RC); } diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h index 1080c3f81..63a5be3d3 100644 --- a/src/Simulator/RedstoneSimulator.h +++ b/src/Simulator/RedstoneSimulator.h @@ -4,7 +4,7 @@ #include "Simulator.h" /// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used -typedef cCoordWithIntList cRedstoneSimulatorChunkData; +typedef cCoordWithBlockVector cRedstoneSimulatorChunkData; @@ -39,7 +39,6 @@ private: { Vector3i a_BlockPos; // Position of powered block Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos - BLOCKTYPE a_SourceBlock; // The source block type (for pistons pushing away sources and replacing with non source etc.) }; struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side) @@ -47,8 +46,6 @@ private: Vector3i a_BlockPos; Vector3i a_MiddlePos; Vector3i a_SourcePos; - BLOCKTYPE a_SourceBlock; - BLOCKTYPE a_MiddleBlock; }; struct sSimulatedPlayerToggleableList @@ -81,102 +78,89 @@ private: // In addition to being non-performant, it would stop the player from actually breaking said device /* ====== SOURCES ====== */ - /// <summary>Handles the redstone torch</summary> + /** Handles the redstone torch */ void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); - /// <summary>Handles the redstone block</summary> + /** Handles the redstone block */ void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles levers</summary> + /** Handles levers */ void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles buttons</summary> + /** Handles buttons */ void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); - /// <summary>Handles daylight sensors</summary> + /** Handles daylight sensors */ void HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles pressure plates</summary> + /** Handles pressure plates */ void HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); /* ==================== */ /* ====== CARRIERS ====== */ - /// <summary>Handles redstone wire</summary> + /** Handles redstone wire */ void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles repeaters</summary> + /** Handles repeaters */ void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); /* ====================== */ /* ====== DEVICES ====== */ - /// <summary>Handles pistons</summary> + /** Handles pistons */ void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles dispensers and droppers</summary> + /** Handles dispensers and droppers */ void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles TNT (exploding)</summary> + /** Handles TNT (exploding) */ void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles redstone lamps</summary> + /** Handles redstone lamps */ void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); - /// <summary>Handles doords</summary> + /** Handles doords */ void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles activator, detector, and powered rails</summary> + /** Handles activator, detector, and powered rails */ void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); - /// <summary>Handles trapdoors</summary> + /** Handles trapdoors */ void HandleTrapdoor(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Handles noteblocks</summary> + /** Handles noteblocks */ void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ); /* ===================== */ /* ====== Helper functions ====== */ - /// <summary>Marks a block as powered</summary> + /** Marks a block as powered */ void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock); - /// <summary>Marks a block as being powered through another block</summary> + /** Marks a block as being powered through another block */ void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock); - /// <summary>Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back</summary> + /** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */ void SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered); - /// <summary>Marks the second block in a direction as linked powered</summary> + /** Marks the second block in a direction as linked powered */ void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceBlock); - /// <summary>Marks all blocks immediately surrounding a coordinate as powered</summary> + /** Marks all blocks immediately surrounding a coordinate as powered */ void SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock); - /// <summary>Queues a repeater to be powered or unpowered</summary> + /** Queues a repeater to be powered or unpowered */ void QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn); - /// <summary>Returns if a coordinate is powered or linked powered</summary> + /** Returns if a coordinate is powered or linked powered */ bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { return AreCoordsDirectlyPowered(a_BlockX, a_BlockY, a_BlockZ) || AreCoordsLinkedPowered(a_BlockX, a_BlockY, a_BlockZ); } - /// <summary>Returns if a coordinate is in the directly powered blocks list</summary> + /** Returns if a coordinate is in the directly powered blocks list */ bool AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Returns if a coordinate is in the indirectly powered blocks list</summary> + /** Returns if a coordinate is in the indirectly powered blocks list */ bool AreCoordsLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Returns if a coordinate was marked as simulated (for blocks toggleable by players)</summary> + /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */ bool AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered); - /// <summary>Returns if a repeater is powered</summary> + /** Returns if a repeater is powered */ bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); - /// <summary>Returns if a piston is powered</summary> + /** Returns if a piston is powered */ bool IsPistonPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); + /** Returns if a wire is powered + The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire + */ + bool IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ); - /// <summary>Returns if lever metadata marks it as emitting power</summary> + + /** Returns if lever metadata marks it as emitting power */ bool IsLeverOn(NIBBLETYPE a_BlockMeta); - /// <summary>Returns if button metadata marks it as emitting power</summary> + /** Returns if button metadata marks it as emitting power */ bool IsButtonOn(NIBBLETYPE a_BlockMeta); /* ============================== */ /* ====== Misc Functions ====== */ - /// <summary>Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation</summary> - inline static bool IsViableMiddleBlock(BLOCKTYPE Block) - { - if (!g_BlockIsSolid[Block]) { return false; } + /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */ + inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return g_BlockFullyOccupiesVoxel[Block]; } - switch (Block) - { - // Add SOLID but not viable middle blocks here - case E_BLOCK_PISTON: - case E_BLOCK_PISTON_EXTENSION: - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_DAYLIGHT_SENSOR: - { - return false; - } - default: return true; - } - } - - /// <summary>Returns if a block is a mechanism (something that accepts power and does something)</summary> + /** Returns if a block is a mechanism (something that accepts power and does something) */ inline static bool IsMechanism(BLOCKTYPE Block) { switch (Block) @@ -205,16 +189,16 @@ private: } } - /// <summary>Returns if a block has the potential to output power</summary> + /** Returns if a block has the potential to output power */ inline static bool IsPotentialSource(BLOCKTYPE Block) { switch (Block) { + case E_BLOCK_DETECTOR_RAIL: case E_BLOCK_DAYLIGHT_SENSOR: case E_BLOCK_WOODEN_BUTTON: case E_BLOCK_STONE_BUTTON: case E_BLOCK_REDSTONE_WIRE: - case E_BLOCK_REDSTONE_TORCH_OFF: case E_BLOCK_REDSTONE_TORCH_ON: case E_BLOCK_LEVER: case E_BLOCK_REDSTONE_REPEATER_ON: @@ -227,7 +211,7 @@ private: } } - /// <summary>Returns if a block is any sort of redstone device</summary> + /** Returns if a block is any sort of redstone device */ inline static bool IsRedstone(BLOCKTYPE Block) { switch (Block) @@ -248,6 +232,7 @@ private: case E_BLOCK_LEVER: case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_POWERED_RAIL: case E_BLOCK_REDSTONE_LAMP_OFF: case E_BLOCK_REDSTONE_LAMP_ON: case E_BLOCK_REDSTONE_REPEATER_OFF: diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp index e15058840..5b9a3bb0a 100644 --- a/src/StringCompression.cpp +++ b/src/StringCompression.cpp @@ -11,7 +11,7 @@ /// Compresses a_Data into a_Compressed; returns Z_XXX error constants same as zlib's compress2() -int CompressString(const char * a_Data, int a_Length, AString & a_Compressed) +int CompressString(const char * a_Data, int a_Length, AString & a_Compressed, int a_Factor) { uLongf CompressedSize = compressBound(a_Length); @@ -19,7 +19,7 @@ int CompressString(const char * a_Data, int a_Length, AString & a_Compressed) // It saves us one allocation and one memcpy of the entire compressed data // It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010) a_Compressed.resize(CompressedSize); - int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, Z_DEFAULT_COMPRESSION); + int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, a_Factor); if (errorcode != Z_OK) { return errorcode; diff --git a/src/StringCompression.h b/src/StringCompression.h index 459e8f568..3f4e12d2d 100644 --- a/src/StringCompression.h +++ b/src/StringCompression.h @@ -10,7 +10,7 @@ /// Compresses a_Data into a_Compressed using ZLIB; returns Z_XXX error constants same as zlib's compress2() -extern int CompressString(const char * a_Data, int a_Length, AString & a_Compressed); +extern int CompressString(const char * a_Data, int a_Length, AString & a_Compressed, int a_Factor); /// Uncompresses a_Data into a_Uncompressed; returns Z_XXX error constants same as zlib's decompress() extern int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize); diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 0b38297ef..0dbd41c12 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -18,27 +18,40 @@ -AString & AppendVPrintf(AString & str, const char *format, va_list args) +AString & AppendVPrintf(AString & str, const char * format, va_list args) { ASSERT(format != NULL); char buffer[2048]; size_t len; + #ifdef va_copy + va_list argsCopy; + va_copy(argsCopy, args); + #else + #define argsCopy args + #endif #ifdef _MSC_VER // MS CRT provides secure printf that doesn't behave like in the C99 standard - if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, args)) != -1) + if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, argsCopy)) != -1) #else // _MSC_VER - if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, args)) < ARRAYCOUNT(buffer)) + if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < ARRAYCOUNT(buffer)) #endif // else _MSC_VER { // The result did fit into the static buffer + #ifdef va_copy + va_end(argsCopy); + #endif str.append(buffer, len); return str; } + #ifdef va_copy + va_end(argsCopy); + #endif - // The result did not fit into the static buffer + // The result did not fit into the static buffer, use a dynamic buffer: #ifdef _MSC_VER // for MS CRT, we need to calculate the result length + // MS doesn't have va_copy() and does nod need it at all len = _vscprintf(format, args); if (len == -1) { @@ -47,13 +60,19 @@ AString & AppendVPrintf(AString & str, const char *format, va_list args) #endif // _MSC_VER // Allocate a buffer and printf into it: + #ifdef va_copy + va_copy(argsCopy, args); + #endif std::vector<char> Buffer(len + 1); #ifdef _MSC_VER - vsprintf_s((char *)&(Buffer.front()), Buffer.size(), format, args); + vsprintf_s((char *)&(Buffer.front()), Buffer.size(), format, argsCopy); #else // _MSC_VER - vsnprintf((char *)&(Buffer.front()), Buffer.size(), format, args); + vsnprintf((char *)&(Buffer.front()), Buffer.size(), format, argsCopy); #endif // else _MSC_VER str.append(&(Buffer.front()), Buffer.size() - 1); + #ifdef va_copy + va_end(argsCopy); + #endif return str; } @@ -89,7 +108,7 @@ AString Printf(const char * format, ...) -AString & AppendPrintf(AString &str, const char *format, ...) +AString & AppendPrintf(AString &str, const char * format, ...) { va_list args; va_start(args, format); diff --git a/src/StringUtils.h b/src/StringUtils.h index 2373f3843..dfbfc2a75 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -21,7 +21,7 @@ typedef std::list<AString> AStringList; -/// Add the formated string to the existing data in the string +/** Add the formated string to the existing data in the string */ extern AString & AppendVPrintf(AString & str, const char * format, va_list args); /// Output the formatted text into the string diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index a721e6b7e..bfcad3d92 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -85,10 +85,10 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA { if (DraggingItem.m_ItemType <= 0) // Empty-handed? { + DraggingItem = Slot.CopyOne(); // Obtain copy of slot to preserve lore, enchantments, etc. + DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); Slot.m_ItemCount -= DraggingItem.m_ItemCount; - DraggingItem.m_ItemType = Slot.m_ItemType; - DraggingItem.m_ItemDamage = Slot.m_ItemDamage; if (Slot.m_ItemCount <= 0) { @@ -101,9 +101,12 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA cItemHandler * Handler = ItemHandler(Slot.m_ItemType); if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize())) { - Slot.m_ItemType = DraggingItem.m_ItemType; - Slot.m_ItemCount++; - Slot.m_ItemDamage = DraggingItem.m_ItemDamage; + char OldSlotCount = Slot.m_ItemCount; + + Slot = DraggingItem.CopyOne(); // See above + OldSlotCount++; + Slot.m_ItemCount = OldSlotCount; + DraggingItem.m_ItemCount--; } if (DraggingItem.m_ItemCount <= 0) @@ -226,7 +229,7 @@ void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ for (int i = 0; i < m_NumSlots; i++) { const cItem * Slot = GetSlot(i, a_Player); - if (!Slot->IsStackableWith(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) { // Different items continue; @@ -265,7 +268,7 @@ bool cSlotArea::CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool for (int i = 0; i < NumSlots; i++) { const cItem & SlotItem = *GetSlot(i, a_Player); - if (!SlotItem.IsStackableWith(a_Dragging)) + if (!SlotItem.IsEqual(a_Dragging)) { continue; } @@ -908,7 +911,7 @@ void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) } // for i - itr->second[] double vX = 0, vY = 0, vZ = 0; - EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY); + EulerToVector(-a_Player.GetYaw(), a_Player.GetPitch(), vZ, vX, vY); vY = -vY * 2 + 1.f; a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because player created } diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index ee75921d1..3ffeff7a0 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -642,7 +642,7 @@ int cWindow::DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int Area->SetSlot(LocalSlotNum, a_Player, ToStore); NumDistributed += ToStore.m_ItemCount; } - else if (AtSlot.IsStackableWith(a_Item)) + else if (AtSlot.IsEqual(a_Item)) { // Occupied, add and cap at MaxStack: int CanStore = std::min(a_NumToEachSlot, (int)MaxStack - AtSlot.m_ItemCount); diff --git a/src/World.cpp b/src/World.cpp index 39300d419..3e7c0ef74 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -10,11 +10,13 @@ #include "Root.h" #include "inifile/iniFile.h" #include "ChunkMap.h" +#include "Generating/ChunkDesc.h" #include "OSSupport/Timer.h" // Entities (except mobs): #include "Entities/ExpOrb.h" #include "Entities/FallingBlock.h" +#include "Entities/Minecart.h" #include "Entities/Pickup.h" #include "Entities/Player.h" #include "Entities/TNTEntity.h" @@ -229,6 +231,7 @@ cWorld::cWorld(const AString & a_WorldName) : m_WorldName(a_WorldName), m_IniFileName(m_WorldName + "/world.ini"), m_StorageSchema("Default"), + m_StorageCompressionFactor(6), m_IsSpawnExplicitlySet(false), m_WorldAgeSecs(0), m_TimeOfDaySecs(0), @@ -238,6 +241,7 @@ cWorld::cWorld(const AString & a_WorldName) : m_SkyDarkness(0), m_Weather(eWeather_Sunny), m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) + m_GeneratorCallbacks(*this), m_TickThread(*this) { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); @@ -509,6 +513,7 @@ void cWorld::Start(void) } m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema); + m_StorageCompressionFactor = IniFile.GetValueSetI ("Storage", "CompressionFactor", m_StorageCompressionFactor); m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3); m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3); m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false); @@ -582,8 +587,8 @@ void cWorld::Start(void) m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); m_Lighting.Start(this); - m_Storage.Start(this, m_StorageSchema); - m_Generator.Start(this, IniFile); + m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor ); + m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile); m_ChunkSender.Start(this); m_TickThread.Start(); @@ -688,6 +693,7 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) TickClients(a_Dt); TickQueuedBlocks(); TickQueuedTasks(); + TickScheduledTasks(); GetSimulatorManager()->Simulate(a_Dt); @@ -859,6 +865,31 @@ void cWorld::TickQueuedTasks(void) } // for itr - m_Tasks[] } +void cWorld::TickScheduledTasks() +{ + ScheduledTaskList Tasks; + // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks + { + cCSLock Lock(m_CSScheduledTasks); + ScheduledTaskList::iterator itr = m_ScheduledTasks.begin(); + while (itr != m_ScheduledTasks.end() && (*itr)->Ticks > 0) + { + Tasks.push_back(m_ScheduledTasks.front()); + m_ScheduledTasks.pop_front(); + } + for(;itr != m_ScheduledTasks.end(); itr++) + { + (*itr)->Ticks--; + } + } + + // Execute and delete each task: + for (ScheduledTaskList::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) + { + (*itr)->Run(*this); + delete *itr; + } // for itr - m_Tasks[] +} @@ -1645,6 +1676,29 @@ int cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) +int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content, int a_BlockHeight) +{ + cMinecart * Minecart; + switch (a_MinecartType) + { + case E_ITEM_MINECART: Minecart = new cRideableMinecart (a_X, a_Y, a_Z, a_Content, a_BlockHeight); break; + case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (a_X, a_Y, a_Z); break; + case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (a_X, a_Y, a_Z); break; + case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (a_X, a_Y, a_Z); break; + case E_ITEM_MINECART_WITH_HOPPER: Minecart = new cMinecartWithHopper (a_X, a_Y, a_Z); break; + default: + { + return -1; + } + } // switch (a_MinecartType) + Minecart->Initialize(this); + return Minecart->GetUniqueID(); +} + + + + + void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff) { UNUSED(a_InitialVelocityCoeff); @@ -2569,6 +2623,19 @@ void cWorld::QueueTask(cTask * a_Task) m_Tasks.push_back(a_Task); } +void cWorld::ScheduleTask(cScheduledTask * a_Task) +{ + cCSLock Lock(m_CSScheduledTasks); + for(ScheduledTaskList::iterator itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end(); itr++) + { + if((*itr)->Ticks >= a_Task->Ticks) + { + m_ScheduledTasks.insert(itr, a_Task); + return; + } + } + m_ScheduledTasks.push_back(a_Task); +} @@ -2843,3 +2910,77 @@ void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cChunkGeneratorCallbacks: + +cWorld::cChunkGeneratorCallbacks::cChunkGeneratorCallbacks(cWorld & a_World) : + m_World(&a_World) +{ +} + + + + + +void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc) +{ + cChunkDef::BlockNibbles BlockMetas; + a_ChunkDesc.CompressBlockMetas(BlockMetas); + + m_World->SetChunkData( + a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), + a_ChunkDesc.GetBlockTypes(), BlockMetas, + NULL, NULL, // We don't have lighting, chunk will be lighted when needed + &a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(), + a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(), + true + ); + + // Save the chunk right after generating, so that we don't have to generate it again on next run + m_World->GetStorage().QueueSaveChunk(a_ChunkDesc.GetChunkX(), 0, a_ChunkDesc.GetChunkZ()); +} + + + + + +bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(int a_ChunkX, int a_ChunkZ) +{ + return m_World->IsChunkValid(a_ChunkX, a_ChunkZ); +} + + + + + +bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) +{ + return m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) +{ + cPluginManager::Get()->CallHookChunkGenerating( + m_World, a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), &a_ChunkDesc + ); +} + + + + + +void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_ChunkDesc) +{ + cPluginManager::Get()->CallHookChunkGenerated( + m_World, a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), &a_ChunkDesc + ); +} + + + + + diff --git a/src/World.h b/src/World.h index f90ddd90f..ef74a65c7 100644 --- a/src/World.h +++ b/src/World.h @@ -82,7 +82,18 @@ public: virtual void Run(cWorld & a_World) = 0; } ; + /// A common ancestor for all scheduled tasks queued onto the tick thread + class cScheduledTask + { + public: + cScheduledTask(const int a_Ticks) : Ticks(a_Ticks) {}; + virtual ~cScheduledTask() {}; + virtual void Run(cWorld & a_World) = 0; + int Ticks; + }; + typedef std::vector<cTask *> cTasks; + typedef std::list<cScheduledTask *> ScheduledTaskList; class cTaskSaveAllChunks : public cTask @@ -363,6 +374,9 @@ public: /// Spawns an falling block entity at the given position. It returns the UniqueID of the spawned falling block. int SpawnFallingBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE BlockType, NIBBLETYPE BlockMeta); + /// Spawns an minecart at the given coordinates. + int SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content = cItem(), int a_BlockHeight = 1); + /// Spawns an experience orb at the given location with the given reward. It returns the UniqueID of the spawned experience orb. int SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward); @@ -533,6 +547,9 @@ public: /// Queues a task onto the tick thread. The task object will be deleted once the task is finished void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp + + // Queues a task onto the tick thread. The task object will be deleted once the task is finished + void ScheduleTask(cScheduledTask * a_Task); /// Returns the number of chunks loaded int GetNumChunks() const; // tolua_export @@ -636,6 +653,27 @@ private: virtual void Execute(void) override; } ; + + /** Implementation of the callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ + class cChunkGeneratorCallbacks : + public cChunkGenerator::cChunkSink, + public cChunkGenerator::cPluginInterface + { + cWorld * m_World; + + // cChunkSink overrides: + virtual void OnChunkGenerated (cChunkDesc & a_ChunkDesc) override; + virtual bool IsChunkValid (int a_ChunkX, int a_ChunkZ) override; + virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) override; + + // cPluginInterface overrides: + virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) override; + virtual void CallHookChunkGenerated (cChunkDesc & a_ChunkDesc) override; + + public: + cChunkGeneratorCallbacks(cWorld & a_World); + } ; + AString m_WorldName; AString m_IniFileName; @@ -643,6 +681,8 @@ private: /// Name of the storage schema used to load and save chunks AString m_StorageSchema; + int m_StorageCompressionFactor; + /// The dimension of the world, used by the client to provide correct lighting scheme eDimension m_Dimension; @@ -714,6 +754,9 @@ private: cChunkGenerator m_Generator; + /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ + cChunkGeneratorCallbacks m_GeneratorCallbacks; + cChunkSender m_ChunkSender; cLightingThread m_Lighting; cTickThread m_TickThread; @@ -721,9 +764,16 @@ private: /// Guards the m_Tasks cCriticalSection m_CSTasks; + /// Guards the m_ScheduledTasks + cCriticalSection m_CSScheduledTasks; + /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks cTasks m_Tasks; + /// Tasks that have been queued to be executed on the tick thread at some number of ticks in + /// the future; guarded by m_CSScheduledTasks + ScheduledTaskList m_ScheduledTasks; + /// Guards m_Clients cCriticalSection m_CSClients; @@ -751,6 +801,9 @@ private: /// Executes all tasks queued onto the tick thread void TickQueuedTasks(void); + /// Executes all tasks queued onto the tick thread + void TickScheduledTasks(void); + /// Ticks all clients that are in this world void TickClients(float a_Dt); diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index e5043de1f..e1205f2be 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -257,7 +257,7 @@ void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_C m_Writer.AddDouble("", a_Entity->GetSpeedZ()); m_Writer.EndList(); m_Writer.BeginList("Rotation", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetRotation()); + m_Writer.AddDouble("", a_Entity->GetYaw()); m_Writer.AddDouble("", a_Entity->GetPitch()); m_Writer.EndList(); } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 8605930b6..16513cd1b 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -58,8 +58,9 @@ Since only the header is actually in the memory, this number can be high, but st /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWSSAnvil: -cWSSAnvil::cWSSAnvil(cWorld * a_World) : - super(a_World) +cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) : + super(a_World), + m_CompressionFactor(a_CompressionFactor) { // Create a level.dat file for mapping tools, if it doesn't already exist: AString fnam; @@ -272,7 +273,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) } Writer.Finish(); - CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data); + CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data, m_CompressionFactor); return true; } @@ -1150,7 +1151,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<cEmptyMinecart> Minecart(new cEmptyMinecart(0, 0, 0)); + std::auto_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; @@ -1891,7 +1892,7 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N { return false; } - a_Entity.SetRotation(Rotation[0]); + a_Entity.SetYaw(Rotation[0]); a_Entity.SetRoll (Rotation[1]); return true; diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 0a7406267..e66e63b08 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -47,7 +47,7 @@ class cWSSAnvil : public: - cWSSAnvil(cWorld * a_World); + cWSSAnvil(cWorld * a_World, int a_CompressionFactor); virtual ~cWSSAnvil(); protected: @@ -89,6 +89,8 @@ protected: cCriticalSection m_CS; cMCAFiles m_Files; // a MRU cache of MCA files + + int m_CompressionFactor; /// Gets chunk data from the correct file; locks file CS as needed bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index e2556b96e..ea17a8ec1 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -193,7 +193,7 @@ cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk) // Load it anew: AString FileName; Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ ); - cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ); + cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ, m_CompressionFactor); if (f == NULL) { return NULL; @@ -399,8 +399,9 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En return; \ } -cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) : +cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ, int a_CompressionFactor) : m_FileName(a_FileName), + m_CompressionFactor(a_CompressionFactor), m_LayerX(a_LayerX), m_LayerZ(a_LayerZ), m_NumDirty(0), @@ -648,7 +649,7 @@ void cWSSCompact::cPAKFile::UpdateChunk1To2() // Re-compress data AString CompressedData; { - int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); + int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData,m_CompressionFactor); if (errorcode != Z_OK) { LOGERROR("Error %d compressing data for chunk [%d, %d]", @@ -786,7 +787,7 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3() // Re-compress data AString CompressedData; { - int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); + int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData, m_CompressionFactor); if (errorcode != Z_OK) { LOGERROR("Error %d compressing data for chunk [%d, %d]", @@ -939,7 +940,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld // Compress the data: AString CompressedData; - int errorcode = CompressString(Data.data(), Data.size(), CompressedData); + int errorcode = CompressString(Data.data(), Data.size(), CompressedData, m_CompressionFactor); if ( errorcode != Z_OK ) { LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h index 3223a986e..64b8d7f31 100644 --- a/src/WorldStorage/WSSCompact.h +++ b/src/WorldStorage/WSSCompact.h @@ -53,7 +53,7 @@ class cWSSCompact : public cWSSchema { public: - cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {} + cWSSCompact(cWorld * a_World, int a_CompressionFactor) : cWSSchema(a_World), m_CompressionFactor(a_CompressionFactor) {} virtual ~cWSSCompact(); protected: @@ -74,7 +74,7 @@ protected: { public: - cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ); + cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ, int a_CompressionFactor); ~cPAKFile(); bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); @@ -95,6 +95,7 @@ protected: protected: AString m_FileName; + int m_CompressionFactor; int m_LayerX; int m_LayerZ; @@ -119,6 +120,8 @@ protected: cCriticalSection m_CS; cPAKFiles m_PAKFiles; // A MRU cache of PAK files + int m_CompressionFactor; + /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk); diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index 6aec525a8..711c8612f 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -68,11 +68,11 @@ cWorldStorage::~cWorldStorage() -bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName) +bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor ) { m_World = a_World; m_StorageSchemaName = a_StorageSchemaName; - InitSchemas(); + InitSchemas(a_StorageCompressionFactor); return super::Start(); } @@ -197,11 +197,11 @@ void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) -void cWorldStorage::InitSchemas(void) +void cWorldStorage::InitSchemas(int a_StorageCompressionFactor) { // The first schema added is considered the default - m_Schemas.push_back(new cWSSAnvil (m_World)); - m_Schemas.push_back(new cWSSCompact (m_World)); + m_Schemas.push_back(new cWSSAnvil (m_World,a_StorageCompressionFactor)); + m_Schemas.push_back(new cWSSCompact (m_World,a_StorageCompressionFactor)); m_Schemas.push_back(new cWSSForgetful(m_World)); // Add new schemas here diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index 06cae1717..bb189b6c9 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -76,7 +76,7 @@ public: void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); void UnqueueSave(const cChunkCoords & a_Chunk); - bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args + bool Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor); // Hide the cIsThread's Start() method, we need to provide args void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event void WaitForFinish(void); void WaitForLoadQueueEmpty(void); @@ -126,7 +126,7 @@ protected: /// The one storage schema used for saving cWSSchema * m_SaveSchema; - void InitSchemas(void); + void InitSchemas(int a_StorageCompressionFactor); virtual void Execute(void) override; diff --git a/src/main.cpp b/src/main.cpp index 0620e0f0e..340149e0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,9 +47,20 @@ void NonCtrlHandler(int a_Signal) case SIGSEGV: { std::signal(SIGSEGV, SIG_DFL); - LOGWARN("Segmentation fault; MCServer has crashed :("); + LOGERROR(" D: | MCServer has encountered an error and needs to close"); + LOGERROR("Details | SIGSEGV: Segmentation fault"); exit(EXIT_FAILURE); } + case SIGABRT: + #ifdef SIGABRT_COMPAT + case SIGABRT_COMPAT: + #endif + { + std::signal(a_Signal, SIG_DFL); + LOGERROR(" D: | MCServer has encountered an error and needs to close"); + LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault"); + break; + } case SIGTERM: { std::signal(SIGTERM, SIG_IGN); // Server is shutting down, wait for it... |