diff options
Diffstat (limited to 'source')
86 files changed, 4909 insertions, 808 deletions
diff --git a/source/AllToLua.pkg b/source/AllToLua.pkg index b423c43a5..00257e460 100644 --- a/source/AllToLua.pkg +++ b/source/AllToLua.pkg @@ -17,6 +17,8 @@ $cfile "ChunkDef.h" $cfile "../iniFile/iniFile.h" +$cfile "OSSupport/File.h" + $cfile "BlockID.h" $cfile "StringUtils.h" $cfile "Defines.h" diff --git a/source/Authenticator.cpp b/source/Authenticator.cpp index dcc63299e..a45617f93 100644 --- a/source/Authenticator.cpp +++ b/source/Authenticator.cpp @@ -100,6 +100,16 @@ void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, co +void cAuthenticator::Start(void) +{ + m_ShouldTerminate = false; + super::Start(); +} + + + + + void cAuthenticator::Stop(void) { m_ShouldTerminate = true; diff --git a/source/Authenticator.h b/source/Authenticator.h index c9e647329..868476d80 100644 --- a/source/Authenticator.h +++ b/source/Authenticator.h @@ -42,7 +42,10 @@ public: /// Queues a request for authenticating a user. If the auth fails, the user is kicked void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); - // Stops the authenticator thread + /// Starts the authenticator thread. The thread may be started and stopped repeatedly + void Start(void); + + /// Stops the authenticator thread. The thread may be started and stopped repeatedly void Stop(void); private: diff --git a/source/Bindings.cpp b/source/Bindings.cpp index ae2082511..c241bad75 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 09/16/13 11:41:45. +** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:21. */ #ifndef __cplusplus @@ -17,6 +17,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "tolua_base.h" #include "ChunkDef.h" #include "../iniFile/iniFile.h" +#include "OSSupport/File.h" #include "BlockID.h" #include "StringUtils.h" #include "Defines.h" @@ -240,31 +241,33 @@ static void tolua_reg_types (lua_State* tolua_S) tolua_usertype(tolua_S,"cCraftingRecipe"); tolua_usertype(tolua_S,"cPlugin"); tolua_usertype(tolua_S,"cItemGrid"); - tolua_usertype(tolua_S,"cBlockArea"); + tolua_usertype(tolua_S,"cHTTPServer::cCallbacks"); tolua_usertype(tolua_S,"cLuaWindow"); tolua_usertype(tolua_S,"cInventory"); tolua_usertype(tolua_S,"cBoundingBox"); tolua_usertype(tolua_S,"cBlockEntityWithItems"); + tolua_usertype(tolua_S,"cWindow"); + tolua_usertype(tolua_S,"cGroup"); tolua_usertype(tolua_S,"HTTPFormData"); - tolua_usertype(tolua_S,"cTracer"); + tolua_usertype(tolua_S,"cCraftingGrid"); tolua_usertype(tolua_S,"cArrowEntity"); tolua_usertype(tolua_S,"cDropSpenserEntity"); - tolua_usertype(tolua_S,"cWindow"); - tolua_usertype(tolua_S,"Vector3i"); - tolua_usertype(tolua_S,"cCraftingGrid"); - tolua_usertype(tolua_S,"cGroup"); + tolua_usertype(tolua_S,"cBlockArea"); + tolua_usertype(tolua_S,"cTracer"); tolua_usertype(tolua_S,"cStringMap"); + tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"Vector3i"); tolua_usertype(tolua_S,"cBlockEntity"); tolua_usertype(tolua_S,"cCriticalSection"); tolua_usertype(tolua_S,"HTTPTemplateRequest"); - tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"cFile"); tolua_usertype(tolua_S,"std::vector<std::string>"); tolua_usertype(tolua_S,"cClientHandle"); tolua_usertype(tolua_S,"cChatColor"); - tolua_usertype(tolua_S,"sWebAdminPage"); tolua_usertype(tolua_S,"cWebPlugin"); - tolua_usertype(tolua_S,"cIniFile"); tolua_usertype(tolua_S,"cWebAdmin"); + tolua_usertype(tolua_S,"cIniFile"); + tolua_usertype(tolua_S,"sWebAdminPage"); tolua_usertype(tolua_S,"cItem"); tolua_usertype(tolua_S,"cPawn"); tolua_usertype(tolua_S,"cPlayer"); @@ -2460,6 +2463,260 @@ tolua_lerror: } #endif //#ifndef TOLUA_DISABLE +/* method: Exists of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Exists00 +static int tolua_AllToLua_cFile_Exists00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Exists(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Exists'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Delete of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Delete00 +static int tolua_AllToLua_cFile_Delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Delete(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Rename of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Rename00 +static int tolua_AllToLua_cFile_Rename00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_OrigPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_NewPath = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Rename(a_OrigPath,a_NewPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_OrigPath); + tolua_pushcppstring(tolua_S,(const char*)a_NewPath); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Rename'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Copy of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Copy00 +static int tolua_AllToLua_cFile_Copy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_SrcFileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_DstFileName = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Copy(a_SrcFileName,a_DstFileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_SrcFileName); + tolua_pushcppstring(tolua_S,(const char*)a_DstFileName); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Copy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFolder00 +static int tolua_AllToLua_cFile_IsFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFolder(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFile of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFile00 +static int tolua_AllToLua_cFile_IsFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFile(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSize of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_GetSize00 +static int tolua_AllToLua_cFile_GetSize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) cFile::GetSize(a_FileName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CreateFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_CreateFolder00 +static int tolua_AllToLua_cFile_CreateFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FolderPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::CreateFolder(a_FolderPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FolderPath); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CreateFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* function: BlockStringToType */ #ifndef TOLUA_DISABLE_tolua_AllToLua_BlockStringToType00 static int tolua_AllToLua_BlockStringToType00(lua_State* tolua_S) @@ -2758,6 +3015,51 @@ static int tolua_AllToLua_StringToDamageType00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* function: GetIniItemSet */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetIniItemSet00 +static int tolua_AllToLua_GetIniItemSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err)) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isstring(tolua_S,3,0,&tolua_err) || + !tolua_isstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* a_IniFile = ((cIniFile*) tolua_tousertype(tolua_S,1,0)); + const char* a_Section = ((const char*) tolua_tostring(tolua_S,2,0)); + const char* a_Key = ((const char*) tolua_tostring(tolua_S,3,0)); + const char* a_Default = ((const char*) tolua_tostring(tolua_S,4,0)); + { + cItem tolua_ret = (cItem) GetIniItemSet(*a_IniFile,a_Section,a_Key,a_Default); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIniItemSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* function: TrimString */ #ifndef TOLUA_DISABLE_tolua_AllToLua_TrimString00 static int tolua_AllToLua_TrimString00(lua_State* tolua_S) @@ -4696,6 +4998,38 @@ static int tolua_AllToLua_cEntity_IsMob00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsFallingBlock of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsFallingBlock00 +static int tolua_AllToLua_cEntity_IsFallingBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFallingBlock'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsFallingBlock(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFallingBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: IsMinecart of class cEntity */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMinecart00 static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S) @@ -4792,6 +5126,38 @@ static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsProjectile of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsProjectile00 +static int tolua_AllToLua_cEntity_IsProjectile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsProjectile'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsProjectile(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsProjectile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: IsA of class cEntity */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsA00 static int tolua_AllToLua_cEntity_IsA00(lua_State* tolua_S) @@ -7656,6 +8022,38 @@ static int tolua_AllToLua_cEntity_IsRclking00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsInvisible of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsInvisible00 +static int tolua_AllToLua_cEntity_IsInvisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInvisible'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInvisible(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInvisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: GetEyeHeight of class cPlayer */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyeHeight00 static int tolua_AllToLua_cPlayer_GetEyeHeight00(lua_State* tolua_S) @@ -9676,15 +10074,15 @@ static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S) else #endif { - int a_MicroPosX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MicroPosY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MicroPosZ = ((int) tolua_tonumber(tolua_S,4,0)); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f)); float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f)); float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f)); { - cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_MicroPosX,a_MicroPosY,a_MicroPosZ,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); } } @@ -9718,15 +10116,15 @@ static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S) else #endif { - int a_MicroPosX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_MicroPosY = ((int) tolua_tonumber(tolua_S,3,0)); - int a_MicroPosZ = ((int) tolua_tonumber(tolua_S,4,0)); + double a_X = ((double) tolua_tonumber(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,4,0)); const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); float a_SpeedX = ((float) tolua_tonumber(tolua_S,6,0.f)); float a_SpeedY = ((float) tolua_tonumber(tolua_S,7,0.f)); float a_SpeedZ = ((float) tolua_tonumber(tolua_S,8,0.f)); { - cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_MicroPosX,a_MicroPosY,a_MicroPosZ,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_X,a_Y,a_Z,*a_Item,a_SpeedX,a_SpeedY,a_SpeedZ)); tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); tolua_register_gc(tolua_S,lua_gettop(tolua_S)); } @@ -10828,6 +11226,38 @@ static int tolua_AllToLua_cPlugin_GetLocalDirectory00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: GetLocalFolder of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalFolder00 +static int tolua_AllToLua_cPlugin_GetLocalFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlugin* self = (const cPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLocalFolder'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLocalFolder(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLocalFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* get function: __cWebPlugin__ of class cPluginLua */ #ifndef TOLUA_DISABLE_tolua_get_cPluginLua___cWebPlugin__ static int tolua_get_cPluginLua___cWebPlugin__(lua_State* tolua_S) @@ -13282,6 +13712,134 @@ static int tolua_AllToLua_cWorld_GetWeather00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: IsWeatherSunny of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherSunny00 +static int tolua_AllToLua_cWorld_IsWeatherSunny00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherSunny'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherSunny(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherSunny'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherRain of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherRain00 +static int tolua_AllToLua_cWorld_IsWeatherRain00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherRain'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherRain(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherRain'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherStorm of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherStorm00 +static int tolua_AllToLua_cWorld_IsWeatherStorm00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherStorm'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherStorm(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherStorm'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsWeatherWet of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsWeatherWet00 +static int tolua_AllToLua_cWorld_IsWeatherWet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsWeatherWet'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsWeatherWet(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsWeatherWet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: SetNextBlockTick of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetNextBlockTick00 static int tolua_AllToLua_cWorld_SetNextBlockTick00(lua_State* tolua_S) @@ -18621,8 +19179,8 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S) #endif { { - AString tolua_ret = (AString) cWebAdmin::GetMemoryUsage(); - tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + int tolua_ret = (int) cWebAdmin::GetMemoryUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); } } return 1; @@ -18634,77 +19192,77 @@ static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: GetPort of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPort00 -static int tolua_AllToLua_cWebAdmin_GetPort00(lua_State* tolua_S) +/* method: GetPage of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 +static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) ) goto tolua_lerror; else #endif { cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); + const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPort'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL); #endif { - int tolua_ret = (int) self->GetPort(); - tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage)); + tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPort'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); return 0; #endif } #endif //#ifndef TOLUA_DISABLE -/* method: GetPage of class cWebAdmin */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 -static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) +/* method: GetDefaultPage of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetDefaultPage00 +static int tolua_AllToLua_cWebAdmin_GetDefaultPage00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) + !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); - const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL); #endif { - sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request); - { -#ifdef __cplusplus - void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret)); - tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#else - void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage)); - tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); -#endif - } + AString tolua_ret = (AString) self->GetDefaultPage(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'GetDefaultPage'.",&tolua_err); return 0; #endif } @@ -19385,6 +19943,62 @@ static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua } #endif //#ifndef TOLUA_DISABLE +/* method: GetVirtualRAMUsage of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetVirtualRAMUsage00 +static int tolua_AllToLua_cRoot_GetVirtualRAMUsage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + int tolua_ret = (int) cRoot::GetVirtualRAMUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetVirtualRAMUsage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPhysicalRAMUsage of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPhysicalRAMUsage00 +static int tolua_AllToLua_cRoot_GetPhysicalRAMUsage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + int tolua_ret = (int) cRoot::GetPhysicalRAMUsage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPhysicalRAMUsage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: new of class Vector3f */ #ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00 static int tolua_AllToLua_Vector3f_new00(lua_State* tolua_S) @@ -26131,41 +26745,6 @@ static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: SetChunkCoords of class cChunkDesc */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetChunkCoords00 -static int tolua_AllToLua_cChunkDesc_SetChunkCoords00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || - !tolua_isnumber(tolua_S,2,0,&tolua_err) || - !tolua_isnumber(tolua_S,3,0,&tolua_err) || - !tolua_isnoobj(tolua_S,4,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); - int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0)); - int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetChunkCoords'", NULL); -#endif - { - self->SetChunkCoords(a_ChunkX,a_ChunkZ); - } - } - return 0; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'SetChunkCoords'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: FillBlocks of class cChunkDesc */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillBlocks00 static int tolua_AllToLua_cChunkDesc_FillBlocks00(lua_State* tolua_S) @@ -28648,8 +29227,47 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"biExtremeHillsEdge",biExtremeHillsEdge); tolua_constant(tolua_S,"biJungle",biJungle); tolua_constant(tolua_S,"biJungleHills",biJungleHills); + tolua_constant(tolua_S,"biJungleEdge",biJungleEdge); + tolua_constant(tolua_S,"biDeepOcean",biDeepOcean); + tolua_constant(tolua_S,"biStoneBeach",biStoneBeach); + tolua_constant(tolua_S,"biColdBeach",biColdBeach); + tolua_constant(tolua_S,"biBirchForest",biBirchForest); + tolua_constant(tolua_S,"biBirchForestHills",biBirchForestHills); + tolua_constant(tolua_S,"biRoofedForest",biRoofedForest); + tolua_constant(tolua_S,"biColdTaiga",biColdTaiga); + tolua_constant(tolua_S,"biColdTaigaHills",biColdTaigaHills); + tolua_constant(tolua_S,"biMegaTaiga",biMegaTaiga); + tolua_constant(tolua_S,"biMegaTaigaHills",biMegaTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlus",biExtremeHillsPlus); + tolua_constant(tolua_S,"biSavanna",biSavanna); + tolua_constant(tolua_S,"biSavannaPlateau",biSavannaPlateau); + tolua_constant(tolua_S,"biMesa",biMesa); + tolua_constant(tolua_S,"biMesaPlateauF",biMesaPlateauF); + tolua_constant(tolua_S,"biMesaPlateau",biMesaPlateau); tolua_constant(tolua_S,"biNumBiomes",biNumBiomes); tolua_constant(tolua_S,"biMaxBiome",biMaxBiome); + tolua_constant(tolua_S,"biVariant",biVariant); + tolua_constant(tolua_S,"biSunflowerPlains",biSunflowerPlains); + tolua_constant(tolua_S,"biDesertM",biDesertM); + tolua_constant(tolua_S,"biExtremeHillsM",biExtremeHillsM); + tolua_constant(tolua_S,"biFlowerForest",biFlowerForest); + tolua_constant(tolua_S,"biTaigaM",biTaigaM); + tolua_constant(tolua_S,"biSwamplandM",biSwamplandM); + tolua_constant(tolua_S,"biIcePlainsSpikes",biIcePlainsSpikes); + tolua_constant(tolua_S,"biJungleM",biJungleM); + tolua_constant(tolua_S,"biJungleEdgeM",biJungleEdgeM); + tolua_constant(tolua_S,"biBirchForestM",biBirchForestM); + tolua_constant(tolua_S,"biBirchForestHillsM",biBirchForestHillsM); + tolua_constant(tolua_S,"biRoofedForestM",biRoofedForestM); + tolua_constant(tolua_S,"biColdTaigaM",biColdTaigaM); + tolua_constant(tolua_S,"biMegaSpruceTaiga",biMegaSpruceTaiga); + tolua_constant(tolua_S,"biMegaSpruceTaigaHills",biMegaSpruceTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlusM",biExtremeHillsPlusM); + tolua_constant(tolua_S,"biSavannaM",biSavannaM); + tolua_constant(tolua_S,"biSavannaPlateauM",biSavannaPlateauM); + tolua_constant(tolua_S,"biMesaBryce",biMesaBryce); + tolua_constant(tolua_S,"biMesaPlateauFM",biMesaPlateauFM); + tolua_constant(tolua_S,"biMesaPlateauM",biMesaPlateauM); #ifdef __cplusplus tolua_cclass(tolua_S,"cIniFile","cIniFile","",tolua_collect_cIniFile); #else @@ -28724,6 +29342,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00); tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments01); tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFile","cFile","",NULL); + tolua_beginmodule(tolua_S,"cFile"); + tolua_function(tolua_S,"Exists",tolua_AllToLua_cFile_Exists00); + tolua_function(tolua_S,"Delete",tolua_AllToLua_cFile_Delete00); + tolua_function(tolua_S,"Rename",tolua_AllToLua_cFile_Rename00); + tolua_function(tolua_S,"Copy",tolua_AllToLua_cFile_Copy00); + tolua_function(tolua_S,"IsFolder",tolua_AllToLua_cFile_IsFolder00); + tolua_function(tolua_S,"IsFile",tolua_AllToLua_cFile_IsFile00); + tolua_function(tolua_S,"GetSize",tolua_AllToLua_cFile_GetSize00); + tolua_function(tolua_S,"CreateFolder",tolua_AllToLua_cFile_CreateFolder00); + tolua_endmodule(tolua_S); tolua_constant(tolua_S,"E_BLOCK_AIR",E_BLOCK_AIR); tolua_constant(tolua_S,"E_BLOCK_STONE",E_BLOCK_STONE); tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS); @@ -29030,14 +29659,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_ITEM_BOOK_AND_QUILL",E_ITEM_BOOK_AND_QUILL); tolua_constant(tolua_S,"E_ITEM_WRITTEN_BOOK",E_ITEM_WRITTEN_BOOK); tolua_constant(tolua_S,"E_ITEM_EMERALD",E_ITEM_EMERALD); + tolua_constant(tolua_S,"E_ITEM_ITEM_FRAME",E_ITEM_ITEM_FRAME); tolua_constant(tolua_S,"E_ITEM_FLOWER_POT",E_ITEM_FLOWER_POT); tolua_constant(tolua_S,"E_ITEM_CARROT",E_ITEM_CARROT); tolua_constant(tolua_S,"E_ITEM_POTATO",E_ITEM_POTATO); tolua_constant(tolua_S,"E_ITEM_BAKED_POTATO",E_ITEM_BAKED_POTATO); tolua_constant(tolua_S,"E_ITEM_POISONOUS_POTATO",E_ITEM_POISONOUS_POTATO); + tolua_constant(tolua_S,"E_ITEM_EMPTY_MAP",E_ITEM_EMPTY_MAP); tolua_constant(tolua_S,"E_ITEM_GOLDEN_CARROT",E_ITEM_GOLDEN_CARROT); tolua_constant(tolua_S,"E_ITEM_HEAD",E_ITEM_HEAD); tolua_constant(tolua_S,"E_ITEM_CARROT_ON_STICK",E_ITEM_CARROT_ON_STICK); + tolua_constant(tolua_S,"E_ITEM_NETHER_STAR",E_ITEM_NETHER_STAR); tolua_constant(tolua_S,"E_ITEM_PUMPKIN_PIE",E_ITEM_PUMPKIN_PIE); tolua_constant(tolua_S,"E_ITEM_FIREWORK_ROCKET",E_ITEM_FIREWORK_ROCKET); tolua_constant(tolua_S,"E_ITEM_FIREWORK_STAR",E_ITEM_FIREWORK_STAR); @@ -29231,11 +29863,35 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED); tolua_constant(tolua_S,"E_META_TRACKS_X",E_META_TRACKS_X); tolua_constant(tolua_S,"E_META_TRACKS_Z",E_META_TRACKS_Z); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PICKUP",E_META_SPAWN_EGG_PICKUP); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXPERIENCE_ORB",E_META_SPAWN_EGG_EXPERIENCE_ORB); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_LEASH_KNOT",E_META_SPAWN_EGG_LEASH_KNOT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PAINTING",E_META_SPAWN_EGG_PAINTING); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ARROW",E_META_SPAWN_EGG_ARROW); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOWBALL",E_META_SPAWN_EGG_SNOWBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREBALL",E_META_SPAWN_EGG_FIREBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SMALL_FIREBALL",E_META_SPAWN_EGG_SMALL_FIREBALL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_PEARL",E_META_SPAWN_EGG_ENDER_PEARL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EYE_OF_ENDER",E_META_SPAWN_EGG_EYE_OF_ENDER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPLASH_POTION",E_META_SPAWN_EGG_SPLASH_POTION); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_EXP_BOTTLE",E_META_SPAWN_EGG_EXP_BOTTLE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ITEM_FRAME",E_META_SPAWN_EGG_ITEM_FRAME); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER_SKULL",E_META_SPAWN_EGG_WITHER_SKULL); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PRIMED_TNT",E_META_SPAWN_EGG_PRIMED_TNT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FALLING_BLOCK",E_META_SPAWN_EGG_FALLING_BLOCK); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_FIREWORK",E_META_SPAWN_EGG_FIREWORK); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BOAT",E_META_SPAWN_EGG_BOAT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART",E_META_SPAWN_EGG_MINECART); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_CHEST",E_META_SPAWN_EGG_MINECART_CHEST); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_FURNACE",E_META_SPAWN_EGG_MINECART_FURNACE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_TNT",E_META_SPAWN_EGG_MINECART_TNT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_HOPPER",E_META_SPAWN_EGG_MINECART_HOPPER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MINECART_SPAWNER",E_META_SPAWN_EGG_MINECART_SPAWNER); tolua_constant(tolua_S,"E_META_SPAWN_EGG_CREEPER",E_META_SPAWN_EGG_CREEPER); tolua_constant(tolua_S,"E_META_SPAWN_EGG_SKELETON",E_META_SPAWN_EGG_SKELETON); tolua_constant(tolua_S,"E_META_SPAWN_EGG_SPIDER",E_META_SPAWN_EGG_SPIDER); - tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE); tolua_constant(tolua_S,"E_META_SPAWN_EGG_GIANT",E_META_SPAWN_EGG_GIANT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE",E_META_SPAWN_EGG_ZOMBIE); tolua_constant(tolua_S,"E_META_SPAWN_EGG_SLIME",E_META_SPAWN_EGG_SLIME); tolua_constant(tolua_S,"E_META_SPAWN_EGG_GHAST",E_META_SPAWN_EGG_GHAST); tolua_constant(tolua_S,"E_META_SPAWN_EGG_ZOMBIE_PIGMAN",E_META_SPAWN_EGG_ZOMBIE_PIGMAN); @@ -29260,6 +29916,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"E_META_SPAWN_EGG_IRON_GOLEM",E_META_SPAWN_EGG_IRON_GOLEM); tolua_constant(tolua_S,"E_META_SPAWN_EGG_HORSE",E_META_SPAWN_EGG_HORSE); tolua_constant(tolua_S,"E_META_SPAWN_EGG_VILLAGER",E_META_SPAWN_EGG_VILLAGER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_CRYSTAL",E_META_SPAWN_EGG_ENDER_CRYSTAL); tolua_constant(tolua_S,"dimNether",dimNether); tolua_constant(tolua_S,"dimOverworld",dimOverworld); tolua_constant(tolua_S,"dimEnd",dimEnd); @@ -29319,6 +29976,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"StringToDimension",tolua_AllToLua_StringToDimension00); tolua_function(tolua_S,"DamageTypeToString",tolua_AllToLua_DamageTypeToString00); tolua_function(tolua_S,"StringToDamageType",tolua_AllToLua_StringToDamageType00); + tolua_function(tolua_S,"GetIniItemSet",tolua_AllToLua_GetIniItemSet00); tolua_function(tolua_S,"TrimString",tolua_AllToLua_TrimString00); tolua_function(tolua_S,"NoCaseCompare",tolua_AllToLua_NoCaseCompare00); tolua_function(tolua_S,"ReplaceString",tolua_AllToLua_ReplaceString00); @@ -29471,20 +30129,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cEntity","cEntity","",NULL); tolua_beginmodule(tolua_S,"cEntity"); - tolua_constant(tolua_S,"ENTITY_STATUS_HURT",cEntity::ENTITY_STATUS_HURT); - tolua_constant(tolua_S,"ENTITY_STATUS_DEAD",cEntity::ENTITY_STATUS_DEAD); - tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_TAMING",cEntity::ENTITY_STATUS_WOLF_TAMING); - tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_TAMED",cEntity::ENTITY_STATUS_WOLF_TAMED); - tolua_constant(tolua_S,"ENTITY_STATUS_WOLF_SHAKING",cEntity::ENTITY_STATUS_WOLF_SHAKING); - tolua_constant(tolua_S,"ENTITY_STATUS_EATING_ACCEPTED",cEntity::ENTITY_STATUS_EATING_ACCEPTED); - tolua_constant(tolua_S,"ENTITY_STATUS_SHEEP_EATING",cEntity::ENTITY_STATUS_SHEEP_EATING); - tolua_constant(tolua_S,"FIRE_TICKS_PER_DAMAGE",cEntity::FIRE_TICKS_PER_DAMAGE); - tolua_constant(tolua_S,"FIRE_DAMAGE",cEntity::FIRE_DAMAGE); - tolua_constant(tolua_S,"LAVA_TICKS_PER_DAMAGE",cEntity::LAVA_TICKS_PER_DAMAGE); - tolua_constant(tolua_S,"LAVA_DAMAGE",cEntity::LAVA_DAMAGE); - tolua_constant(tolua_S,"BURN_TICKS_PER_DAMAGE",cEntity::BURN_TICKS_PER_DAMAGE); - tolua_constant(tolua_S,"BURN_DAMAGE",cEntity::BURN_DAMAGE); - tolua_constant(tolua_S,"BURN_TICKS",cEntity::BURN_TICKS); tolua_constant(tolua_S,"etEntity",cEntity::etEntity); tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer); tolua_constant(tolua_S,"etPickup",cEntity::etPickup); @@ -29494,13 +30138,16 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"etBoat",cEntity::etBoat); tolua_constant(tolua_S,"etTNT",cEntity::etTNT); tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); + tolua_constant(tolua_S,"etMob",cEntity::etMob); tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00); tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00); tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00); tolua_function(tolua_S,"IsMob",tolua_AllToLua_cEntity_IsMob00); + tolua_function(tolua_S,"IsFallingBlock",tolua_AllToLua_cEntity_IsFallingBlock00); tolua_function(tolua_S,"IsMinecart",tolua_AllToLua_cEntity_IsMinecart00); tolua_function(tolua_S,"IsBoat",tolua_AllToLua_cEntity_IsBoat00); tolua_function(tolua_S,"IsTNT",tolua_AllToLua_cEntity_IsTNT00); + tolua_function(tolua_S,"IsProjectile",tolua_AllToLua_cEntity_IsProjectile00); tolua_function(tolua_S,"IsA",tolua_AllToLua_cEntity_IsA00); tolua_function(tolua_S,"GetClass",tolua_AllToLua_cEntity_GetClass00); tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cEntity_GetClassStatic00); @@ -29587,6 +30234,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"IsRiding",tolua_AllToLua_cEntity_IsRiding00); tolua_function(tolua_S,"IsSprinting",tolua_AllToLua_cEntity_IsSprinting00); tolua_function(tolua_S,"IsRclking",tolua_AllToLua_cEntity_IsRclking00); + tolua_function(tolua_S,"IsInvisible",tolua_AllToLua_cEntity_IsInvisible00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cPawn","cPawn","cEntity",NULL); tolua_beginmodule(tolua_S,"cPawn"); @@ -29792,6 +30440,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SetVersion",tolua_AllToLua_cPlugin_SetVersion00); tolua_function(tolua_S,"GetDirectory",tolua_AllToLua_cPlugin_GetDirectory00); tolua_function(tolua_S,"GetLocalDirectory",tolua_AllToLua_cPlugin_GetLocalDirectory00); + tolua_function(tolua_S,"GetLocalFolder",tolua_AllToLua_cPlugin_GetLocalFolder00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cPluginLua","cPluginLua","cPlugin",NULL); tolua_beginmodule(tolua_S,"cPluginLua"); @@ -29871,6 +30520,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SetWeather",tolua_AllToLua_cWorld_SetWeather00); tolua_function(tolua_S,"ChangeWeather",tolua_AllToLua_cWorld_ChangeWeather00); tolua_function(tolua_S,"GetWeather",tolua_AllToLua_cWorld_GetWeather00); + tolua_function(tolua_S,"IsWeatherSunny",tolua_AllToLua_cWorld_IsWeatherSunny00); + tolua_function(tolua_S,"IsWeatherRain",tolua_AllToLua_cWorld_IsWeatherRain00); + tolua_function(tolua_S,"IsWeatherStorm",tolua_AllToLua_cWorld_IsWeatherStorm00); + tolua_function(tolua_S,"IsWeatherWet",tolua_AllToLua_cWorld_IsWeatherWet00); tolua_function(tolua_S,"SetNextBlockTick",tolua_AllToLua_cWorld_SetNextBlockTick00); tolua_function(tolua_S,"GetMaxSugarcaneHeight",tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00); tolua_function(tolua_S,"GetMaxCactusHeight",tolua_AllToLua_cWorld_GetMaxCactusHeight00); @@ -30167,11 +30820,11 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName); tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName); tolua_endmodule(tolua_S); - tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","",NULL); + tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL); tolua_beginmodule(tolua_S,"cWebAdmin"); tolua_function(tolua_S,"GetMemoryUsage",tolua_AllToLua_cWebAdmin_GetMemoryUsage00); - tolua_function(tolua_S,"GetPort",tolua_AllToLua_cWebAdmin_GetPort00); tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00); + tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00); tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL); @@ -30199,6 +30852,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00); tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cRoot_BroadcastChat00); tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00); + tolua_function(tolua_S,"GetVirtualRAMUsage",tolua_AllToLua_cRoot_GetVirtualRAMUsage00); + tolua_function(tolua_S,"GetPhysicalRAMUsage",tolua_AllToLua_cRoot_GetPhysicalRAMUsage00); tolua_endmodule(tolua_S); #ifdef __cplusplus tolua_cclass(tolua_S,"Vector3f","Vector3f","",tolua_collect_Vector3f); @@ -30488,7 +31143,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_beginmodule(tolua_S,"cChunkDesc"); tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cChunkDesc_GetChunkX00); tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cChunkDesc_GetChunkZ00); - tolua_function(tolua_S,"SetChunkCoords",tolua_AllToLua_cChunkDesc_SetChunkCoords00); tolua_function(tolua_S,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00); tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00); tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00); @@ -30594,34 +31248,35 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL); tolua_beginmodule(tolua_S,"cMonster"); + tolua_constant(tolua_S,"mtBat",cMonster::mtBat); + tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); + tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); + tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken); + tolua_constant(tolua_S,"mtCow",cMonster::mtCow); tolua_constant(tolua_S,"mtCreeper",cMonster::mtCreeper); - tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton); - tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider); - tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant); - tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); - tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime); - tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast); - tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); + tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon); tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman); - tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); - tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish); - tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); + tolua_constant(tolua_S,"mtGhast",cMonster::mtGhast); + tolua_constant(tolua_S,"mtGiant",cMonster::mtGiant); + tolua_constant(tolua_S,"mtHorse",cMonster::mtHorse); + tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem); tolua_constant(tolua_S,"mtMagmaCube",cMonster::mtMagmaCube); - tolua_constant(tolua_S,"mtEnderDragon",cMonster::mtEnderDragon); - tolua_constant(tolua_S,"mtWither",cMonster::mtWither); - tolua_constant(tolua_S,"mtBat",cMonster::mtBat); - tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch); + tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom); + tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot); tolua_constant(tolua_S,"mtPig",cMonster::mtPig); tolua_constant(tolua_S,"mtSheep",cMonster::mtSheep); - tolua_constant(tolua_S,"mtCow",cMonster::mtCow); - tolua_constant(tolua_S,"mtChicken",cMonster::mtChicken); - tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid); - tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); - tolua_constant(tolua_S,"mtMooshroom",cMonster::mtMooshroom); + tolua_constant(tolua_S,"mtSilverfish",cMonster::mtSilverfish); + tolua_constant(tolua_S,"mtSkeleton",cMonster::mtSkeleton); + tolua_constant(tolua_S,"mtSlime",cMonster::mtSlime); tolua_constant(tolua_S,"mtSnowGolem",cMonster::mtSnowGolem); - tolua_constant(tolua_S,"mtOcelot",cMonster::mtOcelot); - tolua_constant(tolua_S,"mtIronGolem",cMonster::mtIronGolem); + tolua_constant(tolua_S,"mtSpider",cMonster::mtSpider); + tolua_constant(tolua_S,"mtSquid",cMonster::mtSquid); tolua_constant(tolua_S,"mtVillager",cMonster::mtVillager); + tolua_constant(tolua_S,"mtWitch",cMonster::mtWitch); + tolua_constant(tolua_S,"mtWither",cMonster::mtWither); + tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); + tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); + tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL); diff --git a/source/Bindings.h b/source/Bindings.h index 4d1777180..1d567520c 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 09/16/13 11:41:45.
+** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:22.
*/
/* Exported function */
diff --git a/source/BlockID.cpp b/source/BlockID.cpp index ecdbc0c34..7c3fa0b8e 100644 --- a/source/BlockID.cpp +++ b/source/BlockID.cpp @@ -307,6 +307,48 @@ EMCSBiome StringToBiome(const AString & a_BiomeString) {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 (int i = 0; i < ARRAYCOUNT(BiomeMap); i++) @@ -518,6 +560,21 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString) +cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default) +{ + AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default); + cItem res; + if (!StringToItem(ItemStr, res)) + { + res.Empty(); + } + return res; +} + + + + + // This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: class cBlockPropertiesInitializer { @@ -603,6 +660,7 @@ public: g_BlockTransparent[E_BLOCK_FIRE] = true; g_BlockTransparent[E_BLOCK_FLOWER_POT] = true; g_BlockTransparent[E_BLOCK_GLASS] = true; + g_BlockTransparent[E_BLOCK_GLASS_PANE] = true; g_BlockTransparent[E_BLOCK_ICE] = true; g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; g_BlockTransparent[E_BLOCK_LEAVES] = true; @@ -646,7 +704,6 @@ public: g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true; g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true; g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true; - g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true; g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true; g_BlockOneHitDig[E_BLOCK_RED_ROSE] = true; g_BlockOneHitDig[E_BLOCK_REEDS] = true; diff --git a/source/BlockID.h b/source/BlockID.h index c2bf8dbdf..28725406d 100644 --- a/source/BlockID.h +++ b/source/BlockID.h @@ -746,8 +746,9 @@ enum eExplosionSource -// fwd: cItem.h: +// fwd: class cItem; +class cIniFile; @@ -785,6 +786,9 @@ extern AString DamageTypeToString(eDamageType a_DamageType); /// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure extern eDamageType StringToDamageType(const AString & a_DamageString); +/// Returns a cItem representing the item described in an IniFile's value; if the value doesn't exist, creates it with the provided default. +extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default); + // tolua_end diff --git a/source/Blocks/BlockCrops.h b/source/Blocks/BlockCrops.h index 9e19b14ec..e7b320eac 100644 --- a/source/Blocks/BlockCrops.h +++ b/source/Blocks/BlockCrops.h @@ -78,10 +78,16 @@ public: void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta < 7) + NIBBLETYPE Light = a_World->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); + + if ((Meta < 7) && (Light > 8)) { a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); } + else if (Light < 9) + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } } diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h index 6cab1fa38..7bc71f7f3 100644 --- a/source/Blocks/BlockFarmland.h +++ b/source/Blocks/BlockFarmland.h @@ -30,30 +30,38 @@ public: virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override { - // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining. - // NOTE: The desert biomes do not get precipitation, so another check needs to be made. + bool Found = false; - // Search for water in a close proximity: - // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) + int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ); + if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills)) { - // Too close to the world edge, cannot check surroudnings; don't tick at all - return; + // Rain hydrates farmland, too, except in Desert biomes. + Found = true; } - bool Found = false; - int NumBlocks = Area.GetBlockCount(); - BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); - for (int i = 0; i < NumBlocks; i++) + else { - if ( - (BlockTypes[i] == E_BLOCK_WATER) || - (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) - ) + // Search for water in a close proximity: + // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) { - Found = true; - break; + // Too close to the world edge, cannot check surroudnings; don't tick at all + return; } + + int NumBlocks = Area.GetBlockCount(); + BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); + for (int i = 0; i < NumBlocks; i++) + { + if ( + (BlockTypes[i] == E_BLOCK_WATER) || + (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) + ) + { + Found = true; + break; + } + } // for i - BlockTypes[] } NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); diff --git a/source/Blocks/BlockPlanks.h b/source/Blocks/BlockPlanks.h index b30164741..f3b8dbfb6 100644 --- a/source/Blocks/BlockPlanks.h +++ b/source/Blocks/BlockPlanks.h @@ -24,7 +24,7 @@ public: ) override { a_BlockType = m_BlockType; - NIBBLETYPE Meta = a_Player->GetEquippedItem().m_ItemDamage; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); a_BlockMeta = Meta; return true; } diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h index dd4544586..cb5ee995a 100644 --- a/source/Blocks/BlockWood.h +++ b/source/Blocks/BlockWood.h @@ -24,7 +24,7 @@ public: ) override { a_BlockType = m_BlockType; - NIBBLETYPE Meta = a_Player->GetEquippedItem().m_ItemDamage; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta); return true; } diff --git a/source/ChunkDef.h b/source/ChunkDef.h index 4cc2d15b0..9db88f293 100644 --- a/source/ChunkDef.h +++ b/source/ChunkDef.h @@ -93,9 +93,54 @@ enum EMCSBiome biJungle = 21, biJungleHills = 22, - // Automatically capture the maximum biome value into biMaxBiome: + // 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 + 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 diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp index 074b50bad..f67a546fd 100644 --- a/source/ClientHandle.cpp +++ b/source/ClientHandle.cpp @@ -735,6 +735,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo cWorld * World = m_Player->GetWorld(); ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ); + // The ItemHandler is also responsible for spawning the pickups BlockHandler(a_OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); World->BroadcastSoundParticleEffect(2001, a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, a_OldBlock, this); diff --git a/source/ClientHandle.h b/source/ClientHandle.h index 01059c511..ef6dbd124 100644 --- a/source/ClientHandle.h +++ b/source/ClientHandle.h @@ -1,3 +1,4 @@ + // cClientHandle.h // Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet diff --git a/source/CraftingRecipes.cpp b/source/CraftingRecipes.cpp index 13a8ac1e0..9dc471781 100644 --- a/source/CraftingRecipes.cpp +++ b/source/CraftingRecipes.cpp @@ -310,7 +310,7 @@ void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid & void cCraftingRecipes::LoadRecipes(void) { - LOG("-- Loading crafting recipes from crafting.txt --"); + LOGD("Loading crafting recipes from crafting.txt..."); ClearRecipes(); // Load the crafting.txt file: @@ -338,7 +338,7 @@ void cCraftingRecipes::LoadRecipes(void) } AddRecipeLine(LineNum, Recipe); } // for itr - Split[] - LOG("-- %d crafting recipes loaded from crafting.txt --", m_Recipes.size()); + LOG("Loaded %d crafting recipes", m_Recipes.size()); } diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp index dc3c7796e..d465c75bd 100644 --- a/source/Entities/Entity.cpp +++ b/source/Entities/Entity.cpp @@ -549,12 +549,11 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); - BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ ); + BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block { if (m_bOnGround) // check if it's still on the ground { - BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ ); if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. { m_bOnGround = false; @@ -613,7 +612,7 @@ 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 + else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts { fallspeed = 0; m_bOnGround = true; diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h index 064275c06..c6b70a7fc 100644 --- a/source/Entities/Entity.h +++ b/source/Entities/Entity.h @@ -61,7 +61,26 @@ struct TakeDamageInfo // tolua_begin class cEntity { -public: +public: + + enum eEntityType + { + etEntity, // For all other types + etPlayer, + etPickup, + etMonster, + etFallingBlock, + etMinecart, + etBoat, + etTNT, + etProjectile, + + // Common variations + etMob = etMonster, // DEPRECATED, use etMonster instead! + } ; + + // tolua_end + enum { ENTITY_STATUS_HURT = 2, @@ -91,21 +110,6 @@ public: BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire } ; - enum eEntityType - { - etEntity, // For all other types - etPlayer, - etPickup, - etMonster, - etFallingBlock, - etMinecart, - etBoat, - etTNT, - etProjectile, - } ; - - // tolua_end - cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); virtual ~cEntity(); @@ -236,16 +240,16 @@ public: /// Returns the curently equipped weapon; empty item if none virtual cItem GetEquippedWeapon(void) const { return cItem(); } - /// Returns the currently equipped helmet; empty item if nonte + /// Returns the currently equipped helmet; empty item if none virtual cItem GetEquippedHelmet(void) const { return cItem(); } - /// Returns the currently equipped chestplate; empty item if nonte + /// Returns the currently equipped chestplate; empty item if none virtual cItem GetEquippedChestplate(void) const { return cItem(); } - /// Returns the currently equipped leggings; empty item if nonte + /// Returns the currently equipped leggings; empty item if none virtual cItem GetEquippedLeggings(void) const { return cItem(); } - /// Returns the currently equipped boots; empty item if nonte + /// Returns the currently equipped boots; empty item if none virtual cItem GetEquippedBoots(void) const { return cItem(); } /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) diff --git a/source/Entities/Minecart.cpp b/source/Entities/Minecart.cpp index 4787f9157..f75e23d8b 100644 --- a/source/Entities/Minecart.cpp +++ b/source/Entities/Minecart.cpp @@ -8,6 +8,7 @@ #include "Minecart.h" #include "../World.h" #include "../ClientHandle.h" +#include "../Chunk.h" #include "Player.h" @@ -52,34 +53,43 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) { - if ((GetPosY() > 0) && (GetPosY() < cChunkDef::Height)) + int PosY = (int)floor(GetPosY()); + if ((PosY <= 0) || (PosY >= cChunkDef::Height)) { - BLOCKTYPE BelowType = GetWorld()->GetBlock(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ())); - BLOCKTYPE InsideType = GetWorld()->GetBlock(floor(GetPosX()), floor(GetPosY()), floor(GetPosZ())); + // Outside the world, just process normal falling physics + super::HandlePhysics(a_Dt, a_Chunk); + BroadcastMovementUpdate(); + return; + } + + int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + if (Chunk == NULL) + { + // 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)) + if (IsBlockRail(BelowType)) + { + HandleRailPhysics(a_Dt, *Chunk); + } + else + { + if (IsBlockRail(InsideType)) { - HandleRailPhysics(a_Dt, a_Chunk); + SetPosY(PosY + 1); + HandleRailPhysics(a_Dt, *Chunk); } else { - if (IsBlockRail(InsideType)) - { - SetPosY(ceil(GetPosY())); - HandleRailPhysics(a_Dt, a_Chunk); - } - else - { - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - } + super::HandlePhysics(a_Dt, *Chunk); + BroadcastMovementUpdate(); } } - else - { - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - } } @@ -88,6 +98,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) static const double MAX_SPEED = 8; static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); + void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) { @@ -99,7 +110,9 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) */ // Get block meta below the cart - NIBBLETYPE BelowMeta = GetWorld()->GetBlockMeta(floor(GetPosX()), floor(GetPosY() -1 ), floor(GetPosZ())); + 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) diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h index e35914684..488f91fb2 100644 --- a/source/Entities/Pickup.h +++ b/source/Entities/Pickup.h @@ -24,7 +24,7 @@ class cPickup : public: CLASS_PROTODEF(cPickup); - cPickup(double a_MicroPosX, double a_MicroPosY, double a_MicroPosZ, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export + cPickup(double a_X, double a_Y, double a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export cItem & GetItem(void) {return m_Item; } // tolua_export const cItem & GetItem(void) const {return m_Item; } diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp index 9132bdbd0..d93b45614 100644 --- a/source/Entities/Player.cpp +++ b/source/Entities/Player.cpp @@ -16,7 +16,6 @@ #include "../Item.h" #include "../Tracer.h" #include "../Root.h" -#include "../OSSupport/MakeDir.h" #include "../OSSupport/Timer.h" #include "../MersenneTwister.h" #include "../Chunk.h" @@ -1340,7 +1339,7 @@ bool cPlayer::LoadFromDisk() bool cPlayer::SaveToDisk() { - cMakeDir::MakeDir("players"); + cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); // create the JSON data Json::Value JSON_PlayerPosition; diff --git a/source/FurnaceRecipe.cpp b/source/FurnaceRecipe.cpp index 8b1ee09a2..2e2276981 100644 --- a/source/FurnaceRecipe.cpp +++ b/source/FurnaceRecipe.cpp @@ -51,7 +51,7 @@ cFurnaceRecipe::~cFurnaceRecipe() void cFurnaceRecipe::ReloadRecipes(void) { ClearRecipes(); - LOG("-- Loading furnace recipes --"); + LOGD("Loading furnace recipes..."); std::ifstream f; char a_File[] = "furnace.txt"; @@ -175,7 +175,7 @@ void cFurnaceRecipe::ReloadRecipes(void) { LOGERROR("ERROR: FurnaceRecipe, syntax error" ); } - LOG("Got %u furnace recipes, and %u fuels.", m_pState->Recipes.size(), m_pState->Fuel.size()); + LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); } diff --git a/source/Generating/BioGen.cpp b/source/Generating/BioGen.cpp index 4345852c6..926120afc 100644 --- a/source/Generating/BioGen.cpp +++ b/source/Generating/BioGen.cpp @@ -27,7 +27,7 @@ void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap -void cBioGenConstant::Initialize(cIniFile & a_IniFile) +void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile) { AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains"); m_Biome = StringToBiome(Biome); @@ -131,10 +131,10 @@ void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a -void cBioGenCache::Initialize(cIniFile & a_IniFile) +void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile) { - super::Initialize(a_IniFile); - m_BioGenToCache->Initialize(a_IniFile); + super::InitializeBiomeGen(a_IniFile); + m_BioGenToCache->InitializeBiomeGen(a_IniFile); } @@ -242,9 +242,9 @@ void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::Biome -void cBioGenCheckerboard::Initialize(cIniFile & a_IniFile) +void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile) { - super::Initialize(a_IniFile); + super::InitializeBiomeGen(a_IniFile); AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", ""); m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64); m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize; @@ -276,9 +276,9 @@ void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & -void cBioGenVoronoi::Initialize(cIniFile & a_IniFile) +void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { - super::Initialize(a_IniFile); + super::InitializeBiomeGen(a_IniFile); m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64); AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", ""); InitializeBiomes(Biomes); @@ -358,9 +358,9 @@ void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::B -void cBioGenDistortedVoronoi::Initialize(cIniFile & a_IniFile) +void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { - // Do NOT call super::Initialize(), as it would try to read Voronoi params instead of DistortedVoronoi params + // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", ""); InitializeBiomes(Biomes); @@ -409,7 +409,7 @@ cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) : -void cBioGenMultiStepMap::Initialize(cIniFile & a_IniFile) +void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile) { m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize); m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize); diff --git a/source/Generating/BioGen.h b/source/Generating/BioGen.h index f2afc3e8c..bc70bfab2 100644 --- a/source/Generating/BioGen.h +++ b/source/Generating/BioGen.h @@ -33,7 +33,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; } ; @@ -72,7 +72,7 @@ protected: int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; } ; @@ -109,7 +109,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; } ; @@ -134,7 +134,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ); } ; @@ -156,7 +156,7 @@ public: protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; /// Distorts the coords using a Perlin-like noise void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ); @@ -195,7 +195,7 @@ protected: // cBiomeGen overrides: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void Initialize(cIniFile & a_IniFile) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; /** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore diff --git a/source/Generating/ChunkDesc.h b/source/Generating/ChunkDesc.h index 41b85a814..067d8494a 100644 --- a/source/Generating/ChunkDesc.h +++ b/source/Generating/ChunkDesc.h @@ -36,13 +36,13 @@ public: cChunkDesc(int a_ChunkX, int a_ChunkZ); ~cChunkDesc(); + void SetChunkCoords(int a_ChunkX, int a_ChunkZ); + // tolua_begin int GetChunkX(void) const { return m_ChunkX; } int GetChunkZ(void) const { return m_ChunkZ; } - void SetChunkCoords(int a_ChunkX, int a_ChunkZ); - void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); @@ -74,7 +74,7 @@ public: /// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk. void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite); - /// Reads an area from the chunk into a cBlockArea + /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ); /// Returns the maximum height value in the heightmap diff --git a/source/Generating/CompoGen.cpp b/source/Generating/CompoGen.cpp index b99919e0d..cc2a203af 100644 --- a/source/Generating/CompoGen.cpp +++ b/source/Generating/CompoGen.cpp @@ -10,7 +10,9 @@ #include "Globals.h" #include "CompoGen.h" #include "../BlockID.h" +#include "../Item.h" #include "../LinearUpscale.h" +#include "../../iniFile/iniFile.h" @@ -48,6 +50,16 @@ void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType); + m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenDebugBiomes: @@ -102,20 +114,16 @@ void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenClassic: -cCompoGenClassic::cCompoGenClassic( - int a_SeaLevel, int a_BeachHeight, int a_BeachDepth, - BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom, - BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea -) : - m_SeaLevel(a_SeaLevel), - m_BeachHeight(a_BeachHeight), - m_BeachDepth(a_BeachDepth), - m_BlockTop(a_BlockTop), - m_BlockMiddle(a_BlockMiddle), - m_BlockBottom(a_BlockBottom), - m_BlockBeach(a_BlockBeach), - m_BlockBeachBottom(a_BlockBeachBottom), - m_BlockSea(a_BlockSea) +cCompoGenClassic::cCompoGenClassic(void) : + m_SeaLevel(60), + m_BeachHeight(2), + m_BeachDepth(4), + m_BlockTop(E_BLOCK_GRASS), + m_BlockMiddle(E_BLOCK_DIRT), + m_BlockBottom(E_BLOCK_STONE), + m_BlockBeach(E_BLOCK_SAND), + m_BlockBeachBottom(E_BLOCK_SANDSTONE), + m_BlockSea(E_BLOCK_STATIONARY_WATER) { } @@ -184,6 +192,23 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel); + m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight); + m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth); + m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType); + m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType); + m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType); + m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType); + m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType); + m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenBiomal: @@ -286,6 +311,15 @@ void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1; +} + + + + + void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) { BLOCKTYPE Pattern[] = @@ -485,10 +519,19 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cCompoGenCache: -cCompoGenCache::cCompoGenCache(cTerrainCompositionGen * a_Underlying, int a_CacheSize) : +cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) : m_Underlying(a_Underlying), m_CacheSize(a_CacheSize), m_CacheOrder(new int[a_CacheSize]), @@ -521,13 +564,13 @@ cCompoGenCache::~cCompoGenCache() void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) { - //* + #ifdef _DEBUG if (((m_NumHits + m_NumMisses) % 1024) == 10) { LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); } - //*/ + #endif // _DEBUG int ChunkX = a_ChunkDesc.GetChunkX(); int ChunkZ = a_ChunkDesc.GetChunkZ(); @@ -562,7 +605,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) // Not in the cache: m_NumMisses++; - m_Underlying->ComposeTerrain(a_ChunkDesc); + m_Underlying.ComposeTerrain(a_ChunkDesc); // Insert it as the first item in the MRU order: int Idx = m_CacheOrder[m_CacheSize - 1]; @@ -580,3 +623,12 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) + +void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Underlying.InitializeCompoGen(a_IniFile); +} + + + + diff --git a/source/Generating/CompoGen.h b/source/Generating/CompoGen.h index 8391de66e..2ee286b06 100644 --- a/source/Generating/CompoGen.h +++ b/source/Generating/CompoGen.h @@ -27,9 +27,9 @@ class cCompoGenSameBlock : public cTerrainCompositionGen { public: - cCompoGenSameBlock(BLOCKTYPE a_BlockType, bool a_IsBedrocked) : - m_BlockType(a_BlockType), - m_IsBedrocked(a_IsBedrocked) + cCompoGenSameBlock(void) : + m_BlockType(E_BLOCK_STONE), + m_IsBedrocked(true) {} protected: @@ -39,6 +39,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; @@ -65,11 +66,7 @@ class cCompoGenClassic : public cTerrainCompositionGen { public: - cCompoGenClassic( - int a_SeaLevel, int a_BeachHeight, int a_BeachDepth, - BLOCKTYPE a_BlockTop, BLOCKTYPE a_BlockMiddle, BLOCKTYPE a_BlockBottom, - BLOCKTYPE a_BlockBeach, BLOCKTYPE a_BlockBeachBottom, BLOCKTYPE a_BlockSea - ); + cCompoGenClassic(void); protected: @@ -85,6 +82,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; @@ -95,9 +93,9 @@ class cCompoGenBiomal : public cTerrainCompositionGen { public: - cCompoGenBiomal(int a_Seed, int a_SeaLevel) : + cCompoGenBiomal(int a_Seed) : m_Noise(a_Seed + 1000), - m_SeaLevel(a_SeaLevel - 1) // we do an adjustment later in filling the terrain with water + m_SeaLevel(62) { } @@ -108,6 +106,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); @@ -136,6 +135,7 @@ protected: // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; @@ -147,15 +147,16 @@ class cCompoGenCache : public cTerrainCompositionGen { public: - cCompoGenCache(cTerrainCompositionGen * a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying + cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying ~cCompoGenCache(); // cTerrainCompositionGen override: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; protected: - cTerrainCompositionGen * m_Underlying; + cTerrainCompositionGen & m_Underlying; struct sCacheData { diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index 0852f559e..2637b64e7 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -211,7 +211,7 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) m_UnderlyingBiomeGen = m_BiomeGen; m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); } - m_BiomeGen->Initialize(a_IniFile); + m_BiomeGen->InitializeBiomeGen(a_IniFile); } @@ -231,35 +231,24 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) bool CacheOffByDefault = false; if (NoCaseCompare(HeightGenName, "flat") == 0) { - int Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", 5); - m_HeightGen = new cHeiGenFlat(Height); + m_HeightGen = new cHeiGenFlat; CacheOffByDefault = true; // We're generating faster than a cache would retrieve data } else if (NoCaseCompare(HeightGenName, "classic") == 0) { - // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values): - float HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); - float HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); - float HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); - float HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); - float HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); - float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); - m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3); + m_HeightGen = new cHeiGenClassic(Seed); } else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) { m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - ((cDistortedHeightmap *)m_HeightGen)->Initialize(a_IniFile); } else if (NoCaseCompare(HeightGenName, "End") == 0) { m_HeightGen = new cEndGen(Seed); - ((cEndGen *)m_HeightGen)->Initialize(a_IniFile); } else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) { m_HeightGen = new cNoise3DComposable(Seed); - ((cNoise3DComposable *)m_HeightGen)->Initialize(a_IniFile); } else // "biomal" or <not found> { @@ -283,6 +272,9 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) //*/ } + // Read the settings: + m_HeightGen->InitializeHeightGen(a_IniFile); + // Add a cache, if requested: int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); if (CacheSize > 0) @@ -294,9 +286,9 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) ); CacheSize = 4; } - LOGINFO("Using a cache for Heightgen of size %d.", CacheSize); + LOGD("Using a cache for Heightgen of size %d.", CacheSize); m_UnderlyingHeightGen = m_HeightGen; - m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize); + m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize); } } @@ -306,6 +298,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()) { @@ -314,9 +307,7 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) } if (NoCaseCompare(CompoGenName, "sameblock") == 0) { - int Block = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "SameBlockType", "stone"); - bool Bedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); - m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked); + m_CompositionGen = new cCompoGenSameBlock; } else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) { @@ -324,38 +315,23 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) } else if (NoCaseCompare(CompoGenName, "classic") == 0) { - int SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", 60); - int BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", 2); - int BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", 4); - BLOCKTYPE BlockTop = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockTop", "grass"); - BLOCKTYPE BlockMiddle = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt"); - BLOCKTYPE BlockBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBottom", "stone"); - BLOCKTYPE BlockBeach = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeach", "sand"); - BLOCKTYPE BlockBeachBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone"); - BLOCKTYPE BlockSea = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater"); - m_CompositionGen = new cCompoGenClassic( - SeaLevel, BeachHeight, BeachDepth, BlockTop, BlockMiddle, BlockBottom, BlockBeach, - BlockBeachBottom, BlockSea - ); + m_CompositionGen = new cCompoGenClassic; } else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) { - m_CompositionGen = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen); - ((cDistortedHeightmap *)m_CompositionGen)->Initialize(a_IniFile); + m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen); } else if (NoCaseCompare(CompoGenName, "end") == 0) { - m_CompositionGen = new cEndGen(m_ChunkGenerator.GetSeed()); - ((cEndGen *)m_CompositionGen)->Initialize(a_IniFile); + m_CompositionGen = new cEndGen(Seed); } else if (NoCaseCompare(CompoGenName, "nether") == 0) { - m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed()); + m_CompositionGen = new cCompoGenNether(Seed); } else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) { m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); - ((cNoise3DComposable *)m_CompositionGen)->Initialize(a_IniFile); } else { @@ -363,9 +339,7 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) { LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); } - int SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", 62); - int Seed = m_ChunkGenerator.GetSeed(); - m_CompositionGen = new cCompoGenBiomal(Seed, SeaLevel); + m_CompositionGen = new cCompoGenBiomal(Seed); /* // Performance-testing: @@ -383,11 +357,14 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) //*/ } + // Read the settings from the ini file: + m_CompositionGen->InitializeCompoGen(a_IniFile); + int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); if (CompoGenCacheSize > 1) { m_UnderlyingCompositionGen = m_CompositionGen; - m_CompositionGen = new cCompoGenCache(m_UnderlyingCompositionGen, 32); + m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32); } } diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h index 1d5c98e5d..d5e33a439 100644 --- a/source/Generating/ComposableGenerator.h +++ b/source/Generating/ComposableGenerator.h @@ -47,7 +47,7 @@ public: virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; /// Reads parameters from the ini file, prepares generator for use. - virtual void Initialize(cIniFile & a_IniFile) {} + virtual void InitializeBiomeGen(cIniFile & a_IniFile) {} } ; @@ -67,6 +67,9 @@ public: /// Generates heightmap for the given chunk virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeHeightGen(cIniFile & a_IniFile) {} } ; @@ -84,6 +87,9 @@ public: virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeCompoGen(cIniFile & a_IniFile) {} } ; diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp index 6ac4d61d5..98eab31b5 100644 --- a/source/Generating/DistortedHeightmap.cpp +++ b/source/Generating/DistortedHeightmap.cpp @@ -60,7 +60,7 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : m_OceanFloorSelect(a_Seed + 3000), m_BiomeGen(a_BiomeGen), m_UnderlyingHeiGen(a_Seed, a_BiomeGen), - m_HeightGen(&m_UnderlyingHeiGen, 64) + m_HeightGen(m_UnderlyingHeiGen, 64) { m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); @@ -77,11 +77,18 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) { + if (m_IsInitialized) + { + return; + } + // Read the params from the INI file: m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); + + m_IsInitialized = true; } @@ -184,6 +191,15 @@ void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::He +void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) { // Frequencies for the ocean floor selecting noise: @@ -311,6 +327,15 @@ void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) { int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); diff --git a/source/Generating/DistortedHeightmap.h b/source/Generating/DistortedHeightmap.h index b2b235e61..6d7007375 100644 --- a/source/Generating/DistortedHeightmap.h +++ b/source/Generating/DistortedHeightmap.h @@ -30,8 +30,6 @@ class cDistortedHeightmap : public: cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); - void Initialize(cIniFile & a_IniFile); - protected: typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; @@ -77,6 +75,9 @@ protected: NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; + /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen) + bool m_IsInitialized; + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) void PrepareState(int a_ChunkX, int a_ChunkZ); @@ -93,10 +94,15 @@ protected: /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); + /// Reads the settings from the ini file. Skips reading if already initialized + void Initialize(cIniFile & a_IniFile); + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; // cTerrainCompositionGen overrides: virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; } ; diff --git a/source/Generating/HeiGen.cpp b/source/Generating/HeiGen.cpp index 2aa942c73..5dee181b7 100644 --- a/source/Generating/HeiGen.cpp +++ b/source/Generating/HeiGen.cpp @@ -6,6 +6,8 @@ #include "Globals.h" #include "HeiGen.h" #include "../LinearUpscale.h" +#include "../../iniFile/iniFile.h" + @@ -26,10 +28,19 @@ void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap +void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenCache: -cHeiGenCache::cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize) : +cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) : m_HeiGenToCache(a_HeiGenToCache), m_CacheSize(a_CacheSize), m_CacheOrder(new int[a_CacheSize]), @@ -99,7 +110,7 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap // Not in the cache: m_NumMisses++; - m_HeiGenToCache->GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); + m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); // Insert it as the first item in the MRU order: int Idx = m_CacheOrder[m_CacheSize - 1]; @@ -117,6 +128,15 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap +void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeiGenToCache.InitializeHeightGen(a_IniFile); +} + + + + + bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height) { for (int i = 0; i < m_CacheSize; i++) @@ -137,17 +157,10 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenClassic: -cHeiGenClassic::cHeiGenClassic(int a_Seed, float a_HeightFreq1, float a_HeightAmp1, float a_HeightFreq2, float a_HeightAmp2, float a_HeightFreq3, float a_HeightAmp3) : +cHeiGenClassic::cHeiGenClassic(int a_Seed) : m_Seed(a_Seed), - m_Noise(a_Seed), - m_HeightFreq1(a_HeightFreq1), - m_HeightAmp1 (a_HeightAmp1), - m_HeightFreq2(a_HeightFreq2), - m_HeightAmp2 (a_HeightAmp2), - m_HeightFreq3(a_HeightFreq3), - m_HeightAmp3 (a_HeightAmp3) + m_Noise(a_Seed) { - // Nothing needed yet } @@ -199,6 +212,20 @@ void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightM +void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); + m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); + m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); + m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); + m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); + m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cHeiGenBiomal: @@ -294,6 +321,15 @@ void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMa +void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile) +{ + // No user-settable params +} + + + + + NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors) { // Sum up how many biomes of each type there are in the neighborhood: diff --git a/source/Generating/HeiGen.h b/source/Generating/HeiGen.h index 437b5f104..1b246c70a 100644 --- a/source/Generating/HeiGen.h +++ b/source/Generating/HeiGen.h @@ -25,14 +25,15 @@ class cHeiGenFlat : public cTerrainHeightGen { public: - cHeiGenFlat(int a_Height) : m_Height(a_Height) {} + cHeiGenFlat(void) : m_Height(5) {} protected: int m_Height; - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; } ; @@ -44,18 +45,19 @@ class cHeiGenCache : public cTerrainHeightGen { public: - cHeiGenCache(cTerrainHeightGen * a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache + cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache ~cHeiGenCache(); - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; /// Retrieves height at the specified point in the cache, returns true if found, false if not found bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height); protected: - cTerrainHeightGen * m_HeiGenToCache; + cTerrainHeightGen & m_HeiGenToCache; struct sCacheData { @@ -83,7 +85,7 @@ class cHeiGenClassic : public cTerrainHeightGen { public: - cHeiGenClassic(int a_Seed, float a_HeightFreq1, float a_HeightAmp1, float a_HeightFreq2, float a_HeightAmp2, float a_HeightFreq3, float a_HeightAmp3); + cHeiGenClassic(int a_Seed); protected: @@ -95,8 +97,9 @@ protected: float GetNoise(float x, float y); - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; } ; @@ -130,8 +133,9 @@ protected: } ; static const sGenParam m_GenParam[biNumBiomes]; - // cTerrainHeightGen override: + // cTerrainHeightGen overrides: virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors); } ; diff --git a/source/Globals.h b/source/Globals.h index 150051de0..1e531f7f3 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -109,7 +109,6 @@ typedef unsigned short UInt16; #endif // GetFreeSpace #else #include <sys/types.h> - #include <sys/stat.h> // for mkdir #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> @@ -142,6 +141,7 @@ typedef unsigned short UInt16; // CRT stuff: +#include <sys/stat.h> #include <assert.h> #include <stdio.h> #include <math.h> diff --git a/source/GroupManager.cpp b/source/GroupManager.cpp index cef32dd58..b79fde9dc 100644 --- a/source/GroupManager.cpp +++ b/source/GroupManager.cpp @@ -43,7 +43,7 @@ cGroupManager::~cGroupManager() cGroupManager::cGroupManager() : m_pState( new sGroupManagerState ) { - LOG("-- Loading Groups --"); + LOGD("-- Loading Groups --"); cIniFile IniFile("groups.ini"); if (!IniFile.ReadFile()) { @@ -57,7 +57,7 @@ cGroupManager::cGroupManager() std::string KeyName = IniFile.GetKeyName( i ); cGroup* Group = GetGroup( KeyName.c_str() ); - LOG("Loading group: %s", KeyName.c_str() ); + LOGD("Loading group: %s", KeyName.c_str() ); Group->SetName( KeyName ); char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; @@ -73,7 +73,6 @@ cGroupManager::cGroupManager() for( unsigned int i = 0; i < Split.size(); i++) { Group->AddCommand( Split[i] ); - //LOG("%s", Split[i].c_str() ); } } @@ -84,7 +83,6 @@ cGroupManager::cGroupManager() for( unsigned int i = 0; i < Split.size(); i++) { Group->AddPermission( Split[i] ); - //LOGINFO("Permission: %s", Split[i].c_str() ); } } @@ -98,7 +96,7 @@ cGroupManager::cGroupManager() } } } - LOG("-- Groups Successfully Loaded --"); + LOGD("-- Groups Successfully Loaded --"); } diff --git a/source/HTTPServer/EnvelopeParser.cpp b/source/HTTPServer/EnvelopeParser.cpp new file mode 100644 index 000000000..8dbe05f14 --- /dev/null +++ b/source/HTTPServer/EnvelopeParser.cpp @@ -0,0 +1,132 @@ + +// EnvelopeParser.cpp + +// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME + +#include "Globals.h" +#include "EnvelopeParser.h" + + + + + +cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsInHeaders(true) +{ +} + + + + + +int cEnvelopeParser::Parse(const char * a_Data, int a_Size) +{ + if (!m_IsInHeaders) + { + return 0; + } + + // Start searching 1 char from the end of the already received data, if available: + size_t SearchStart = m_IncomingData.size(); + SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0; + + m_IncomingData.append(a_Data, a_Size); + + size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart); + if (idxCRLF == AString::npos) + { + // Not a complete line yet, all input consumed: + return a_Size; + } + + // Parse as many lines as found: + size_t Last = 0; + do + { + if (idxCRLF == Last) + { + // This was the last line of the data. Finish whatever value has been cached and return: + NotifyLast(); + m_IsInHeaders = false; + return a_Size - (m_IncomingData.size() - idxCRLF) + 2; + } + if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last)) + { + // An error has occurred + m_IsInHeaders = false; + return -1; + } + Last = idxCRLF + 2; + idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2); + } while (idxCRLF != AString::npos); + m_IncomingData.erase(0, Last); + + // Parsed all lines and still expecting more + return a_Size; +} + + + + + +void cEnvelopeParser::Reset(void) +{ + m_IsInHeaders = true; + m_IncomingData.clear(); + m_LastKey.clear(); + m_LastValue.clear(); +} + + + + + +void cEnvelopeParser::NotifyLast(void) +{ + if (!m_LastKey.empty()) + { + m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue); + m_LastKey.clear(); + } + m_LastValue.clear(); +} + + + + + +bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size) +{ + ASSERT(a_Size > 0); + if (a_Data[0] <= ' ') + { + // This line is a continuation for the previous line + if (m_LastKey.empty()) + { + return false; + } + // Append, including the whitespace in a_Data[0] + m_LastValue.append(a_Data, a_Size); + return true; + } + + // This is a line with a new key: + NotifyLast(); + for (size_t i = 0; i < a_Size; i++) + { + if (a_Data[i] == ':') + { + m_LastKey.assign(a_Data, i); + m_LastValue.assign(a_Data + i + 2, a_Size - i - 2); + return true; + } + } // for i - a_Data[] + + // No colon was found, key-less header?? + return false; +} + + + + diff --git a/source/HTTPServer/EnvelopeParser.h b/source/HTTPServer/EnvelopeParser.h new file mode 100644 index 000000000..6430fbebf --- /dev/null +++ b/source/HTTPServer/EnvelopeParser.h @@ -0,0 +1,69 @@ + +// EnvelopeParser.h + +// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME + + + + + +#pragma once + + + + + +class cEnvelopeParser +{ +public: + class cCallbacks + { + public: + /// Called when a full header line is parsed + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0; + } ; + + + cEnvelopeParser(cCallbacks & a_Callbacks); + + /** Parses the incoming data. + Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header + */ + int Parse(const char * a_Data, int a_Size); + + /// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream + void Reset(void); + + /// Returns true if more input is expected for the envelope header + bool IsInHeaders(void) const { return m_IsInHeaders; } + + /// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions + void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; } + +public: + /// Callbacks to call for the various events + cCallbacks & m_Callbacks; + + /// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data. + bool m_IsInHeaders; + + /// Buffer for the incoming data until it is parsed + AString m_IncomingData; + + /// Holds the last parsed key; used for line-wrapped values + AString m_LastKey; + + /// Holds the last parsed value; used for line-wrapped values + AString m_LastValue; + + + /// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them + void NotifyLast(void); + + /// Parses one line of header data. Returns true if successful + bool ParseLine(const char * a_Data, size_t a_Size); +} ; + + + + diff --git a/source/HTTPServer/HTTPConnection.cpp b/source/HTTPServer/HTTPConnection.cpp new file mode 100644 index 000000000..68afdfc11 --- /dev/null +++ b/source/HTTPServer/HTTPConnection.cpp @@ -0,0 +1,247 @@ + +// HTTPConnection.cpp + +// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server. + +#include "Globals.h" +#include "HTTPConnection.h" +#include "HTTPMessage.h" +#include "HTTPServer.h" + + + + + +cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : + m_HTTPServer(a_HTTPServer), + m_State(wcsRecvHeaders), + m_CurrentRequest(NULL) +{ + // LOGD("HTTP: New connection at %p", this); +} + + + + + +cHTTPConnection::~cHTTPConnection() +{ + // LOGD("HTTP: Del connection at %p", this); +} + + + + + +void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response) +{ + AppendPrintf(m_OutgoingData, "%d %s\r\nContent-Length: 0\r\n\r\n", a_StatusCode, a_Response.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::SendNeedAuth(const AString & a_Realm) +{ + AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; +} + + + + + +void cHTTPConnection::Send(const cHTTPResponse & a_Response) +{ + ASSERT(m_State = wcsRecvIdle); + a_Response.AppendToData(m_OutgoingData); + m_State = wcsSendingResp; + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::Send(const void * a_Data, int a_Size) +{ + ASSERT(m_State == wcsSendingResp); + AppendPrintf(m_OutgoingData, "%x\r\n", a_Size); + m_OutgoingData.append((const char *)a_Data, a_Size); + m_OutgoingData.append("\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::FinishResponse(void) +{ + ASSERT(m_State == wcsSendingResp); + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + m_HTTPServer.NotifyConnectionWrite(*this); +} + + + + + +void cHTTPConnection::AwaitNextRequest(void) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() ) + break; + } + + case wcsRecvIdle: + { + // The client is waiting for a response, send an "Internal server error": + m_OutgoingData.append("HTTP/1.1 500 Internal Server Error\r\n\r\n"); + m_HTTPServer.NotifyConnectionWrite(*this); + m_State = wcsRecvHeaders; + break; + } + + case wcsSendingResp: + { + // The response headers have been sent, we need to terminate the response body: + m_OutgoingData.append("0\r\n\r\n"); + m_State = wcsRecvHeaders; + break; + } + + default: + { + ASSERT(!"Unhandled state recovery"); + break; + } + } +} + + + + + +void cHTTPConnection::Terminate(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + +void cHTTPConnection::DataReceived(const char * a_Data, int a_Size) +{ + switch (m_State) + { + case wcsRecvHeaders: + { + if (m_CurrentRequest == NULL) + { + m_CurrentRequest = new cHTTPRequest; + } + + int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size); + if (BytesConsumed < 0) + { + delete m_CurrentRequest; + m_CurrentRequest = NULL; + m_State = wcsInvalid; + m_HTTPServer.CloseConnection(*this); + return; + } + if (m_CurrentRequest->IsInHeaders()) + { + // The request headers are not yet complete + return; + } + + // The request has finished parsing its headers successfully, notify of it: + m_State = wcsRecvBody; + m_HTTPServer.NewRequest(*this, *m_CurrentRequest); + m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength(); + if (m_CurrentRequestBodyRemaining < 0) + { + // The body length was not specified in the request, assume zero + m_CurrentRequestBodyRemaining = 0; + } + + // Process the rest of the incoming data into the request body: + if (a_Size > BytesConsumed) + { + DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + } + else + { + DataReceived("", 0); // If the request has zero body length, let it be processed right-away + } + break; + } + + case wcsRecvBody: + { + ASSERT(m_CurrentRequest != NULL); + if (m_CurrentRequestBodyRemaining > 0) + { + int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size); + m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume); + m_CurrentRequestBodyRemaining -= BytesToConsume; + } + if (m_CurrentRequestBodyRemaining == 0) + { + m_State = wcsRecvIdle; + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + delete m_CurrentRequest; + m_CurrentRequest = NULL; + } + break; + } + + default: + { + // TODO: Should we be receiving data in this state? + break; + } + } +} + + + + + +void cHTTPConnection::GetOutgoingData(AString & a_Data) +{ + std::swap(a_Data, m_OutgoingData); +} + + + + + +void cHTTPConnection::SocketClosed(void) +{ + if (m_CurrentRequest != NULL) + { + m_HTTPServer.RequestFinished(*this, *m_CurrentRequest); + } + m_HTTPServer.CloseConnection(*this); +} + + + + + diff --git a/source/HTTPServer/HTTPConnection.h b/source/HTTPServer/HTTPConnection.h new file mode 100644 index 000000000..14603bb70 --- /dev/null +++ b/source/HTTPServer/HTTPConnection.h @@ -0,0 +1,101 @@ + +// HTTPConnection.h + +// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server. + + + + + +#pragma once + +#include "../OSSupport/SocketThreads.h" + + + + + +// fwd: +class cHTTPServer; +class cHTTPResponse; +class cHTTPRequest; + + + + + +class cHTTPConnection : + public cSocketThreads::cCallback +{ +public: + + enum eState + { + wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if NULL) + wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid) + wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == NULL) + wcsSendingResp, ///< Sending response body (m_CurrentRequest == NULL) + wcsInvalid, ///< The request was malformed, the connection is closing + } ; + + cHTTPConnection(cHTTPServer & a_HTTPServer); + ~cHTTPConnection(); + + /// Sends HTTP status code together with a_Reason (used for HTTP errors) + void SendStatusAndReason(int a_StatusCode, const AString & a_Reason); + + /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm + void SendNeedAuth(const AString & a_Realm); + + /// Sends the headers contained in a_Response + void Send(const cHTTPResponse & a_Response); + + /// Sends the data as the response (may be called multiple times) + void Send(const void * a_Data, int a_Size); + + /// Sends the data as the response (may be called multiple times) + void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); } + + /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) + void FinishResponse(void); + + /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" + void AwaitNextRequest(void); + + /// Terminates the connection; finishes any request being currently processed + void Terminate(void); + +protected: + typedef std::map<AString, AString> cNameValueMap; + + /// The parent webserver that is to be notified of events on this connection + cHTTPServer & m_HTTPServer; + + /// All the incoming data until the entire request header is parsed + AString m_IncomingHeaderData; + + /// Status in which the request currently is + eState m_State; + + /// Data that is queued for sending, once the socket becomes writable + AString m_OutgoingData; + + /// The request being currently received (valid only between having parsed the headers and finishing receiving the body) + cHTTPRequest * m_CurrentRequest; + + /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody + int m_CurrentRequestBodyRemaining; + + + // cSocketThreads::cCallback overrides: + virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client + virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + virtual void SocketClosed (void) override; // The socket has been closed for any reason +} ; + +typedef std::vector<cHTTPConnection *> cHTTPConnections; + + + + + diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp new file mode 100644 index 000000000..7db7b4e6d --- /dev/null +++ b/source/HTTPServer/HTTPFormParser.cpp @@ -0,0 +1,278 @@ + +// HTTPFormParser.cpp + +// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP + +#include "Globals.h" +#include "HTTPFormParser.h" +#include "HTTPMessage.h" +#include "MultipartParser.h" +#include "NameValueParser.h" + + + + + +cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsValid(true) +{ + if (a_Request.GetMethod() == "GET") + { + m_Kind = fpkURL; + + // Directly parse the URL in the request: + const AString & URL = a_Request.GetURL(); + size_t idxQM = URL.find('?'); + if (idxQM != AString::npos) + { + Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1); + } + return; + } + if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT")) + { + if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0) + { + m_Kind = fpkFormUrlEncoded; + return; + } + if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0) + { + m_Kind = fpkMultipart; + BeginMultipart(a_Request); + return; + } + } + // Invalid method / content type combination, this is not a HTTP form + m_IsValid = false; +} + + + + + +void cHTTPFormParser::Parse(const char * a_Data, int a_Size) +{ + if (!m_IsValid) + { + return; + } + + switch (m_Kind) + { + case fpkURL: + case fpkFormUrlEncoded: + { + // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish() + m_IncomingData.append(a_Data, a_Size); + break; + } + case fpkMultipart: + { + ASSERT(m_MultipartParser.get() != NULL); + m_MultipartParser->Parse(a_Data, a_Size); + break; + } + default: + { + ASSERT(!"Unhandled form kind"); + break; + } + } +} + + + + + +bool cHTTPFormParser::Finish(void) +{ + switch (m_Kind) + { + case fpkURL: + case fpkFormUrlEncoded: + { + // m_IncomingData has all the form data, parse it now: + ParseFormUrlEncoded(); + break; + } + } + return (m_IsValid && m_IncomingData.empty()); +} + + + + + +bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request) +{ + const AString & ContentType = a_Request.GetContentType(); + return ( + (ContentType == "application/x-www-form-urlencoded") || + (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) || + ( + (a_Request.GetMethod() == "GET") && + (a_Request.GetURL().find('?') != AString::npos) + ) + ); + return false; +} + + + + + +void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request) +{ + ASSERT(m_MultipartParser.get() == NULL); + m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this)); +} + + + + + +void cHTTPFormParser::ParseFormUrlEncoded(void) +{ + // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish() + // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway + AStringVector Lines = StringSplit(m_IncomingData, "&"); + for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr) + { + AStringVector Components = StringSplit(*itr, "="); + switch (Components.size()) + { + default: + { + // Neither name nor value, or too many "="s, mark this as invalid form: + m_IsValid = false; + return; + } + case 1: + { + // Only name present + (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = ""; + break; + } + case 2: + { + // name=value format: + (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' ')); + break; + } + } + } // for itr - Lines[] + m_IncomingData.clear(); +} + + + + + +void cHTTPFormParser::OnPartStart(void) +{ + m_CurrentPartFileName.clear(); + m_CurrentPartName.clear(); + m_IsCurrentPartFile = false; + m_FileHasBeenAnnounced = false; +} + + + + + +void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value) +{ + if (NoCaseCompare(a_Key, "Content-Disposition") == 0) + { + size_t len = a_Value.size(); + size_t ParamsStart = AString::npos; + for (size_t i = 0; i < len; ++i) + { + if (a_Value[i] > ' ') + { + if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0) + { + // Content disposition is not "form-data", mark the whole form invalid + m_IsValid = false; + return; + } + ParamsStart = a_Value.find(';', i + 9); + break; + } + } + if (ParamsStart == AString::npos) + { + // There is data missing in the Content-Disposition field, mark the whole form invalid: + m_IsValid = false; + return; + } + + // Parse the field name and optional filename from this header: + cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart); + Parser.Finish(); + m_CurrentPartName = Parser["name"]; + if (!Parser.IsValid() || m_CurrentPartName.empty()) + { + // The required parameter "name" is missing, mark the whole form invalid: + m_IsValid = false; + return; + } + m_CurrentPartFileName = Parser["filename"]; + } +} + + + + + +void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size) +{ + if (m_CurrentPartName.empty()) + { + // Prologue, epilogue or invalid part + return; + } + if (m_CurrentPartFileName.empty()) + { + // This is a variable, store it in the map + iterator itr = find(m_CurrentPartName); + if (itr == end()) + { + (*this)[m_CurrentPartName] = AString(a_Data, a_Size); + } + else + { + itr->second.append(a_Data, a_Size); + } + } + else + { + // This is a file, pass it on through the callbacks + if (!m_FileHasBeenAnnounced) + { + m_Callbacks.OnFileStart(*this, m_CurrentPartFileName); + m_FileHasBeenAnnounced = true; + } + m_Callbacks.OnFileData(*this, a_Data, a_Size); + } +} + + + + + +void cHTTPFormParser::OnPartEnd(void) +{ + if (m_FileHasBeenAnnounced) + { + m_Callbacks.OnFileEnd(*this); + } + m_CurrentPartName.clear(); + m_CurrentPartFileName.clear(); +} + + + + diff --git a/source/HTTPServer/HTTPFormParser.h b/source/HTTPServer/HTTPFormParser.h new file mode 100644 index 000000000..b92ef9d3c --- /dev/null +++ b/source/HTTPServer/HTTPFormParser.h @@ -0,0 +1,107 @@ + +// HTTPFormParser.h + +// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP + + + + +#pragma once + +#include "MultipartParser.h" + + + + + +// fwd: +class cHTTPRequest; + + + + + +class cHTTPFormParser : + public std::map<AString, AString>, + public cMultipartParser::cCallbacks +{ +public: + class cCallbacks + { + public: + /// Called when a new file part is encountered in the form data + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0; + + /// Called when more file data has come for the current file in the form data + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0; + + /// Called when the current file part has ended in the form data + virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0; + } ; + + + cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks); + + /// Adds more data into the parser, as the request body is received + void Parse(const char * a_Data, int a_Size); + + /** Notifies that there's no more data incoming and the parser should finish its parsing. + Returns true if parsing successful + */ + bool Finish(void); + + /// Returns true if the headers suggest the request has form data parseable by this class + static bool HasFormData(const cHTTPRequest & a_Request); + +protected: + enum eKind + { + fpkURL, ///< The form has been transmitted as parameters to a GET request + fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded" + fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data" + }; + + /// The callbacks to call for incoming file data + cCallbacks & m_Callbacks; + + /// The kind of the parser (decided in the constructor, used in Parse() + eKind m_Kind; + + /// Buffer for the incoming data until it's parsed + AString m_IncomingData; + + /// True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false. + bool m_IsValid; + + /// The parser for the multipart data, if used + std::auto_ptr<cMultipartParser> m_MultipartParser; + + /// Name of the currently parsed part in multipart data + AString m_CurrentPartName; + + /// True if the currently parsed part in multipart data is a file + bool m_IsCurrentPartFile; + + /// Filename of the current parsed part in multipart data (for file uploads) + AString m_CurrentPartFileName; + + /// Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd + bool m_FileHasBeenAnnounced; + + + /// Sets up the object for parsing a fpkMultipart request + void BeginMultipart(const cHTTPRequest & a_Request); + + /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds) + void ParseFormUrlEncoded(void); + + // cMultipartParser::cCallbacks overrides: + virtual void OnPartStart (void) override; + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override; + virtual void OnPartData (const char * a_Data, int a_Size) override; + virtual void OnPartEnd (void) override; +} ; + + + + diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp new file mode 100644 index 000000000..ab23866e6 --- /dev/null +++ b/source/HTTPServer/HTTPMessage.cpp @@ -0,0 +1,279 @@ + +// HTTPMessage.cpp + +// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes + +#include "Globals.h" +#include "HTTPMessage.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPMessage: + +cHTTPMessage::cHTTPMessage(eKind a_Kind) : + m_Kind(a_Kind), + m_ContentLength(-1) +{ +} + + + + + +void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) +{ + AString Key = a_Key; + StrToLower(Key); + cNameValueMap::iterator itr = m_Headers.find(Key); + if (itr == m_Headers.end()) + { + m_Headers[Key] = a_Value; + } + else + { + // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2) + itr->second.append(", "); + itr->second.append(a_Value); + } + + // Special processing for well-known headers: + if (Key == "content-type") + { + m_ContentType = m_Headers[Key]; + } + else if (Key == "content-length") + { + m_ContentLength = atoi(m_Headers[Key].c_str()); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPRequest: + +cHTTPRequest::cHTTPRequest(void) : + super(mkRequest), + m_EnvelopeParser(*this), + m_IsValid(true), + m_UserData(NULL), + m_HasAuth(false) +{ +} + + + + + +int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size) +{ + if (!m_IsValid) + { + return -1; + } + + if (m_Method.empty()) + { + // The first line hasn't been processed yet + int res = ParseRequestLine(a_Data, a_Size); + if ((res < 0) || (res == a_Size)) + { + return res; + } + int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res); + if (res2 < 0) + { + m_IsValid = false; + return res2; + } + return res2 + res; + } + + if (m_EnvelopeParser.IsInHeaders()) + { + int res = m_EnvelopeParser.Parse(a_Data, a_Size); + if (res < 0) + { + m_IsValid = false; + } + return res; + } + return 0; +} + + + + + +AString cHTTPRequest::GetBareURL(void) const +{ + size_t idxQM = m_URL.find('?'); + if (idxQM != AString::npos) + { + return m_URL.substr(0, idxQM); + } + else + { + return m_URL; + } +} + + + + + +int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size) +{ + m_IncomingHeaderData.append(a_Data, a_Size); + size_t IdxEnd = m_IncomingHeaderData.size(); + + // Ignore the initial CRLFs (HTTP spec's "should") + size_t LineStart = 0; + while ( + (LineStart < IdxEnd) && + ( + (m_IncomingHeaderData[LineStart] == '\r') || + (m_IncomingHeaderData[LineStart] == '\n') + ) + ) + { + LineStart++; + } + if (LineStart >= IdxEnd) + { + m_IsValid = false; + return -1; + } + + int NumSpaces = 0; + size_t MethodEnd = 0; + size_t URLEnd = 0; + for (size_t i = LineStart; i < IdxEnd; i++) + { + switch (m_IncomingHeaderData[i]) + { + case ' ': + { + switch (NumSpaces) + { + case 0: + { + MethodEnd = i; + break; + } + case 1: + { + URLEnd = i; + break; + } + default: + { + // Too many spaces in the request + m_IsValid = false; + return -1; + } + } + NumSpaces += 1; + break; + } + case '\n': + { + if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7)) + { + // LF too early, without a CR, without two preceeding spaces or too soon after the second space + m_IsValid = false; + return -1; + } + // Check that there's HTTP/version at the end + if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0) + { + m_IsValid = false; + return -1; + } + m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart); + m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1); + return i + 1; + } + } // switch (m_IncomingHeaderData[i]) + } // for i - m_IncomingHeaderData[] + + // CRLF hasn't been encountered yet, consider all data consumed + return a_Size; +} + + + + + +void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + if ( + (NoCaseCompare(a_Key, "Authorization") == 0) && + (strncmp(a_Value.c_str(), "Basic ", 6) == 0) + ) + { + AString UserPass = Base64Decode(a_Value.substr(6)); + size_t idxCol = UserPass.find(':'); + if (idxCol != AString::npos) + { + m_AuthUsername = UserPass.substr(0, idxCol); + m_AuthPassword = UserPass.substr(idxCol + 1); + m_HasAuth = true; + } + } + AddHeader(a_Key, a_Value); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPResponse: + +cHTTPResponse::cHTTPResponse(void) : + super(mkResponse) +{ +} + + + + + +void cHTTPResponse::AppendToData(AString & a_DataStream) const +{ + a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: "); + a_DataStream.append(m_ContentType); + a_DataStream.append("\r\n"); + for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr) + { + if ((itr->first == "Content-Type") || (itr->first == "Content-Length")) + { + continue; + } + a_DataStream.append(itr->first); + a_DataStream.append(": "); + a_DataStream.append(itr->second); + a_DataStream.append("\r\n"); + } // for itr - m_Headers[] + a_DataStream.append("\r\n"); +} + + + + diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h new file mode 100644 index 000000000..f5284c535 --- /dev/null +++ b/source/HTTPServer/HTTPMessage.h @@ -0,0 +1,164 @@ + +// HTTPMessage.h + +// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes + + + + + +#pragma once + +#include "EnvelopeParser.h" + + + + + +class cHTTPMessage +{ +public: + enum + { + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + } ; + + enum eKind + { + mkRequest, + mkResponse, + } ; + + cHTTPMessage(eKind a_Kind); + + /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length + void AddHeader(const AString & a_Key, const AString & a_Value); + + void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; } + void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; } + + const AString & GetContentType (void) const { return m_ContentType; } + int GetContentLength(void) const { return m_ContentLength; } + +protected: + typedef std::map<AString, AString> cNameValueMap; + + eKind m_Kind; + + cNameValueMap m_Headers; + + /// Type of the content; parsed by AddHeader(), set directly by SetContentLength() + AString m_ContentType; + + /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() + int m_ContentLength; +} ; + + + + + +class cHTTPRequest : + public cHTTPMessage, + protected cEnvelopeParser::cCallbacks +{ + typedef cHTTPMessage super; + +public: + cHTTPRequest(void); + + /** Parses the request line and then headers from the received data. + Returns the number of bytes consumed or a negative number for error + */ + int ParseHeaders(const char * a_Data, int a_Size); + + /// Returns true if the request did contain a Content-Length header + bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); } + + /// Returns the method used in the request + const AString & GetMethod(void) const { return m_Method; } + + /// Returns the URL used in the request + const AString & GetURL(void) const { return m_URL; } + + /// Returns the URL used in the request, without any parameters + AString GetBareURL(void) const; + + /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)! + void SetUserData(void * a_UserData) { m_UserData = a_UserData; } + + /// Retrieves the UserData pointer that has been stored within this request. + void * GetUserData(void) const { return m_UserData; } + + /// Returns true if more data is expected for the request headers + bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); } + + /// Returns true if the request did present auth data that was understood by the parser + bool HasAuth(void) const { return m_HasAuth; } + + /// Returns the username that the request presented. Only valid if HasAuth() is true + const AString & GetAuthUsername(void) const { return m_AuthUsername; } + + /// Returns the password that the request presented. Only valid if HasAuth() is true + const AString & GetAuthPassword(void) const { return m_AuthPassword; } + +protected: + /// Parser for the envelope data + cEnvelopeParser m_EnvelopeParser; + + /// True if the data received so far is parsed successfully. When false, all further parsing is skipped + bool m_IsValid; + + /// Bufferred incoming data, while parsing for the request line + AString m_IncomingHeaderData; + + /// Method of the request (GET / PUT / POST / ...) + AString m_Method; + + /// Full URL of the request + AString m_URL; + + /// Data that the HTTPServer callbacks are allowed to store. + void * m_UserData; + + /// Set to true if the request contains auth data that was understood by the parser + bool m_HasAuth; + + /// The username used for auth + AString m_AuthUsername; + + /// The password used for auth + AString m_AuthPassword; + + + /** Parses the incoming data for the first line (RequestLine) + Returns the number of bytes consumed, or -1 for an error + */ + int ParseRequestLine(const char * a_Data, int a_Size); + + // cEnvelopeParser::cCallbacks overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; +} ; + + + + + +class cHTTPResponse : + public cHTTPMessage +{ + typedef cHTTPMessage super; + +public: + cHTTPResponse(void); + + /** Appends the response to the specified datastream - response line and headers. + The body will be sent later directly through cConnection::Send() + */ + void AppendToData(AString & a_DataStream) const; +} ; + + + + diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp new file mode 100644 index 000000000..f6f5b0f8b --- /dev/null +++ b/source/HTTPServer/HTTPServer.cpp @@ -0,0 +1,258 @@ + +// HTTPServer.cpp + +// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing + +#include "Globals.h" +#include "HTTPServer.h" +#include "HTTPMessage.h" +#include "HTTPConnection.h" +#include "HTTPFormParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +class cDebugCallbacks : + public cHTTPServer::cCallbacks, + protected cHTTPFormParser::cCallbacks +{ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + if (cHTTPFormParser::HasFormData(a_Request)) + { + a_Request.SetUserData(new cHTTPFormParser(a_Request, *this)); + } + } + + + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override + { + cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); + if (FormParser != NULL) + { + FormParser->Parse(a_Data, a_Size); + } + } + + + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override + { + cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData()); + if (FormParser != NULL) + { + if (FormParser->Finish()) + { + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send("<html><body><table border=1 cellspacing=0><tr><th>Name</th><th>Value</th></tr>\r\n"); + for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr) + { + a_Connection.Send(Printf("<tr><td valign=\"top\"><pre>%s</pre></td><td valign=\"top\"><pre>%s</pre></td></tr>\r\n", itr->first.c_str(), itr->second.c_str())); + } // for itr - FormParser[] + a_Connection.Send("</table></body></html>"); + return; + } + + // Parsing failed: + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Form parsing failed"); + return; + } + + // Test the auth failure and success: + if (a_Request.GetURL() == "/auth") + { + if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b")) + { + a_Connection.SendNeedAuth("MCServer WebAdmin"); + return; + } + } + + cHTTPResponse Resp; + Resp.SetContentType("text/plain"); + a_Connection.Send(Resp); + a_Connection.Send("Hello, world"); + } + + + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override + { + // TODO + } + + + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override + { + // TODO + } + + + virtual void OnFileEnd(cHTTPFormParser & a_Parser) override + { + // TODO + } + +} g_DebugCallbacks; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHTTPServer: + +cHTTPServer::cHTTPServer(void) : + m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), + m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), + m_Callbacks(NULL) +{ +} + + + + + +cHTTPServer::~cHTTPServer() +{ + Stop(); +} + + + + + +bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) +{ + bool HasAnyPort; + HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); + HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; + if (!HasAnyPort) + { + return false; + } + + return true; +} + + + + + +bool cHTTPServer::Start(cCallbacks & a_Callbacks) +{ + m_Callbacks = &a_Callbacks; + if (!m_ListenThreadIPv4.Start()) + { + return false; + } + if (!m_ListenThreadIPv6.Start()) + { + m_ListenThreadIPv4.Stop(); + return false; + } + return true; +} + + + + + +void cHTTPServer::Stop(void) +{ + m_ListenThreadIPv4.Stop(); + m_ListenThreadIPv6.Stop(); + + // Drop all current connections: + cCSLock Lock(m_CSConnections); + while (!m_Connections.empty()) + { + m_Connections.front()->Terminate(); + } // for itr - m_Connections[] +} + + + + + +void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) +{ + cHTTPConnection * Connection = new cHTTPConnection(*this); + m_SocketThreads.AddClient(a_Socket, Connection); + cCSLock Lock(m_CSConnections); + m_Connections.push_back(Connection); +} + + + + + +void cHTTPServer::CloseConnection(cHTTPConnection & a_Connection) +{ + m_SocketThreads.RemoveClient(&a_Connection); + cCSLock Lock(m_CSConnections); + for (cHTTPConnections::iterator itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (*itr == &a_Connection) + { + m_Connections.erase(itr); + break; + } + } + delete &a_Connection; +} + + + + + +void cHTTPServer::NotifyConnectionWrite(cHTTPConnection & a_Connection) +{ + m_SocketThreads.NotifyWrite(&a_Connection); +} + + + + + +void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + m_Callbacks->OnRequestBegun(a_Connection, a_Request); +} + + + + + +void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) +{ + m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size); +} + + + + + +void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + m_Callbacks->OnRequestFinished(a_Connection, a_Request); + a_Connection.AwaitNextRequest(); +} + + + + diff --git a/source/HTTPServer/HTTPServer.h b/source/HTTPServer/HTTPServer.h new file mode 100644 index 000000000..fea2a9029 --- /dev/null +++ b/source/HTTPServer/HTTPServer.h @@ -0,0 +1,101 @@ + +// HTTPServer.h + +// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing + + + + + +#pragma once + +#include "../OSSupport/ListenThread.h" +#include "../OSSupport/SocketThreads.h" +#include "../../iniFile/iniFile.h" + + + + + +// fwd: +class cHTTPMessage; +class cHTTPRequest; +class cHTTPResponse; +class cHTTPConnection; + +typedef std::vector<cHTTPConnection *> cHTTPConnections; + + + + + + +class cHTTPServer : + public cListenThread::cCallback +{ +public: + class cCallbacks + { + public: + /** Called when a new request arrives over a connection and its headers have been parsed. + The request body needn't have arrived yet. + */ + virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + + /// Called when another part of request body has arrived. + virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0; + + /// Called when the request body has been fully received in previous calls to OnRequestBody() + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0; + } ; + + cHTTPServer(void); + ~cHTTPServer(); + + /// Initializes the server on the specified ports + bool Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6); + + /// Starts the server and assigns the callbacks to use for incoming requests + bool Start(cCallbacks & a_Callbacks); + + /// Stops the server, drops all current connections + void Stop(void); + +protected: + friend class cHTTPConnection; + + cListenThread m_ListenThreadIPv4; + cListenThread m_ListenThreadIPv6; + + cSocketThreads m_SocketThreads; + + cCriticalSection m_CSConnections; + cHTTPConnections m_Connections; ///< All the connections that are currently being serviced + + /// The callbacks to call for various events + cCallbacks * m_Callbacks; + + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; + + /// Called by cHTTPConnection to close the connection (presumably due to an error) + void CloseConnection(cHTTPConnection & a_Connection); + + /// Called by cHTTPConnection to notify SocketThreads that there's data to be sent for the connection + void NotifyConnectionWrite(cHTTPConnection & a_Connection); + + /// Called by cHTTPConnection when it finishes parsing the request header + void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + /// Called by cHTTPConenction when it receives more data for the request body + void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size); + + /// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) + void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); +} ; + + + + + diff --git a/source/HTTPServer/MultipartParser.cpp b/source/HTTPServer/MultipartParser.cpp new file mode 100644 index 000000000..b49f6ec07 --- /dev/null +++ b/source/HTTPServer/MultipartParser.cpp @@ -0,0 +1,256 @@ + +// MultipartParser.cpp + +// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts + +#include "Globals.h" +#include "MultipartParser.h" +#include "NameValueParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// self-test: + +#if 0 + +class cMultipartParserTest : + public cMultipartParser::cCallbacks +{ +public: + cMultipartParserTest(void) + { + cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this); + const char Data[] = +"ThisIsIgnoredPrologue\r\n\ +--MyBoundaryString\r\n\ +\r\n\ +Body with confusing strings\r\n\ +--NotABoundary\r\n\ +--MyBoundaryStringWithPostfix\r\n\ +--\r\n\ +--MyBoundaryString\r\n\ +content-disposition: inline\r\n\ +\r\n\ +This is body\r\n\ +--MyBoundaryString\r\n\ +\r\n\ +Headerless body with trailing CRLF\r\n\ +\r\n\ +--MyBoundaryString--\r\n\ +ThisIsIgnoredEpilogue"; + printf("Multipart parsing test commencing.\n"); + Parser.Parse(Data, sizeof(Data) - 1); + // DEBUG: Check if the onscreen output corresponds with the data above + printf("Multipart parsing test finished\n"); + } + + virtual void OnPartStart(void) override + { + printf("Starting a new part\n"); + } + + + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override + { + printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str()); + } + + + virtual void OnPartData(const char * a_Data, int a_Size) override + { + printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data); + } + + + virtual void OnPartEnd(void) override + { + printf("Part end\n"); + } +} g_Test; + +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMultipartParser: + + +cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_IsValid(true), + m_EnvelopeParser(*this), + m_HasHadData(false) +{ + static AString s_Multipart = "multipart/"; + + // Check that the content type is multipart: + AString ContentType(a_ContentType); + if (strncmp(ContentType.c_str(), "multipart/", 10) != 0) + { + m_IsValid = false; + return; + } + size_t idxSC = ContentType.find(';', 10); + if (idxSC == AString::npos) + { + m_IsValid = false; + return; + } + + // Find the multipart boundary: + ContentType.erase(0, idxSC + 1); + cNameValueParser CTParser(ContentType.c_str(), ContentType.size()); + CTParser.Finish(); + if (!CTParser.IsValid()) + { + m_IsValid = false; + return; + } + m_Boundary = CTParser["boundary"]; + m_IsValid = !m_Boundary.empty(); + if (!m_IsValid) + { + return; + } + + // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body + m_EnvelopeParser.SetIsInHeaders(false); + + // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught + m_IncomingData.assign("\r\n"); + + /* + m_Boundary = AString("\r\n--") + m_Boundary + m_BoundaryEnd = m_Boundary + "--\r\n"; + m_Boundary = m_Boundary + "\r\n"; + */ +} + + + + + +void cMultipartParser::Parse(const char * a_Data, int a_Size) +{ + // Skip parsing if invalid + if (!m_IsValid) + { + return; + } + + // Append to buffer, then parse it: + m_IncomingData.append(a_Data, a_Size); + while (true) + { + if (m_EnvelopeParser.IsInHeaders()) + { + int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size()); + if (BytesConsumed < 0) + { + m_IsValid = false; + return; + } + if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders()) + { + // All the incoming data has been consumed and still waiting for more + return; + } + m_IncomingData.erase(0, BytesConsumed); + } + + // Search for boundary / boundary end: + size_t idxBoundary = m_IncomingData.find("\r\n--"); + if (idxBoundary == AString::npos) + { + // Boundary string start not present, present as much data to the part callback as possible + if (m_IncomingData.size() > m_Boundary.size() + 8) + { + size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; + m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); + m_IncomingData.erase(0, BytesToReport); + } + return; + } + if (idxBoundary > 0) + { + m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary); + m_IncomingData.erase(0, idxBoundary); + } + idxBoundary = 4; + size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary); + if (LineEnd == AString::npos) + { + // Not a complete line yet, present as much data to the part callback as possible + if (m_IncomingData.size() > m_Boundary.size() + 8) + { + size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8; + m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport); + m_IncomingData.erase(0, BytesToReport); + } + return; + } + if ( + (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary + (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end + ) + { + // Got a line, but it's not a boundary, report it as data: + m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd); + m_IncomingData.erase(0, LineEnd); + continue; + } + + if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0) + { + // Boundary or BoundaryEnd found: + m_Callbacks.OnPartEnd(); + size_t idxSlash = idxBoundary + m_Boundary.size(); + if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-')) + { + // This was the last part + m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4); + m_IncomingData.clear(); + return; + } + m_Callbacks.OnPartStart(); + m_IncomingData.erase(0, LineEnd + 2); + + // Keep parsing for the headers that may have come with this data: + m_EnvelopeParser.Reset(); + continue; + } + + // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines + m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd); + m_IncomingData.erase(0, LineEnd); + } // while (true) +} + + + + + +void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value) +{ + m_Callbacks.OnPartHeader(a_Key, a_Value); +} + + + + diff --git a/source/HTTPServer/MultipartParser.h b/source/HTTPServer/MultipartParser.h new file mode 100644 index 000000000..d853929ed --- /dev/null +++ b/source/HTTPServer/MultipartParser.h @@ -0,0 +1,76 @@ + +// MultipartParser.h + +// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts + + + + + +#pragma once + +#include "EnvelopeParser.h" + + + + + +class cMultipartParser : + protected cEnvelopeParser::cCallbacks +{ +public: + class cCallbacks + { + public: + /// Called when a new part starts + virtual void OnPartStart(void) = 0; + + /// Called when a complete header line is received for a part + virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0; + + /// Called when body for a part is received + virtual void OnPartData(const char * a_Data, int a_Size) = 0; + + /// Called when the current part ends + virtual void OnPartEnd(void) = 0; + } ; + + /// Creates the parser, expects to find the boundary in a_ContentType + cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks); + + /// Parses more incoming data + void Parse(const char * a_Data, int a_Size); + +protected: + /// The callbacks to call for various parsing events + cCallbacks & m_Callbacks; + + /// True if the data parsed so far is valid; if false, further parsing is skipped + bool m_IsValid; + + /// Parser for each part's envelope + cEnvelopeParser m_EnvelopeParser; + + /// Buffer for the incoming data until it is parsed + AString m_IncomingData; + + /// The boundary, excluding both the initial "--" and the terminating CRLF + AString m_Boundary; + + /// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting. + bool m_HasHadData; + + + /// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size + void ParseLine(const char * a_Data, int a_Size); + + /// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size + void ParseHeaderLine(const char * a_Data, int a_Size); + + // cEnvelopeParser overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override; +} ; + + + + diff --git a/source/HTTPServer/NameValueParser.cpp b/source/HTTPServer/NameValueParser.cpp new file mode 100644 index 000000000..a27f07d19 --- /dev/null +++ b/source/HTTPServer/NameValueParser.cpp @@ -0,0 +1,412 @@ + +// NameValueParser.cpp + +// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap + +#include "Globals.h" +#include "NameValueParser.h" + + + + + + +// DEBUG: Self-test + +#if 0 + +class cNameValueParserTest +{ +public: + cNameValueParserTest(void) + { + const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\""; + + // Now try parsing char-by-char, to debug transitions across datachunk boundaries: + cNameValueParser Parser2; + for (int i = 0; i < sizeof(Data) - 1; i++) + { + Parser2.Parse(Data + i, 1); + } + Parser2.Finish(); + + // Parse as a single chunk of data: + cNameValueParser Parser(Data, sizeof(Data) - 1); + + // Use the debugger to inspect the Parser variable + + // Check that the two parsers have the same content: + for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) + { + ASSERT(Parser2[itr->first] == itr->second); + } // for itr - Parser[] + + // Try parsing in 2-char chunks: + cNameValueParser Parser3; + for (int i = 0; i < sizeof(Data) - 2; i += 2) + { + Parser3.Parse(Data + i, 2); + } + if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char + { + Parser3.Parse(Data + sizeof(Data) - 2, 1); + } + Parser3.Finish(); + + // Check that the third parser has the same content: + for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr) + { + ASSERT(Parser3[itr->first] == itr->second); + } // for itr - Parser[] + + printf("cNameValueParserTest done"); + } +} g_Test; + +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNameValueParser: + +cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) : + m_State(psKeySpace), + m_AllowsKeyOnly(a_AllowsKeyOnly) +{ +} + + + + + +cNameValueParser::cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly) : + m_State(psKeySpace), + m_AllowsKeyOnly(a_AllowsKeyOnly) +{ + Parse(a_Data, a_Size); +} + + + + + +void cNameValueParser::Parse(const char * a_Data, int a_Size) +{ + ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong! + + if ((m_State == psInvalid) || (m_State == psFinished)) + { + return; + } + int Last = 0; + for (int i = 0; i < a_Size;) + { + switch (m_State) + { + case psKeySpace: + { + // Skip whitespace until a non-whitespace is found, then start the key: + while ((i < a_Size) && (a_Data[i] <= ' ')) + { + i++; + } + if ((i < a_Size) && (a_Data[i] > ' ')) + { + m_State = psKey; + Last = i; + } + break; + } + + case psKey: + { + // Read the key until whitespace or an equal sign: + while (i < a_Size) + { + if (a_Data[i] == '=') + { + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + m_State = psEqual; + break; + } + else if (a_Data[i] <= ' ') + { + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + m_State = psEqualSpace; + break; + } + else if (a_Data[i] == ';') + { + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + m_CurrentKey.append(a_Data + Last, i - Last); + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if ((a_Data[i] == '\"') || (a_Data[i] == '\'')) + { + m_State = psInvalid; + return; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + // Still the key, ran out of data to parse, store the part of the key parsed so far: + m_CurrentKey.append(a_Data + Last, a_Size - Last); + return; + } + break; + } + + case psEqualSpace: + { + // The space before the expected equal sign; the current key is already assigned + while (i < a_Size) + { + if (a_Data[i] == '=') + { + m_State = psEqual; + i++; + Last = i; + break; + } + else if (a_Data[i] == ';') + { + // Key-only + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if (a_Data[i] > ' ') + { + m_State = psInvalid; + return; + } + i++; + } // while (i < a_Size) + break; + } // case psEqualSpace + + case psEqual: + { + // just parsed the equal-sign + while (i < a_Size) + { + if (a_Data[i] == ';') + { + if (!m_AllowsKeyOnly) + { + m_State = psInvalid; + return; + } + i++; + Last = i; + (*this)[m_CurrentKey] = ""; + m_CurrentKey.clear(); + m_State = psKeySpace; + break; + } + else if (a_Data[i] == '\"') + { + i++; + Last = i; + m_State = psValueInDQuotes; + break; + } + else if (a_Data[i] == '\'') + { + i++; + Last = i; + m_State = psValueInSQuotes; + break; + } + else + { + m_CurrentValue.push_back(a_Data[i]); + i++; + Last = i; + m_State = psValueRaw; + break; + } + i++; + } // while (i < a_Size) + break; + } // case psEqual + + case psValueInDQuotes: + { + while (i < a_Size) + { + if (a_Data[i] == '\"') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psAfterValue; + i++; + Last = i; + break; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueInDQuotes + + case psValueInSQuotes: + { + while (i < a_Size) + { + if (a_Data[i] == '\'') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psAfterValue; + i++; + Last = i; + break; + } + i++; + } // while (i < a_Size) + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueInSQuotes + + case psValueRaw: + { + while (i < a_Size) + { + if (a_Data[i] == ';') + { + m_CurrentValue.append(a_Data + Last, i - Last); + (*this)[m_CurrentKey] = m_CurrentValue; + m_CurrentKey.clear(); + m_CurrentValue.clear(); + m_State = psKeySpace; + i++; + Last = i; + break; + } + i++; + } + if (i == a_Size) + { + m_CurrentValue.append(a_Data + Last, a_Size - Last); + } + break; + } // case psValueRaw + + case psAfterValue: + { + // Between the closing DQuote or SQuote and the terminating semicolon + while (i < a_Size) + { + if (a_Data[i] == ';') + { + m_State = psKeySpace; + i++; + Last = i; + break; + } + else if (a_Data[i] < ' ') + { + i++; + continue; + } + m_State = psInvalid; + return; + } // while (i < a_Size) + break; + } + } // switch (m_State) + } // for i - a_Data[] +} + + + + + +bool cNameValueParser::Finish(void) +{ + switch (m_State) + { + case psInvalid: + { + return false; + } + case psFinished: + { + return true; + } + case psKey: + case psEqualSpace: + case psEqual: + { + if ((m_AllowsKeyOnly) && !m_CurrentKey.empty()) + { + (*this)[m_CurrentKey] = ""; + m_State = psFinished; + return true; + } + m_State = psInvalid; + return false; + } + case psValueRaw: + { + (*this)[m_CurrentKey] = m_CurrentValue; + m_State = psFinished; + return true; + } + case psValueInDQuotes: + case psValueInSQuotes: + { + // Missing the terminating quotes, this is an error + m_State = psInvalid; + return false; + } + case psKeySpace: + case psAfterValue: + { + m_State = psFinished; + return true; + } + } + ASSERT(!"Unhandled parser state!"); + return false; +} + + + + diff --git a/source/HTTPServer/NameValueParser.h b/source/HTTPServer/NameValueParser.h new file mode 100644 index 000000000..07dc0b942 --- /dev/null +++ b/source/HTTPServer/NameValueParser.h @@ -0,0 +1,70 @@ + +// NameValueParser.h + +// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap + + + + + +#pragma once + + + + + +class cNameValueParser : + public std::map<AString, AString> +{ +public: + /// Creates an empty parser + cNameValueParser(bool a_AllowsKeyOnly = true); + + /// Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later + cNameValueParser(const char * a_Data, int a_Size, bool a_AllowsKeyOnly = true); + + /// Parses the data given + void Parse(const char * a_Data, int a_Size); + + /// Notifies the parser that no more data will be coming. Returns true if the parser state is valid + bool Finish(void); + + /// Returns true if the data parsed so far was valid + bool IsValid(void) const { return (m_State != psInvalid); } + + /// Returns true if the parser expects no more data + bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); } + +protected: + enum eState + { + psKeySpace, ///< Parsing the space in front of the next key + psKey, ///< Currently adding more chars to the key in m_CurrentKey + psEqualSpace, ///< Space after m_CurrentKey + psEqual, ///< Just parsed the = sign after a name + psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign + psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign + psValueRaw, ///< Just parsed a raw value without a quote + psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end + psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped + psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data + } ; + + /// The current state of the parser + eState m_State; + + /// If true, the parser will accept keys without an equal sign and the value + bool m_AllowsKeyOnly; + + /// Buffer for the current Key + AString m_CurrentKey; + + /// Buffer for the current Value; + AString m_CurrentValue; + + +} ; + + + + diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp index 9d38e6f3a..13f5293b9 100644 --- a/source/Items/ItemHandler.cpp +++ b/source/Items/ItemHandler.cpp @@ -247,7 +247,7 @@ void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); - if (a_Player->GetGameMode() == gmSurvival) + if (a_Player->IsGameModeSurvival()) { if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) { diff --git a/source/Log.cpp b/source/Log.cpp index c8937c380..fc19595db 100644 --- a/source/Log.cpp +++ b/source/Log.cpp @@ -5,7 +5,6 @@ #include <fstream> #include <ctime> -#include "OSSupport/MakeDir.h" #include "OSSupport/IsThread.h" #if defined(ANDROID_NDK) @@ -24,9 +23,9 @@ cLog::cLog(const AString & a_FileName ) s_Log = this; // create logs directory - cMakeDir::MakeDir("logs"); + cFile::CreateFolder(FILE_IO_PREFIX + AString("logs")); - OpenLog( (FILE_IO_PREFIX + std::string("logs/") + a_FileName).c_str() ); + OpenLog((FILE_IO_PREFIX + AString("logs/") + a_FileName).c_str() ); } @@ -45,8 +44,10 @@ cLog::~cLog() cLog* cLog::GetInstance() { - if(s_Log) + if (s_Log != NULL) + { return s_Log; + } new cLog("log.txt"); return s_Log; diff --git a/source/MCLogger.cpp b/source/MCLogger.cpp index 2870d8ba1..4f3e5dc0f 100644 --- a/source/MCLogger.cpp +++ b/source/MCLogger.cpp @@ -37,24 +37,7 @@ cMCLogger::cMCLogger(void) { AString FileName; Printf(FileName, "LOG_%d.txt", (int)time(NULL)); - m_Log = new cLog(FileName); - m_Log->Log("--- Started Log ---\n"); - - s_MCLogger = this; - - #ifdef _WIN32 - // See whether we are writing to a console the default console attrib: - g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); - if (g_ShouldColorOutput) - { - CONSOLE_SCREEN_BUFFER_INFO sbi; - GetConsoleScreenBufferInfo(g_Console, &sbi); - g_DefaultConsoleAttrib = sbi.wAttributes; - } - #elif defined (__linux) && !defined(ANDROID_NDK) - g_ShouldColorOutput = isatty(fileno(stdout)); - // TODO: Check if the terminal supports colors, somehow? - #endif + InitLog(FileName); } @@ -63,7 +46,7 @@ cMCLogger::cMCLogger(void) cMCLogger::cMCLogger(const AString & a_FileName) { - m_Log = new cLog(a_FileName); + InitLog(a_FileName); } @@ -84,6 +67,32 @@ cMCLogger::~cMCLogger() +void cMCLogger::InitLog(const AString & a_FileName) +{ + m_Log = new cLog(a_FileName); + m_Log->Log("--- Started Log ---\n"); + + s_MCLogger = this; + + #ifdef _WIN32 + // See whether we are writing to a console the default console attrib: + g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); + if (g_ShouldColorOutput) + { + CONSOLE_SCREEN_BUFFER_INFO sbi; + GetConsoleScreenBufferInfo(g_Console, &sbi); + g_DefaultConsoleAttrib = sbi.wAttributes; + } + #elif defined (__linux) && !defined(ANDROID_NDK) + g_ShouldColorOutput = isatty(fileno(stdout)); + // TODO: Check if the terminal supports colors, somehow? + #endif +} + + + + + void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) { switch( a_LogType ) diff --git a/source/MCLogger.h b/source/MCLogger.h index 6f7d34e66..c949a4cdf 100644 --- a/source/MCLogger.h +++ b/source/MCLogger.h @@ -13,8 +13,12 @@ class cLog; class cMCLogger // tolua_export { // tolua_export public: // tolua_export + /// Creates a logger with the default filename, "logs/LOG_<timestamp>.log" cMCLogger(void); + + /// Creates a logger with the specified filename inside "logs" folder cMCLogger(const AString & a_FileName); // tolua_export + ~cMCLogger(); // tolua_export void Log(const char* a_Format, va_list a_ArgList); @@ -34,17 +38,25 @@ private: csError, } ; + cCriticalSection m_CriticalSection; + cLog * m_Log; + static cMCLogger * s_MCLogger; + + /// Sets the specified color scheme in the terminal (TODO: if coloring available) void SetColor(eColorScheme a_Scheme); /// Resets the color back to whatever is the default in the terminal void ResetColor(void); - cCriticalSection m_CriticalSection; - cLog * m_Log; - static cMCLogger * s_MCLogger; + /// Common initialization for all constructors, creates a logfile with the specified name and assigns s_MCLogger to this + void InitLog(const AString & a_FileName); }; // tolua_export + + + + extern void LOG(const char* a_Format, ...); extern void LOGINFO(const char* a_Format, ...); extern void LOGWARN(const char* a_Format, ...); diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index a80e186eb..e6605ddb0 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -896,6 +896,70 @@ tolua_lerror: +class cLuaWorldTask : + public cWorld::cTask +{ +public: + cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : + 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_QueueTask(lua_State * tolua_S) +{ + // Binding for cWorld::QueueTask + // Params: function + + // 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"); + } + + self->QueueTask(new cLuaWorldTask(*Plugin, FnRef)); + return 0; +} + + + + + static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) { cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); @@ -1235,7 +1299,10 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) static int tolua_cPluginManager_BindCommand(lua_State * L) { - // Function signature: cPluginManager:BindCommand(Command, Permission, Function, HelpString) + /* Function signatures: + cPluginManager:BindCommand(Command, Permission, Function, HelpString) + cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param + */ cPluginLua * Plugin = GetLuaPlugin(L); if (Plugin == NULL) { @@ -1244,26 +1311,30 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) // Read the arguments to this API call: tolua_Error tolua_err; + int idx = 1; + if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) + { + idx++; + } if ( - !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) || - !tolua_iscppstring(L, 2, 0, &tolua_err) || - !tolua_iscppstring(L, 3, 0, &tolua_err) || - !tolua_iscppstring(L, 5, 0, &tolua_err) || - !tolua_isnoobj (L, 6, &tolua_err) + !tolua_iscppstring(L, idx, 0, &tolua_err) || + !tolua_iscppstring(L, idx + 1, 0, &tolua_err) || + !tolua_iscppstring(L, idx + 3, 0, &tolua_err) || + !tolua_isnoobj (L, idx + 4, &tolua_err) ) { tolua_error(L, "#ferror in function 'BindCommand'.", &tolua_err); return 0; } - if (!lua_isfunction(L, 4)) + if (!lua_isfunction(L, idx + 2)) { luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted."); return 0; } - cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0); - AString Command (tolua_tocppstring(L, 2, "")); - AString Permission(tolua_tocppstring(L, 3, "")); - AString HelpString(tolua_tocppstring(L, 5, "")); + cPluginManager * self = cPluginManager::Get(); + AString Command (tolua_tocppstring(L, idx, "")); + AString Permission(tolua_tocppstring(L, idx + 1, "")); + AString HelpString(tolua_tocppstring(L, idx + 3, "")); // Store the function reference: lua_pop(L, 1); // Pop the help string off the stack @@ -1290,37 +1361,42 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) { - // Function signature: cPluginManager:BindConsoleCommand(Command, Function, HelpString) + /* Function signatures: + cPluginManager:BindConsoleCommand(Command, Function, HelpString) + cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param + */ // Get the plugin identification out of LuaState: - lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); - if (!lua_islightuserdata(L, -1)) + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == NULL) { - LOGERROR("cPluginManager:BindConsoleCommand() cannot get plugin instance, what have you done to my Lua state? Command-binding aborted."); + return 0; } - cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1); - lua_pop(L, 1); // Read the arguments to this API call: tolua_Error tolua_err; + int idx = 1; + if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err)) + { + idx++; + } if ( - !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) || // self - !tolua_iscppstring(L, 2, 0, &tolua_err) || // Command - !tolua_iscppstring(L, 4, 0, &tolua_err) || // HelpString - !tolua_isnoobj (L, 5, &tolua_err) + !tolua_iscppstring(L, idx, 0, &tolua_err) || // Command + !tolua_iscppstring(L, idx + 2, 0, &tolua_err) || // HelpString + !tolua_isnoobj (L, idx + 3, &tolua_err) ) { tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err); return 0; } - if (!lua_isfunction(L, 3)) + if (!lua_isfunction(L, idx + 1)) { luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted."); return 0; } - cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0); - AString Command (tolua_tocppstring(L, 2, "")); - AString HelpString(tolua_tocppstring(L, 4, "")); + cPluginManager * self = cPluginManager::Get(); + AString Command (tolua_tocppstring(L, idx, "")); + AString HelpString(tolua_tocppstring(L, idx + 2, "")); // Store the function reference: lua_pop(L, 1); // Pop the help string off the stack @@ -1489,14 +1565,16 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) tolua_Error tolua_err; tolua_err.array = 0; - tolua_err.index = 0; - tolua_err.type = 0; + tolua_err.index = 3; + tolua_err.type = "function"; std::string Title = ""; int Reference = LUA_REFNIL; - if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) && - lua_isfunction( tolua_S, 3 ) ) + if ( + tolua_isstring(tolua_S, 2, 0, &tolua_err ) && + lua_isfunction(tolua_S, 3 ) + ) { Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); Title = ((std::string) tolua_tocppstring(tolua_S,2,0)); @@ -2018,6 +2096,8 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); + tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); @@ -2028,12 +2108,12 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPluginManager"); + tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); - tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlayer"); diff --git a/source/Mobs/IronGolem.h b/source/Mobs/IronGolem.h index d253aefac..d49ff4cab 100644 --- a/source/Mobs/IronGolem.h +++ b/source/Mobs/IronGolem.h @@ -1,7 +1,7 @@ #pragma once -#include "AggressiveMonster.h" +#include "PassiveAggressiveMonster.h" diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp index 7d553758e..c72b4831b 100644 --- a/source/Mobs/Magmacube.cpp +++ b/source/Mobs/Magmacube.cpp @@ -1,7 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "MagmaCube.h" +#include "Magmacube.h" diff --git a/source/Mobs/Zombiepigman.cpp b/source/Mobs/Zombiepigman.cpp index 1aea006a6..1e31a72d9 100644 --- a/source/Mobs/Zombiepigman.cpp +++ b/source/Mobs/Zombiepigman.cpp @@ -1,7 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "ZombiePigman.h" +#include "Zombiepigman.h" #include "../World.h" diff --git a/source/OSSupport/File.cpp b/source/OSSupport/File.cpp index cc0916711..d2eea498a 100644 --- a/source/OSSupport/File.cpp +++ b/source/OSSupport/File.cpp @@ -6,13 +6,12 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "File.h" +#include <fstream> - -/// Simple constructor - creates an unopened file object, use Open() to open / create a real file cFile::cFile(void) : #ifdef USE_STDIO_FILE m_File(NULL) @@ -27,7 +26,6 @@ cFile::cFile(void) : -/// Constructs and opens / creates the file specified, use IsOpen() to check for success cFile::cFile(const AString & iFileName, eMode iMode) : #ifdef USE_STDIO_FILE m_File(NULL) @@ -42,7 +40,6 @@ cFile::cFile(const AString & iFileName, eMode iMode) : -/// Auto-closes the file, if open cFile::~cFile() { if (IsOpen()) @@ -134,7 +131,6 @@ bool cFile::IsEOF(void) const -/// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open int cFile::Read (void * iBuffer, int iNumBytes) { ASSERT(IsOpen()); @@ -151,7 +147,6 @@ int cFile::Read (void * iBuffer, int iNumBytes) -/// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open int cFile::Write(const void * iBuffer, int iNumBytes) { ASSERT(IsOpen()); @@ -169,7 +164,6 @@ int cFile::Write(const void * iBuffer, int iNumBytes) -/// Seeks to iPosition bytes from file start, returns old position or -1 for failure int cFile::Seek (int iPosition) { ASSERT(IsOpen()); @@ -191,7 +185,6 @@ int cFile::Seek (int iPosition) -/// Returns the current position (bytes from file start) int cFile::Tell (void) const { ASSERT(IsOpen()); @@ -208,7 +201,6 @@ int cFile::Tell (void) const -/// Returns the size of file, in bytes, or -1 for failure; asserts if not open int cFile::GetSize(void) const { ASSERT(IsOpen()); @@ -287,6 +279,87 @@ bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName +bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName) +{ + #ifdef _WIN32 + return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0); + #else + // Other OSs don't have a direct CopyFile equivalent, do it the harder way: + std::ifstream src(a_SrcFileName.c_str(), std::ios::binary); + std::ofstream dst(a_DstFileName.c_str(), std::ios::binary); + if (dst.good()) + { + dst << src.rdbuf(); + return true; + } + else + { + return false; + } + #endif +} + + + + + +bool cFile::IsFolder(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode)); + #endif +} + + + + + +bool cFile::IsFile(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode)); + #endif +} + + + + + +int cFile::GetSize(const AString & a_FileName) +{ + struct stat st; + if (stat(a_FileName.c_str(), &st) == 0) + { + return st.st_size; + } + return -1; +} + + + + + +bool cFile::CreateFolder(const AString & a_FolderPath) +{ + #ifdef _WIN32 + return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0); + #else + return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0); + #endif +} + + + + + int cFile::Printf(const char * a_Fmt, ...) { AString buf; diff --git a/source/OSSupport/File.h b/source/OSSupport/File.h index 8a057afa8..cfb3a2019 100644 --- a/source/OSSupport/File.h +++ b/source/OSSupport/File.h @@ -41,9 +41,14 @@ Usage: +// tolua_begin + class cFile { public: + + // tolua_end + #ifdef _WIN32 static const char PathSeparator = '\\'; #else @@ -90,14 +95,33 @@ public: /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error int ReadRestOfFile(AString & a_Contents); + // tolua_begin + /// Returns true if the file specified exists static bool Exists(const AString & a_FileName); /// Deletes a file, returns true if successful static bool Delete(const AString & a_FileName); - /// Renames a file, returns true if successful. May fail if dest already exists (libc-dependant)! - static bool Rename(const AString & a_OrigFileName, const AString & a_NewFileName); + /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! + static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); + + /// Copies a file, returns true if successful. + static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); + + /// Returns true if the specified path is a folder + static bool IsFolder(const AString & a_Path); + + /// Returns true if the specified path is a regular file + static bool IsFile(const AString & a_Path); + + /// Returns the size of the file, or a negative number on error + static int GetSize(const AString & a_FileName); + + /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute + static bool CreateFolder(const AString & a_FolderPath); + + // tolua_end int Printf(const char * a_Fmt, ...); @@ -107,7 +131,7 @@ private: #else HANDLE m_File; #endif -} ; +} ; // tolua_export diff --git a/source/OSSupport/IsThread.cpp b/source/OSSupport/IsThread.cpp index d5fbfcf19..e1ef84c17 100644 --- a/source/OSSupport/IsThread.cpp +++ b/source/OSSupport/IsThread.cpp @@ -53,11 +53,7 @@ static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) cIsThread::cIsThread(const AString & iThreadName) : m_ThreadName(iThreadName), m_ShouldTerminate(false), - #ifdef _WIN32 - m_Handle(NULL) - #else // _WIN32 - m_HasStarted(false) - #endif // else _WIN32 + m_Handle(NULL) { } @@ -77,9 +73,9 @@ cIsThread::~cIsThread() bool cIsThread::Start(void) { + ASSERT(m_Handle == NULL); // Has already started one thread? + #ifdef _WIN32 - ASSERT(m_Handle == NULL); // Has already started one thread? - // Create the thread suspended, so that the mHandle variable is valid in the thread procedure DWORD ThreadID = 0; m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); @@ -99,14 +95,11 @@ bool cIsThread::Start(void) #endif // _DEBUG and _MSC_VER #else // _WIN32 - ASSERT(!m_HasStarted); - if (pthread_create(&m_Handle, NULL, thrExecute, this)) { LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); return false; } - m_HasStarted = true; #endif // else _WIN32 return true; @@ -158,7 +151,6 @@ bool cIsThread::Wait(void) LOGD("Thread %s finished", m_ThreadName.c_str()); #endif // LOGD - m_HasStarted = false; return (res == 0); #endif // else _WIN32 } diff --git a/source/OSSupport/IsThread.h b/source/OSSupport/IsThread.h index 9b7f0b73e..2ea8bf6f9 100644 --- a/source/OSSupport/IsThread.h +++ b/source/OSSupport/IsThread.h @@ -48,7 +48,7 @@ public: /// Returns the OS-dependent thread ID for the caller's thread static unsigned long GetCurrentID(void); -private: +protected: AString m_ThreadName; #ifdef _WIN32 @@ -66,7 +66,6 @@ private: #else // _WIN32 pthread_t m_Handle; - bool m_HasStarted; static void * thrExecute(void * a_Param) { diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp index 0890aabc8..ba3198764 100644 --- a/source/OSSupport/ListenThread.cpp +++ b/source/OSSupport/ListenThread.cpp @@ -224,7 +224,10 @@ void cListenThread::Execute(void) if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) { cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); - m_Callback.OnConnectionAccepted(Client); + if (Client.IsValid()) + { + m_Callback.OnConnectionAccepted(Client); + } } } // for itr - m_Sockets[] } // while (!m_ShouldTerminate) diff --git a/source/OSSupport/MakeDir.cpp b/source/OSSupport/MakeDir.cpp deleted file mode 100644 index 10ccfe9ec..000000000 --- a/source/OSSupport/MakeDir.cpp +++ /dev/null @@ -1,25 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "MakeDir.h" - - - - - -void cMakeDir::MakeDir(const AString & a_Directory) -{ -#ifdef _WIN32 - SECURITY_ATTRIBUTES Attrib; - Attrib.nLength = sizeof(SECURITY_ATTRIBUTES); - Attrib.lpSecurityDescriptor = NULL; - Attrib.bInheritHandle = false; - ::CreateDirectory( (FILE_IO_PREFIX + a_Directory).c_str(), &Attrib); -#else - mkdir( (FILE_IO_PREFIX + a_Directory).c_str(), S_IRWXU | S_IRWXG | S_IRWXO); -#endif -} - - - - diff --git a/source/OSSupport/MakeDir.h b/source/OSSupport/MakeDir.h deleted file mode 100644 index e66cf1071..000000000 --- a/source/OSSupport/MakeDir.h +++ /dev/null @@ -1,16 +0,0 @@ - -#pragma once - - - - - -class cMakeDir -{ -public: - static void MakeDir(const AString & a_Directory); -}; - - - - diff --git a/source/Plugin.cpp b/source/Plugin.cpp index 229b997cd..98ccfb88c 100644 --- a/source/Plugin.cpp +++ b/source/Plugin.cpp @@ -2,10 +2,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Plugin.h" -#include "Entities/Player.h" -#include "World.h" -#include "CommandOutput.h" -#include "Mobs/Monster.h" @@ -32,7 +28,11 @@ cPlugin::~cPlugin() -AString cPlugin::GetLocalDirectory(void) const +AString cPlugin::GetLocalFolder(void) const { return std::string("Plugins/") + m_Directory; -}
\ No newline at end of file +} + + + + diff --git a/source/Plugin.h b/source/Plugin.h index be803bab2..06e5819df 100644 --- a/source/Plugin.h +++ b/source/Plugin.h @@ -121,7 +121,8 @@ public: void SetVersion(int a_Version) { m_Version = a_Version; } const AString & GetDirectory(void) const {return m_Directory; } - AString GetLocalDirectory(void) const; + AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead + AString GetLocalFolder(void) const; // tolua_end diff --git a/source/PluginLua.cpp b/source/PluginLua.cpp index 81a536838..03aefb098 100644 --- a/source/PluginLua.cpp +++ b/source/PluginLua.cpp @@ -81,6 +81,9 @@ bool cPluginLua::Initialize(void) lua_setglobal(m_LuaState, LUA_PLUGIN_INSTANCE_VAR_NAME); lua_pushstring(m_LuaState, GetName().c_str()); lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME); + + tolua_pushusertype(m_LuaState, this, "cPluginLua"); + lua_setglobal(m_LuaState, "g_Plugin"); } std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/"; diff --git a/source/PluginLua.h b/source/PluginLua.h index fee9c4986..908466966 100644 --- a/source/PluginLua.h +++ b/source/PluginLua.h @@ -137,6 +137,43 @@ public: Returns true if the hook was added successfully. */ bool AddHookRef(int a_HookType, int a_FnRefIdx); + + // The following templates allow calls to arbitrary Lua functions residing in the plugin: + + /// Call a Lua function with 0 args + template <typename FnT> bool Call(FnT a_Fn) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn); + } + + /// Call a Lua function with 1 arg + template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0); + } + + /// Call a Lua function with 2 args + template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); + } + + /// Call a Lua function with 3 args + template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); + } + + /// Call a Lua function with 4 args + template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); + } protected: /// Maps command name into Lua function reference diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp index 93ee71926..a557bdc03 100644 --- a/source/PluginManager.cpp +++ b/source/PluginManager.cpp @@ -75,16 +75,16 @@ void cPluginManager::FindPlugins(void) AStringList Files = GetDirectoryContents(PluginsPath.c_str()); for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) { - if (itr->rfind(".") != AString::npos) + if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) { - // Ignore files, we only want directories + // We only want folders, and don't want "." or ".." continue; } // Add plugin name/directory to the list - if (m_Plugins.find( *itr ) == m_Plugins.end()) + if (m_Plugins.find(*itr) == m_Plugins.end()) { - m_Plugins[ *itr ] = NULL; + m_Plugins[*itr] = NULL; } } } @@ -95,7 +95,7 @@ void cPluginManager::FindPlugins(void) void cPluginManager::ReloadPluginsNow(void) { - LOG("Loading plugins"); + LOG("-- Loading Plugins --"); m_bReloadPlugins = false; UnloadPluginsNow(); @@ -135,11 +135,15 @@ void cPluginManager::ReloadPluginsNow(void) if (GetNumPlugins() == 0) { - LOG("No plugins loaded"); + LOG("-- No Plugins Loaded --"); + } + else if ((GetNumPlugins() > 1) || (GetNumPlugins() == 0)) + { + LOG("-- Loaded %i Plugins --", GetNumPlugins()); } else { - LOG("Loaded %i plugin(s)", GetNumPlugins()); + LOG("-- Loaded 1 Plugin --"); } } diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp index 62863cd48..fb7315468 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -465,15 +465,18 @@ void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_Bloc WriteDouble (a_BlockZ); WriteFloat (a_Radius); WriteInt (a_BlocksAffected.size()); + int BlockX = (int)a_BlockX; + int BlockY = (int)a_BlockY; + int BlockZ = (int)a_BlockZ; for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr) { - WriteByte ((Byte)(itr->x - a_BlockX)); - WriteByte ((Byte)(itr->y - a_BlockY)); - WriteByte ((Byte)(itr->z - a_BlockZ)); + WriteByte((Byte)(itr->x - BlockX)); + WriteByte((Byte)(itr->y - BlockY)); + WriteByte((Byte)(itr->z - BlockZ)); } - WriteFloat ((float)a_PlayerMotion.x); - WriteFloat ((float)a_PlayerMotion.y); - WriteFloat ((float)a_PlayerMotion.z); + WriteFloat((float)a_PlayerMotion.x); + WriteFloat((float)a_PlayerMotion.y); + WriteFloat((float)a_PlayerMotion.z); Flush(); } @@ -1701,7 +1704,7 @@ void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity) WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled? } } - else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)); + else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)) { WriteByte(0x10); WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow? diff --git a/source/Protocol/Protocol16x.cpp b/source/Protocol/Protocol16x.cpp index be5b45f19..0eac7b081 100644 --- a/source/Protocol/Protocol16x.cpp +++ b/source/Protocol/Protocol16x.cpp @@ -7,6 +7,8 @@ Implements the 1.6.x protocol classes: - release 1.6.1 protocol (#73) - cProtocol162 - release 1.6.2 protocol (#74) + - release 1.6.3 protocol (#77) - no relevant changes + - release 1.6.4 protocol (#78) - no relevant changes (others may be added later in the future for the 1.6 release series) */ diff --git a/source/Protocol/Protocol16x.h b/source/Protocol/Protocol16x.h index 077c7958b..2447f90a7 100644 --- a/source/Protocol/Protocol16x.h +++ b/source/Protocol/Protocol16x.h @@ -7,6 +7,8 @@ Declares the 1.6.x protocol classes: - release 1.6.1 protocol (#73) - cProtocol162 - release 1.6.2 protocol (#74) + - release 1.6.3 protocol (#77) - no relevant changes + - release 1.6.4 protocol (#78) - no relevant changes (others may be added later in the future for the 1.6 release series) */ diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index 853018329..fe99b22e1 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -55,6 +55,8 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) case PROTO_VERSION_1_5_2: return "1.5.2"; case PROTO_VERSION_1_6_1: return "1.6.1"; case PROTO_VERSION_1_6_2: return "1.6.2"; + case PROTO_VERSION_1_6_3: return "1.6.3"; + case PROTO_VERSION_1_6_4: return "1.6.4"; } ASSERT(!"Unknown protocol version"); return Printf("Unknown protocol (%d)", a_ProtocolVersion); @@ -707,6 +709,8 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void) return true; } case PROTO_VERSION_1_6_2: + case PROTO_VERSION_1_6_3: + case PROTO_VERSION_1_6_4: { m_Protocol = new cProtocol162(m_Client); return true; @@ -746,6 +750,8 @@ void cProtocolRecognizer::HandleServerPing(void) case PROTO_VERSION_1_5_2: case PROTO_VERSION_1_6_1: case PROTO_VERSION_1_6_2: + case PROTO_VERSION_1_6_3: + case PROTO_VERSION_1_6_4: { // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff. // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29 diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h index 2178d5e61..c53288230 100644 --- a/source/Protocol/ProtocolRecognizer.h +++ b/source/Protocol/ProtocolRecognizer.h @@ -18,8 +18,8 @@ // Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2" -#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74" +#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4" +#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78" @@ -42,6 +42,8 @@ public: PROTO_VERSION_1_5_2 = 61, PROTO_VERSION_1_6_1 = 73, PROTO_VERSION_1_6_2 = 74, + PROTO_VERSION_1_6_3 = 77, + PROTO_VERSION_1_6_4 = 78, PROTO_VERSION_NEXT, PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp index 04c06c887..93f2ccdd3 100644 --- a/source/RCONServer.cpp +++ b/source/RCONServer.cpp @@ -78,8 +78,8 @@ protected: cRCONServer::cRCONServer(cServer & a_Server) : m_Server(a_Server), - m_ListenThread4(*this, cSocket::IPv4, "RCON"), - m_ListenThread6(*this, cSocket::IPv6, "RCON") + m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"), + m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6") { } diff --git a/source/Root.cpp b/source/Root.cpp index 3933535f1..290a5269a 100644 --- a/source/Root.cpp +++ b/source/Root.cpp @@ -17,16 +17,23 @@ #include "Protocol/ProtocolRecognizer.h" // for protocol version constants #include "CommandOutput.h" #include "DeadlockDetect.h" +#include "OSSupport/Timer.h" #include "../iniFile/iniFile.h" -#include <iostream> +#ifdef _WIN32 + #include <psapi.h> +#elif defined(__linux__) + #include <fstream> +#elif defined(__APPLE__) + #include <mach/mach.h> +#endif -cRoot* cRoot::s_Root = 0; +cRoot* cRoot::s_Root = NULL; @@ -98,6 +105,9 @@ void cRoot::Start(void) m_bStop = false; while (!m_bStop) { + cTimer Time; + long long mseconds = Time.GetNowTime(); + m_bRestart = false; LoadGlobalSettings(); @@ -125,58 +135,57 @@ void cRoot::Start(void) LOG("Starting server..."); if (!m_Server->InitServer(IniFile)) { - LOGERROR("Failed to start server, shutting down."); + LOGERROR("Failure starting server, aborting..."); return; } IniFile.WriteFile(); - cIniFile WebIniFile("webadmin.ini"); - if (!WebIniFile.ReadFile()) - { - LOGWARNING("webadmin.ini inaccessible, wabadmin is disabled"); - } - - if (WebIniFile.GetValueB("WebAdmin", "Enabled", false)) - { - LOG("Creating WebAdmin..."); - m_WebAdmin = new cWebAdmin(8080); - } + LOG("Initialising WebAdmin..."); + m_WebAdmin = new cWebAdmin(); + m_WebAdmin->Init(); - LOG("Loading settings..."); + LOGD("Loading settings..."); m_GroupManager = new cGroupManager(); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); - LOG("Loading worlds..."); + LOGD("Loading worlds..."); LoadWorlds(); - LOG("Loading plugin manager..."); + LOGD("Loading plugin manager..."); m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(); - LOG("Loading MonsterConfig..."); + LOGD("Loading MonsterConfig..."); m_MonsterConfig = new cMonsterConfig; // This sets stuff in motion - LOG("Starting Authenticator..."); + LOGD("Starting Authenticator..."); m_Authenticator.Start(); - LOG("Starting worlds..."); + LOGD("Starting worlds..."); StartWorlds(); - LOG("Starting deadlock detector..."); + LOGD("Starting deadlock detector..."); dd.Start(); - LOG("Starting server..."); + LOGD("Finalising startup..."); m_Server->Start(); + + LOG("Starting WebAdmin..."); + m_WebAdmin->Start(); #if !defined(ANDROID_NDK) - LOG("Starting InputThread..."); + LOGD("Starting InputThread..."); m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread #endif - LOG("Initialization done, server running now."); + long long finishmseconds = Time.GetNowTime(); + finishmseconds -= mseconds; + + LOG("Startup complete, took %i ms!", finishmseconds); + while (!m_bStop && !m_bRestart) // These are modified by external threads { cSleep::MilliSleep(1000); @@ -190,37 +199,37 @@ void cRoot::Start(void) LOG("Shutting down server..."); m_Server->Shutdown(); - LOG("Shutting down deadlock detector..."); + LOGD("Shutting down deadlock detector..."); dd.Stop(); - LOG("Stopping world threads..."); + LOGD("Stopping world threads..."); StopWorlds(); - LOG("Stopping authenticator..."); + LOGD("Stopping authenticator..."); m_Authenticator.Stop(); - LOG("Freeing MonsterConfig..."); + LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = NULL; - LOG("Stopping WebAdmin..."); + LOGD("Stopping WebAdmin..."); delete m_WebAdmin; m_WebAdmin = NULL; - LOG("Unloading recipes..."); + LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; delete m_CraftingRecipes; m_CraftingRecipes = NULL; - LOG("Forgetting groups..."); + LOGD("Forgetting groups..."); delete m_GroupManager; m_GroupManager = 0; - LOG("Unloading worlds..."); + LOGD("Unloading worlds..."); UnloadWorlds(); - LOG("Stopping plugin manager..."); + LOGD("Stopping plugin manager..."); delete m_PluginManager; m_PluginManager = NULL; cItemHandler::Deinit(); cBlockHandler::Deinit(); - LOG("Destroying server..."); + LOG("Cleaning up..."); //delete HeartBeat; HeartBeat = 0; delete m_Server; m_Server = 0; - LOG("Shutdown done."); + LOG("Shutdown successful!"); } delete m_Log; m_Log = 0; @@ -568,6 +577,110 @@ AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) +int cRoot::GetVirtualRAMUsage(void) +{ + #ifdef _WIN32 + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc))) + { + return (int)(pmc.PrivateUsage / 1024); + } + return -1; + #elif defined(__linux__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + std::ifstream StatFile("/proc/self/status"); + if (!StatFile.good()) + { + return -1; + } + while (StatFile.good()) + { + AString Line; + std::getline(StatFile, Line); + if (strncmp(Line.c_str(), "VmSize:", 7) == 0) + { + int res = atoi(Line.c_str() + 8); + return (res == 0) ? -1 : res; // If parsing failed, return -1 + } + } + return -1; + #elif defined (__APPLE__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS == task_info( + mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count + )) + { + return (int)(t_info.virtual_size / 1024); + } + return -1; + #else + LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); + return -1; + #endif +} + + + + + +int cRoot::GetPhysicalRAMUsage(void) +{ + #ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + return (int)(pmc.WorkingSetSize / 1024); + } + return -1; + #elif defined(__linux__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + std::ifstream StatFile("/proc/self/status"); + if (!StatFile.good()) + { + return -1; + } + while (StatFile.good()) + { + AString Line; + std::getline(StatFile, Line); + if (strncmp(Line.c_str(), "VmRSS:", 7) == 0) + { + int res = atoi(Line.c_str() + 8); + return (res == 0) ? -1 : res; // If parsing failed, return -1 + } + } + return -1; + #elif defined (__APPLE__) + // Code adapted from http://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS == task_info( + mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count + )) + { + return (int)(t_info.resident_size / 1024); + } + return -1; + #else + LOGINFO("%s: Unknown platform, cannot query memory usage", __FUNCTION__); + return -1; + #endif +} + + + + + void cRoot::LogChunkStats(cCommandOutputCallback & a_Output) { int SumNumValid = 0; diff --git a/source/Root.h b/source/Root.h index 194b1cbb5..2b15d3461 100644 --- a/source/Root.h +++ b/source/Root.h @@ -2,6 +2,7 @@ #pragma once #include "Authenticator.h" +#include "HTTPServer/HTTPServer.h" @@ -104,8 +105,18 @@ public: /// Finds a player from a partial or complete player name and calls the callback - case-insensitive bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + // tolua_begin + /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API - static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); // tolua_export + static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); + + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error + static int GetVirtualRAMUsage(void); + + /// Returns the amount of virtual RAM used, in KiB. Returns a negative number on error + static int GetPhysicalRAMUsage(void); + + // tolua_end private: class cCommand @@ -141,6 +152,7 @@ private: cWebAdmin * m_WebAdmin; cPluginManager * m_PluginManager; cAuthenticator m_Authenticator; + cHTTPServer m_HTTPServer; cMCLogger * m_Log; diff --git a/source/Server.cpp b/source/Server.cpp index dd18f8d3d..879bfae5a 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -103,8 +103,8 @@ void cServer::cTickThread::Execute(void) // cServer: cServer::cServer(void) : - m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"), - m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"), + m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"), + m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"), m_bIsConnected(false), m_bRestarting(false), m_RCONServer(*this), @@ -206,7 +206,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) return false; } - LOG("Starting up server."); LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); @@ -292,7 +291,7 @@ void cServer::PrepareKeys(void) // TODO: Save and load key for persistence across sessions // But generating the key takes only a moment, do we even need that? - LOG("Generating protocol encryption keypair..."); + LOGD("Generating protocol encryption keypair..."); time_t CurTime = time(NULL); CryptoPP::RandomPool rng; diff --git a/source/Simulator/FireSimulator.cpp b/source/Simulator/FireSimulator.cpp index 587f45306..ac3fb9695 100644 --- a/source/Simulator/FireSimulator.cpp +++ b/source/Simulator/FireSimulator.cpp @@ -221,6 +221,7 @@ void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) { + bool IsBlockBelowSolid = false; if (a_RelY > 0) { BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); @@ -233,10 +234,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in { return m_BurnStepTimeFuel; } - } - if ((a_RelY < cChunkDef::Height - 1) && IsFuel(a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ))) - { - return m_BurnStepTimeFuel; + IsBlockBelowSolid = g_BlockIsSolid[BlockBelow]; } for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) @@ -251,6 +249,15 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in } } } // for i - gCrossCoords[] + + if (!IsBlockBelowSolid && (a_RelY >= 0)) + { + // Checked through everything, nothing was flammable + // If block below isn't solid, we can't have fire, it would be a non-fueled fire + // SetBlock just to make sure fire doesn't spawn + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return 0; + } return m_BurnStepTimeNonfuel; } diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp index cb91a4da7..d52b1323f 100644 --- a/source/StringUtils.cpp +++ b/source/StringUtils.cpp @@ -196,6 +196,23 @@ AString & StrToUpper(AString & s) +AString & StrToLower(AString & s) +{ + AString::iterator i = s.begin(); + AString::iterator end = s.end(); + + while (i != end) + { + *i = (char)tolower(*i); + ++i; + } + return s; +} + + + + + int NoCaseCompare(const AString & s1, const AString & s2) { #ifdef _MSC_VER @@ -658,3 +675,141 @@ AString StripColorCodes(const AString & a_Message) + +AString URLDecode(const AString & a_String) +{ + AString res; + size_t len = a_String.length(); + res.reserve(len); + for (size_t i = 0; i < len; i++) + { + char ch = a_String[i]; + if ((ch != '%') || (i > len - 3)) + { + res.push_back(ch); + continue; + } + // Decode the hex value: + char hi = a_String[i + 1], lo = a_String[i + 2]; + if ((hi >= '0') && (hi <= '9')) + { + hi = hi - '0'; + } + else if ((hi >= 'a') && (hi <= 'f')) + { + hi = hi - 'a' + 10; + } + else if ((hi >= 'A') && (hi <= 'F')) + { + hi = hi - 'F' + 10; + } + else + { + res.push_back(ch); + continue; + } + if ((lo >= '0') && (lo <= '9')) + { + lo = lo - '0'; + } + else if ((lo >= 'a') && (lo <= 'f')) + { + lo = lo - 'a' + 10; + } + else if ((lo >= 'A') && (lo <= 'F')) + { + lo = lo - 'A' + 10; + } + else + { + res.push_back(ch); + continue; + } + res.push_back((hi << 4) | lo); + i += 2; + } // for i - a_String[] + return res; +} + + + + + +AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To) +{ + AString res(a_String); + std::replace(res.begin(), res.end(), a_From, a_To); + return res; +} + + + + + +/// Converts one Hex character in a Base64 encoding into the data value +static inline int UnBase64(char c) +{ + if (c >='A' && c <= 'Z') + { + return c - 'A'; + } + if (c >='a' && c <= 'z') + { + return c - 'a' + 26; + } + if (c >= '0' && c <= '9') + { + return c - '0' + 52; + } + if (c == '+') + { + return 62; + } + if (c == '/') + { + return 63; + } + if (c == '=') + { + return -1; + } + return -2; +} + + + + + +AString Base64Decode(const AString & a_Base64String) +{ + AString res; + size_t i, len = a_Base64String.size(); + int o, c; + res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length + for (o = 0, i = 0; i < len; i++) + { + c = UnBase64(a_Base64String[i]); + if (c >= 0) + { + switch (o & 7) + { + case 0: res[o >> 3] |= (c << 2); break; + case 6: res[o >> 3] |= (c >> 4); res[(o >> 3) + 1] |= (c << 4); break; + case 4: res[o >> 3] |= (c >> 2); res[(o >> 3) + 1] |= (c << 6); break; + case 2: res[o >> 3] |= c; break; + } + o += 6; + } + if (c == -1) + { + // Error while decoding, invalid input. Return as much as we've decoded: + res.resize(o >> 3); + return res; + } + } + res.resize(o >> 3); + return res;} + + + + diff --git a/source/StringUtils.h b/source/StringUtils.h index 211799e91..ec9ba96ce 100644 --- a/source/StringUtils.h +++ b/source/StringUtils.h @@ -45,6 +45,9 @@ extern AString TrimString(const AString & str); // tolua_export /// In-place string conversion to uppercase; returns the same string extern AString & StrToUpper(AString & s); +/// In-place string conversion to lowercase; returns the same string +extern AString & StrToLower(AString & s); + /// Case-insensitive string comparison; returns 0 if the strings are the same extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_export @@ -72,6 +75,15 @@ extern AString EscapeString(const AString & a_Message); // tolua_export /// Removes all control codes used by MC for colors and styles extern AString StripColorCodes(const AString & a_Message); // tolua_export +/// URL-Decodes the given string, replacing all "%HH" into the correct characters. Invalid % sequences are left intact +extern AString URLDecode(const AString & a_String); // Cannot export to Lua automatically - would generated an extra return value + +/// Replaces all occurrences of char a_From inside a_String with char a_To. +extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To); // Needn't export to Lua, since Lua doesn't have chars anyway + +/// Decodes a Base64-encoded string into the raw data +extern AString Base64Decode(const AString & a_Base64String); + // If you have any other string helper functions, declare them here diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp index 77a5865d3..08817139a 100644 --- a/source/WebAdmin.cpp +++ b/source/WebAdmin.cpp @@ -14,13 +14,8 @@ #include "Server.h" #include "Root.h" -#include "../iniFile/iniFile.h" - -#ifdef _WIN32 - #include <psapi.h> -#else - #include <sys/resource.h> -#endif +#include "HTTPServer/HTTPMessage.h" +#include "HTTPServer/HTTPConnection.h" @@ -47,313 +42,253 @@ public: -cWebAdmin * WebAdmin = NULL; +cWebAdmin::cWebAdmin(void) : + m_IsInitialized(false), + m_TemplateScript("<webadmin_template>"), + m_IniFile("webadmin.ini") +{ +} -cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ ) : - m_Port(a_Port), - m_bConnected(false), - m_TemplateScript("<webadmin_template>") +void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) { - WebAdmin = this; - m_Event = new cEvent(); - Init( m_Port ); + m_Plugins.remove( a_Plugin ); + m_Plugins.push_back( a_Plugin ); } -cWebAdmin::~cWebAdmin() +void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) { + m_Plugins.remove( a_Plugin ); +} - WebAdmin = 0; - m_WebServer->Stop(); - delete m_WebServer; - delete m_IniFile; - m_Event->Wait(); - delete m_Event; + + +bool cWebAdmin::Init(void) +{ + if (!m_IniFile.ReadFile()) + { + return false; + } + + AString PortsIPv4 = m_IniFile.GetValue("WebAdmin", "Port", "8080"); + AString PortsIPv6 = m_IniFile.GetValue("WebAdmin", "PortsIPv6", ""); + + if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6)) + { + return false; + } + m_IsInitialized = true; + return true; } -void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) +bool cWebAdmin::Start(void) { - m_Plugins.remove( a_Plugin ); - m_Plugins.push_back( a_Plugin ); + if (!m_IsInitialized) + { + // Not initialized + return false; + } + + // Initialize the WebAdmin template script and load the file + m_TemplateScript.Create(); + if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) + { + LOGWARN("Could not load WebAdmin template \"%s\", using default template.", FILE_IO_PREFIX "webadmin/template.lua"); + m_TemplateScript.Close(); + } + + return m_HTTPServer.Start(*this); } -void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) +AString cWebAdmin::GetTemplate() { - m_Plugins.remove( a_Plugin ); + AString retVal = ""; + + char SourceFile[] = "webadmin/template.html"; + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + return ""; + } + + // copy the file into the buffer: + f.ReadRestOfFile(retVal); + + return retVal; } -void cWebAdmin::Request_Handler(webserver::http_request* r) +void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - if( WebAdmin == 0 ) return; - LOG("Path: %s", r->path_.c_str() ); - - if (r->path_ == "/") + if (!a_Request.HasAuth()) { - r->answer_ += "<h1>MCServer WebAdmin</h1>"; - r->answer_ += "<center>"; - r->answer_ += "<form method='get' action='webadmin/'>"; - r->answer_ += "<input type='submit' value='Log in'>"; - r->answer_ += "</form>"; - r->answer_ += "</center>"; + a_Connection.SendNeedAuth("MCServer WebAdmin"); return; } - if (r->path_.empty() || r->path_[0] != '/') + // Check auth: + AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); + if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) { - r->answer_ += "<h1>Bad request</h1>"; - r->answer_ += "<p>"; - r->answer_ = r->path_; // TODO: Shouldn't we sanitize this? Possible security issue. - r->answer_ += "</p>"; + a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password"); return; } - AStringVector Split = StringSplit(r->path_.substr(1), "/"); - - if (Split.empty() || (Split[0] != "webadmin" && Split[0] != "~webadmin")) + // Check if the contents should be wrapped in the template: + AString URL = a_Request.GetBareURL(); + ASSERT(URL.length() > 0); + bool ShouldWrapInTemplate = ((URL.length() > 1) && (URL[1] != '~')); + + // Retrieve the request data: + cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData()); + if (Data == NULL) { - r->answer_ += "<h1>Bad request</h1>"; + a_Connection.SendStatusAndReason(500, "Bad UserData"); return; } - if (!r->authentication_given_) - { - r->answer_ += "no auth"; - r->auth_realm_ = "MCServer WebAdmin"; - } - - bool bDontShowTemplate = false; - if (Split[0] == "~webadmin") + // Wrap it all up for the Lua call: + AString Template; + HTTPTemplateRequest TemplateRequest; + TemplateRequest.Request.Username = a_Request.GetAuthUsername(); + TemplateRequest.Request.Method = a_Request.GetMethod(); + TemplateRequest.Request.Path = URL.substr(1); + + if (Data->m_Form.Finish()) { - bDontShowTemplate = true; + for (cHTTPFormParser::const_iterator itr = Data->m_Form.begin(), end = Data->m_Form.end(); itr != end; ++itr) + { + HTTPFormData HTTPfd; + HTTPfd.Value = itr->second; + HTTPfd.Type = ""; + HTTPfd.Name = itr->first; + TemplateRequest.Request.FormData[itr->first] = HTTPfd; + TemplateRequest.Request.PostParams[itr->first] = itr->second; + TemplateRequest.Request.Params[itr->first] = itr->second; + } // for itr - Data->m_Form[] } - AString UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", ""); - if ((UserPassword != "") && (r->password_ == UserPassword)) + // Try to get the template from the Lua template script + if (ShouldWrapInTemplate) { - AString Template; - - HTTPTemplateRequest TemplateRequest; - TemplateRequest.Request.Username = r->username_; - TemplateRequest.Request.Method = r->method_; - TemplateRequest.Request.Params = r->params_; - TemplateRequest.Request.PostParams = r->params_post_; - TemplateRequest.Request.Path = r->path_.substr(1); - - for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i ) + if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template)) { - webserver::formdata& fd = r->multipart_formdata_[i]; - - HTTPFormData HTTPfd;//( fd.value_ ); - HTTPfd.Value = fd.value_; - HTTPfd.Type = fd.content_type_; - HTTPfd.Name = fd.name_; - LOGINFO("Form data name: %s", fd.name_.c_str() ); - TemplateRequest.Request.FormData[ fd.name_ ] = HTTPfd; + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(Template.c_str(), Template.length()); + return; } + a_Connection.SendStatusAndReason(500, "m_TemplateScript failed"); + return; + } + + AString BaseURL = GetBaseURL(URL); + AString Menu; + Template = "{CONTENT}"; + AString FoundPlugin; - // Try to get the template from the Lua template script - bool bLuaTemplateSuccessful = false; - if (!bDontShowTemplate) - { - bLuaTemplateSuccessful = WebAdmin->m_TemplateScript.Call("ShowPage", WebAdmin, &TemplateRequest, cLuaState::Return, Template); - } - - if (!bLuaTemplateSuccessful) + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + { + cWebPlugin * WebPlugin = *itr; + std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); + for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names) { - AString BaseURL = WebAdmin->GetBaseURL(Split); - AString Menu; - Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate(); - AString FoundPlugin; - - for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr) - { - cWebPlugin* WebPlugin = *itr; - std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); - for( std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names ) - { - Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; - } - } - - sWebAdminPage Page = WebAdmin->GetPage(TemplateRequest.Request); - AString Content = Page.Content; - FoundPlugin = Page.PluginName; - if (!Page.TabName.empty()) - FoundPlugin += " - " + Page.TabName; - - if( FoundPlugin.empty() ) // Default page - { - Content.clear(); - FoundPlugin = "Current Game"; - Content += "<h4>Server Name:</h4>"; - Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>"; - - Content += "<h4>Plugins:</h4><ul>"; - cPluginManager* PM = cRoot::Get()->GetPluginManager(); - if( PM ) - { - const cPluginManager::PluginMap & List = PM->GetAllPlugins(); - for( cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr ) - { - if( itr->second == NULL ) continue; - AString VersionNum; - AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion()); - } - } - Content += "</ul>"; - Content += "<h4>Players:</h4><ul>"; - - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if( World != NULL ) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "</ul><br>"; - } - - - - if (!bDontShowTemplate && (Split.size() > 1)) - { - Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; - } - - AString MemUsage = GetMemoryUsage(); - ReplaceString(Template, "{MEM}", MemUsage); - ReplaceString(Template, "{USERNAME}", r->username_); - ReplaceString(Template, "{MENU}", Menu); - ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); - ReplaceString(Template, "{CONTENT}", Content); - ReplaceString(Template, "{TITLE}", "MCServer"); - - AString NumChunks; - Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); - ReplaceString(Template, "{NUMCHUNKS}", NumChunks); + Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; } - - r->answer_ = Template; } - else + + sWebAdminPage Page = GetPage(TemplateRequest.Request); + AString Content = Page.Content; + FoundPlugin = Page.PluginName; + if (!Page.TabName.empty()) { - r->answer_ += "Wrong username/password"; - r->auth_realm_ = "MCServer WebAdmin"; + FoundPlugin += " - " + Page.TabName; } -} - - - - -bool cWebAdmin::Init(int a_Port) -{ - m_Port = a_Port; - - m_IniFile = new cIniFile("webadmin.ini"); - if (m_IniFile->ReadFile()) + if (FoundPlugin.empty()) // Default page { - m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080); + Content = GetDefaultPage(); } - // Initialize the WebAdmin template script and load the file - m_TemplateScript.Create(); - if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) + if (ShouldWrapInTemplate && (URL.size() > 1)) { - LOGWARN("Could not load WebAdmin template \"%s\", using default template.", FILE_IO_PREFIX "webadmin/template.lua"); - m_TemplateScript.Close(); + Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; } - - LOG("Starting WebAdmin on port %i", m_Port); - -#ifdef _WIN32 - HANDLE hThread = CreateThread( - NULL, // default security - 0, // default stack size - ListenThread, // name of the thread function - this, // thread parameters - 0, // default startup flags - NULL); - CloseHandle( hThread ); // Just close the handle immediately -#else - pthread_t LstnThread; - pthread_create( &LstnThread, 0, ListenThread, this); -#endif - - return true; -} - - - - - -#ifdef _WIN32 -DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam) -#else -void *cWebAdmin::ListenThread( void *lpParam ) -#endif -{ - cWebAdmin* self = (cWebAdmin*)lpParam; - - self->m_WebServer = new webserver(self->m_Port, Request_Handler ); - if (!self->m_WebServer->Begin()) + int MemUsageKiB = GetMemoryUsage(); + if (MemUsageKiB > 0) { - LOGWARN("WebServer failed to start! WebAdmin is disabled"); + ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024)); + ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB)); } - - self->m_Event->Set(); - return 0; + else + { + ReplaceString(Template, "{MEM}", "unknown"); + ReplaceString(Template, "{MEMKIB}", "unknown"); + } + ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername()); + ReplaceString(Template, "{MENU}", Menu); + ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); + ReplaceString(Template, "{CONTENT}", Content); + ReplaceString(Template, "{TITLE}", "MCServer"); + + AString NumChunks; + Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); + ReplaceString(Template, "{NUMCHUNKS}", NumChunks); + + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(Template.c_str(), Template.length()); } -AString cWebAdmin::GetTemplate() +void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - AString retVal = ""; - - char SourceFile[] = "webadmin/template.html"; - - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) - { - return ""; - } - - // copy the file into the buffer: - f.ReadRestOfFile(retVal); - - return retVal; + static const char LoginForm[] = \ + "<h1>MCServer WebAdmin</h1>" \ + "<center>" \ + "<form method='get' action='webadmin/'>" \ + "<input type='submit' value='Log in'>" \ + "</form>" \ + "</center>"; + cHTTPResponse Resp; + Resp.SetContentType("text/html"); + a_Connection.Send(Resp); + a_Connection.Send(LoginForm, sizeof(LoginForm) - 1); + a_Connection.FinishResponse(); } -sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request) +sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { sWebAdminPage Page; AStringVector Split = StringSplit(a_Request.Path, "/"); @@ -362,7 +297,7 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request) AString FoundPlugin; if (Split.size() > 1) { - for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr) + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) { if ((*itr)->GetWebTitle() == Split[1]) { @@ -385,6 +320,41 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request) +AString cWebAdmin::GetDefaultPage(void) +{ + AString Content; + Content += "<h4>Server Name:</h4>"; + Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>"; + + Content += "<h4>Plugins:</h4><ul>"; + cPluginManager * PM = cPluginManager::Get(); + const cPluginManager::PluginMap & List = PM->GetAllPlugins(); + for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr) + { + if (itr->second == NULL) + { + continue; + } + AString VersionNum; + AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion()); + } + Content += "</ul>"; + Content += "<h4>Players:</h4><ul>"; + + cPlayerAccum PlayerAccum; + cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players + if( World != NULL ) + { + World->ForEachPlayer(PlayerAccum); + Content.append(PlayerAccum.m_Contents); + } + Content += "</ul><br>"; + return Content; +} + + + + AString cWebAdmin::GetBaseURL( const AString& a_URL ) { return GetBaseURL(StringSplit(a_URL, "/")); @@ -412,26 +382,90 @@ AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit ) -AString cWebAdmin::GetMemoryUsage(void) +int cWebAdmin::GetMemoryUsage(void) +{ + LOGWARNING("%s: This function is obsolete, use cRoot::GetPhysicalRAMUsage() or cRoot::GetVirtualRAMUsage() instead", __FUNCTION__); + return cRoot::GetPhysicalRAMUsage(); +} + + + + + +void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { - AString MemUsage; -#ifndef _WIN32 - rusage resource_usage; - if (getrusage(RUSAGE_SELF, &resource_usage) != 0) + const AString & URL = a_Request.GetURL(); + if ( + (strncmp(URL.c_str(), "/webadmin", 9) == 0) || + (strncmp(URL.c_str(), "/~webadmin", 10) == 0) + ) { - MemUsage = "Error :("; + a_Request.SetUserData(new cWebadminRequestData(a_Request)); + return; } - else + if (URL == "/") + { + // The root needs no body handler and is fully handled in the OnRequestFinished() call + return; + } + // TODO: Handle other requests +} + + + + + +void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) +{ + cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); + if (Data == NULL) + { + return; + } + Data->OnBody(a_Data, a_Size); +} + + + + + +void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + const AString & URL = a_Request.GetURL(); + if ( + (strncmp(URL.c_str(), "/webadmin", 9) == 0) || + (strncmp(URL.c_str(), "/~webadmin", 10) == 0) + ) + { + HandleWebadminRequest(a_Connection, a_Request); + } + else if (URL == "/") { - Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) ); + // The root needs no body handler and is fully handled in the OnRequestFinished() call + HandleRootRequest(a_Connection, a_Request); } -#else - HANDLE hProcess = GetCurrentProcess(); - PROCESS_MEMORY_COUNTERS pmc; - if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) ) + else { - Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) ); + // TODO: Handle other requests } -#endif - return MemUsage; + + // Delete any request data assigned to the request: + cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); + delete Data; } + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWebAdmin::cWebadminRequestData + +void cWebAdmin::cWebadminRequestData::OnBody(const char * a_Data, int a_Size) +{ + m_Form.Parse(a_Data, a_Size); +} + + + + diff --git a/source/WebAdmin.h b/source/WebAdmin.h index a62b532f1..16b5dd4dc 100644 --- a/source/WebAdmin.h +++ b/source/WebAdmin.h @@ -1,8 +1,26 @@ + +// WebAdmin.h + +// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API) + #pragma once -#include "../WebServer/WebServer.h" #include "OSSupport/Socket.h" #include "LuaState.h" +#include "../iniFile/iniFile.h" +#include "HTTPServer/HTTPServer.h" +#include "HTTPServer/HTTPFormParser.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + @@ -10,7 +28,6 @@ // fwd: class cStringMap; class cEvent; -class cIniFile; class cWebPlugin; @@ -73,7 +90,8 @@ struct sWebAdminPage // tolua_begin -class cWebAdmin +class cWebAdmin : + public cHTTPServer::cCallbacks { public: // tolua_end @@ -81,57 +99,108 @@ public: typedef std::list< cWebPlugin* > PluginList; - cWebAdmin( int a_Port = 8080 ); - ~cWebAdmin(); + cWebAdmin(void); - bool Init( int a_Port ); + /// Initializes the object. Returns true if successfully initialized and ready to start + bool Init(void); + + /// Starts the HTTP server taking care of the admin. Returns true if successful + bool Start(void); - void AddPlugin( cWebPlugin* a_Plugin ); - void RemovePlugin( cWebPlugin* a_Plugin ); + void AddPlugin( cWebPlugin* a_Plugin ); + void RemovePlugin( cWebPlugin* a_Plugin ); // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << - static void Request_Handler(webserver::http_request* r); - // tolua_begin - static AString GetMemoryUsage(void); - - int GetPort() { return m_Port; } + + /// Returns the amount of currently used memory, in KiB, or -1 if it cannot be queried + static int GetMemoryUsage(void); sWebAdminPage GetPage(const HTTPRequest& a_Request); + + /// Returns the contents of the default page - the list of plugins and players + AString GetDefaultPage(void); + AString GetBaseURL(const AString& a_URL); // tolua_end AString GetBaseURL(const AStringVector& a_URLSplit); + +protected: + /// Common base class for request body data handlers + class cRequestData + { + public: + virtual ~cRequestData() {} // Force a virtual destructor in all descendants + + /// Called when a new chunk of body data is received + virtual void OnBody(const char * a_Data, int a_Size) = 0; + } ; + + /// The body handler for requests in the "/webadmin" and "/~webadmin" paths + class cWebadminRequestData : + public cRequestData, + public cHTTPFormParser::cCallbacks + { + public: + cHTTPFormParser m_Form; + + + cWebadminRequestData(cHTTPRequest & a_Request) : + m_Form(a_Request, *this) + { + } + + // cRequestData overrides: + virtual void OnBody(const char * a_Data, int a_Size) override; + + // cHTTPFormParser::cCallbacks overrides. Files are ignored: + virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {} + virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {} + virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {} + } ; + + + /// Set to true if Init() succeeds and the webadmin isn't to be disabled + bool m_IsInitialized; + /// The webadmin.ini file, used for the settings and allowed logins + cIniFile m_IniFile; -private: - int m_Port; + PluginList m_Plugins; - bool m_bConnected; - cSocket m_ListenSocket; + /// The Lua template script to provide templates: + cLuaState m_TemplateScript; + + /// The HTTP server which provides the underlying HTTP parsing, serialization and events + cHTTPServer m_HTTPServer; - cIniFile * m_IniFile; - PluginList m_Plugins; - cEvent * m_Event; + AString GetTemplate(void); + + /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs + void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + /// Handles requests for the root page + void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); + + // cHTTPServer::cCallbacks overrides: + virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; + virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override; + virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; +} ; // tolua_export - webserver * m_WebServer; - /// The Lua template script to provide templates: - cLuaState m_TemplateScript; - #ifdef _WIN32 - static DWORD WINAPI ListenThread(LPVOID lpParam); - #else - static void * ListenThread(void * lpParam); - #endif - AString GetTemplate(); -} ; // tolua_export +// Revert MSVC warnings back to orignal state: +#if defined(_MSC_VER) + #pragma warning(pop) +#endif diff --git a/source/World.cpp b/source/World.cpp index 92098bd8b..ef56e7fe9 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -40,7 +40,7 @@ #include "Mobs/Giant.h" #include "Mobs/Horse.h" #include "Mobs/IronGolem.h" -#include "Mobs/MagmaCube.h" +#include "Mobs/Magmacube.h" #include "Mobs/Mooshroom.h" #include "Mobs/Ocelot.h" #include "Mobs/Pig.h" @@ -56,9 +56,8 @@ #include "Mobs/Wither.h" #include "Mobs/Wolf.h" #include "Mobs/Zombie.h" -#include "Mobs/ZombiePigman.h" +#include "Mobs/Zombiepigman.h" -#include "OSSupport/MakeDir.h" #include "MersenneTwister.h" #include "Generating/Trees.h" #include "PluginManager.h" @@ -261,7 +260,7 @@ cWorld::cWorld(const AString & a_WorldName) : { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); - cMakeDir::MakeDir(m_WorldName.c_str()); + cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); } @@ -773,16 +772,36 @@ void cWorld::TickSpawnMobs(float a_Dt) // Spawn nether mobs switch (nightRand) { - case 5: MobType = cMonster::mtGhast; break; + case 0: MobType = cMonster::mtBlaze; break; + case 1: MobType = cMonster::mtGhast; break; + case 2: MobType = cMonster::mtGhast; break; + case 3: MobType = cMonster::mtGhast; break; + case 4: MobType = cMonster::mtZombiePigman; break; + case 5: MobType = cMonster::mtZombiePigman; break; case 6: MobType = cMonster::mtZombiePigman; break; + case 7: MobType = cMonster::mtZombiePigman; break; + case 8: MobType = cMonster::mtZombiePigman; break; + case 9: MobType = cMonster::mtZombiePigman; break; } break; } case biEnd: { - // Only endermen spawn in the End - MobType = cMonster::mtEnderman; + // Spawn only The End mobs + switch (nightRand) + { + case 0: MobType = cMonster::mtEnderDragon; break; + case 1: MobType = cMonster::mtEnderman; break; + case 2: MobType = cMonster::mtEnderman; break; + case 3: MobType = cMonster::mtEnderman; break; + case 4: MobType = cMonster::mtEnderman; break; + case 5: MobType = cMonster::mtEnderman; break; + case 6: MobType = cMonster::mtEnderman; break; + case 7: MobType = cMonster::mtEnderman; break; + case 8: MobType = cMonster::mtEnderman; break; + case 9: MobType = cMonster::mtEnderman; break; + } break; } @@ -823,6 +842,7 @@ void cWorld::TickSpawnMobs(float a_Dt) case 3: MobType = cMonster::mtSheep; break; case 4: MobType = cMonster::mtSquid; break; case 5: MobType = cMonster::mtWolf; break; + case 6: MobType = cMonster::mtHorse; break; } } // else (night) } // case overworld biomes @@ -849,7 +869,7 @@ void cWorld::TickQueuedTasks(void) } // Execute and delete each task: - for (cTasks::iterator itr = m_Tasks.begin(), end = m_Tasks.end(); itr != end; ++itr) + for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) { (*itr)->Run(*this); delete *itr; diff --git a/source/World.h b/source/World.h index dd856fc72..25bc0b338 100644 --- a/source/World.h +++ b/source/World.h @@ -508,7 +508,7 @@ public: void QueueSaveAllChunks(void); // tolua_export
/// Queues a task onto the tick thread. The task object will be deleted once the task is finished
- void QueueTask(cTask * a_Task);
+ void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp
/// Returns the number of chunks loaded
int GetNumChunks() const; // tolua_export
@@ -554,8 +554,16 @@ public: /// Forces a weather change in the next game tick
void ChangeWeather (void);
- /// Returns the current weather
+ /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible
eWeather GetWeather (void) const { return m_Weather; };
+
+ bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
+ bool IsWeatherRain (void) const { return (m_Weather == wRain); }
+ bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
+
+ /// Returns true if the current weather has any precipitation - rain or storm
+ bool IsWeatherWet (void) const { return (m_Weather != wSunny); }
+
// tolua_end
cChunkGenerator & GetGenerator(void) { return m_Generator; }
@@ -581,7 +589,7 @@ public: /// Appends all usernames starting with a_Text (case-insensitive) into Results
void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
-
+
private:
friend class cRoot;
diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp index 11dc50ee3..c9013b1b3 100644 --- a/source/WorldStorage/NBTChunkSerializer.cpp +++ b/source/WorldStorage/NBTChunkSerializer.cpp @@ -16,7 +16,6 @@ #include "../ItemGrid.h" #include "../StringCompression.h" #include "../Entities/Entity.h" -#include "../OSSupport/MakeDir.h" #include "FastNBT.h" #include "../Entities/FallingBlock.h" #include "../Entities/Boat.h" diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp index 4db1ed106..537e2f723 100644 --- a/source/WorldStorage/WSSAnvil.cpp +++ b/source/WorldStorage/WSSAnvil.cpp @@ -20,7 +20,6 @@ #include "../Item.h" #include "../ItemGrid.h" #include "../StringCompression.h" -#include "../OSSupport/MakeDir.h" #include "FastNBT.h" #include "../Mobs/Monster.h" #include "../Entities/Boat.h" @@ -200,7 +199,7 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) // Load it anew: AString FileName; Printf(FileName, "%s/region", m_World->GetName().c_str()); - cMakeDir::MakeDir(FileName); + cFile::CreateFolder(FILE_IO_PREFIX + FileName); AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); if (f == NULL) |