diff options
Diffstat (limited to 'source')
64 files changed, 3850 insertions, 608 deletions
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 a08985144..48d8f3f83 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/08/13 20:07:51. */ #ifndef __cplusplus @@ -240,18 +240,19 @@ 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,"HTTPFormData"); tolua_usertype(tolua_S,"cTracer"); + tolua_usertype(tolua_S,"HTTPFormData"); + tolua_usertype(tolua_S,"cWindow"); tolua_usertype(tolua_S,"cArrowEntity"); tolua_usertype(tolua_S,"cDropSpenserEntity"); - tolua_usertype(tolua_S,"cWindow"); - tolua_usertype(tolua_S,"Vector3i"); + tolua_usertype(tolua_S,"cBlockArea"); tolua_usertype(tolua_S,"cCraftingGrid"); + tolua_usertype(tolua_S,"Vector3i"); tolua_usertype(tolua_S,"cGroup"); tolua_usertype(tolua_S,"cStringMap"); tolua_usertype(tolua_S,"cBlockEntity"); @@ -9676,15 +9677,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 +9719,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 +10829,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 +13315,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 +18782,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 +18795,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 +19546,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 +26348,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) @@ -29030,14 +29212,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 +29416,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 +29469,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); @@ -29471,20 +29681,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); @@ -29495,10 +29691,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"etTNT",cEntity::etTNT); tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); tolua_constant(tolua_S,"etMob",cEntity::etMob); - tolua_constant(tolua_S,"eEntityType_Entity",cEntity::eEntityType_Entity); - tolua_constant(tolua_S,"eEntityType_Player",cEntity::eEntityType_Player); - tolua_constant(tolua_S,"eEntityType_Pickup",cEntity::eEntityType_Pickup); - tolua_constant(tolua_S,"eEntityType_Mob",cEntity::eEntityType_Mob); 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); @@ -29797,6 +29989,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"); @@ -29876,6 +30069,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); @@ -30172,11 +30369,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); @@ -30204,6 +30401,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); @@ -30493,7 +30692,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); @@ -30599,34 +30797,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..a1daea398 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/08/13 20:07:52.
*/
/* Exported function */
diff --git a/source/BlockID.cpp b/source/BlockID.cpp index ecdbc0c34..d91524de5 100644 --- a/source/BlockID.cpp +++ b/source/BlockID.cpp @@ -603,6 +603,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 +647,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/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/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 a2c99d2a0..d6c449b92 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, @@ -84,28 +103,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, - - // DEPRECATED older constants, left over for compatibility reasons (plugins) - etMob = etMonster, // DEPRECATED, use etMonster instead! - eEntityType_Entity = etEntity, - eEntityType_Player = etPlayer, - eEntityType_Pickup = etPickup, - eEntityType_Mob = etMob, - } ; - - // tolua_end - cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); virtual ~cEntity(); @@ -234,16 +231,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 a2f1e5593..95bad6570 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" @@ -51,34 +52,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(); - } } @@ -87,6 +97,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) { @@ -98,7 +109,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/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/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/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index 0852f559e..e2a8df11b 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -294,7 +294,7 @@ 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); } 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/ManualBindings.cpp b/source/ManualBindings.cpp index a80e186eb..a2b4c8810 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -1235,7 +1235,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 +1247,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 +1297,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 +1501,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)); @@ -2028,12 +2042,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/Horse.h b/source/Mobs/Horse.h index ea6e441bd..83e64308e 100644 --- a/source/Mobs/Horse.h +++ b/source/Mobs/Horse.h @@ -1,7 +1,7 @@ #pragma once -#include "AggressiveMonster.h" +#include "PassiveMonster.h" 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..a4c9a22f4 100644 --- a/source/OSSupport/File.cpp +++ b/source/OSSupport/File.cpp @@ -287,6 +287,21 @@ bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName +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 +} + + + + + int cFile::Printf(const char * a_Fmt, ...) { AString buf; diff --git a/source/OSSupport/File.h b/source/OSSupport/File.h index 8a057afa8..d4ea0d3a8 100644 --- a/source/OSSupport/File.h +++ b/source/OSSupport/File.h @@ -99,6 +99,9 @@ public: /// 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); + /// Returns true if the specified path is a folder + static bool IsFolder(const AString & a_Path); + int Printf(const char * a_Fmt, ...); private: 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/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/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 54bd28c9f..050132917 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -436,15 +436,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(); } 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 5e65213fd..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; @@ -321,7 +320,7 @@ void cServer::OnConnectionAccepted(cSocket & a_Socket) return; } - LOGD("Client \"%s\" connected!", ClientIP.c_str()); + LOG("Client \"%s\" connected!", ClientIP.c_str()); cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance); if (!m_SocketThreads.AddClient(a_Socket, NewHandle)) diff --git a/source/Simulator/FireSimulator.cpp b/source/Simulator/FireSimulator.cpp index 587f45306..da1dc8d15 100644 --- a/source/Simulator/FireSimulator.cpp +++ b/source/Simulator/FireSimulator.cpp @@ -234,10 +234,6 @@ 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; - } for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) { @@ -251,7 +247,23 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in } } } // for i - gCrossCoords[] - return m_BurnStepTimeNonfuel; + + if ((a_RelY > 0) && (a_RelY < cChunkDef::Height - 1)) + { + // Checked through everything, nothing was flammable + // If block below isn't solid, we can't have fire, otherwise, we have non-fueled fire + BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + if (g_BlockIsSolid[BlockBelow]) + { + return m_BurnStepTimeNonfuel; + } + else + { + // SetBlock just to make sure fire doesn't spawn + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return 0; + } + } } 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 606ef0787..96a4731d7 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,7 +56,7 @@ #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" diff --git a/source/World.h b/source/World.h index dd856fc72..dcb197bd0 100644 --- a/source/World.h +++ b/source/World.h @@ -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; }
|