diff options
Diffstat (limited to 'src')
463 files changed, 138449 insertions, 0 deletions
diff --git a/src/AllToLua.bat b/src/AllToLua.bat new file mode 100644 index 000000000..f7867fadb --- /dev/null +++ b/src/AllToLua.bat @@ -0,0 +1,27 @@ + +:: AllToLua.bat + +:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h + + + + + +:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway +git checkout --ours Bindings.cpp +git add -u Bindings.cpp +git checkout --ours Bindings.h +git add -u Bindings.h + + + + + +:: Regenerate the files: +"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg + + + + + +if %ALLTOLUA_WAIT%N == N pause diff --git a/src/AllToLua.pkg b/src/AllToLua.pkg new file mode 100644 index 000000000..ee594be1a --- /dev/null +++ b/src/AllToLua.pkg @@ -0,0 +1,81 @@ + +$#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +$#include "tolua_base.h" + +// Typedefs from Globals.h, so that we don't have to include that file: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; + + +$cfile "ChunkDef.h" + +$cfile "../iniFile/iniFile.h" + +$cfile "OSSupport/File.h" + +$cfile "BlockID.h" +$cfile "StringUtils.h" +$cfile "Defines.h" +$cfile "LuaFunctions.h" +$cfile "ChatColor.h" +$cfile "ClientHandle.h" +$cfile "Entities/Entity.h" +$cfile "Entities/Pawn.h" +$cfile "Entities/Player.h" +$cfile "Entities/Pickup.h" +$cfile "Entities/ProjectileEntity.h" +$cfile "PluginManager.h" +$cfile "Plugin.h" +$cfile "PluginLua.h" +$cfile "Server.h" +$cfile "World.h" +$cfile "Inventory.h" +$cfile "Enchantments.h" +$cfile "Item.h" +$cfile "ItemGrid.h" +$cfile "BlockEntities/BlockEntity.h" +$cfile "BlockEntities/BlockEntityWithItems.h" +$cfile "BlockEntities/ChestEntity.h" +$cfile "BlockEntities/DropSpenserEntity.h" +$cfile "BlockEntities/DispenserEntity.h" +$cfile "BlockEntities/DropperEntity.h" +$cfile "BlockEntities/FurnaceEntity.h" +$cfile "BlockEntities/HopperEntity.h" +$cfile "BlockEntities/JukeboxEntity.h" +$cfile "BlockEntities/NoteEntity.h" +$cfile "BlockEntities/SignEntity.h" +$cfile "WebAdmin.h" +$cfile "WebPlugin.h" +$cfile "Root.h" +$cfile "Vector3f.h" +$cfile "Vector3d.h" +$cfile "Vector3i.h" +$cfile "Matrix4f.h" +$cfile "Cuboid.h" +$cfile "BoundingBox.h" +$cfile "Tracer.h" +$cfile "Group.h" +$cfile "BlockArea.h" +$cfile "Generating/ChunkDesc.h" +$cfile "CraftingRecipes.h" +$cfile "UI/Window.h" +$cfile "LuaWindow.h" +$cfile "Mobs/Monster.h" + + + + + +// Need to declare this class so that the usertype is properly registered in Bindings.cpp - +// it seems impossible to register a usertype in ManualBindings.cpp +class cLineBlockTracer; + + + + diff --git a/src/AllToLua.sh b/src/AllToLua.sh new file mode 100755 index 000000000..887c2490c --- /dev/null +++ b/src/AllToLua.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/usr/bin/tolua++ -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp new file mode 100644 index 000000000..4d9e90a01 --- /dev/null +++ b/src/Authenticator.cpp @@ -0,0 +1,267 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Authenticator.h" +#include "OSSupport/BlockingTCPLink.h" +#include "Root.h" +#include "Server.h" + +#include ".inifile/iniFile.h" + +#include <sstream> + + + + + +#define DEFAULT_AUTH_SERVER "session.minecraft.net" +#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%" +#define MAX_REDIRECTS 10 + + + + + +cAuthenticator::cAuthenticator(void) : + super("cAuthenticator"), + m_Server(DEFAULT_AUTH_SERVER), + m_Address(DEFAULT_AUTH_ADDRESS), + m_ShouldAuthenticate(true) +{ +} + + + + + +cAuthenticator::~cAuthenticator() +{ + Stop(); +} + + + + + +/// Read custom values from INI +void cAuthenticator::ReadINI(cIniFile & IniFile) +{ + m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER); + m_Address = IniFile.GetValueSet("Authentication", "Address", DEFAULT_AUTH_ADDRESS); + m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); +} + + + + + +/// Queues a request for authenticating a user. If the auth fails, the user is kicked +void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash) +{ + if (!m_ShouldAuthenticate) + { + cRoot::Get()->AuthenticateUser(a_ClientID); + return; + } + + cCSLock Lock(m_CS); + m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash)); + m_QueueNonempty.Set(); +} + + + + + +void cAuthenticator::Start(cIniFile & IniFile) +{ + ReadINI(IniFile); + m_ShouldTerminate = false; + super::Start(); +} + + + + + +void cAuthenticator::Stop(void) +{ + m_ShouldTerminate = true; + m_QueueNonempty.Set(); + Wait(); +} + + + + + +void cAuthenticator::Execute(void) +{ + for (;;) + { + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && (m_Queue.size() == 0)) + { + cCSUnlock Unlock(Lock); + m_QueueNonempty.Wait(); + } + if (m_ShouldTerminate) + { + return; + } + ASSERT(!m_Queue.empty()); + + int ClientID = m_Queue.front().m_ClientID; + AString UserName = m_Queue.front().m_Name; + AString ActualAddress = m_Address; + ReplaceString(ActualAddress, "%USERNAME%", UserName); + ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID); + m_Queue.pop_front(); + Lock.Unlock(); + + if (!AuthFromAddress(m_Server, ActualAddress, UserName)) + { + cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!"); + } + else + { + cRoot::Get()->AuthenticateUser(ClientID); + } + } // for (-ever) +} + + + + + +bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */) +{ + // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) + + cBlockingTCPLink Link; + if (!Link.Connect(a_Server.c_str(), 80)) + { + LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str()); + return false; + } + + Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str()); + Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str()); + Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str()); + //Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str()); + Link.SendMessage( AString( "Accept: */*\r\n" ).c_str()); + Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we don´t have to mess with the Content-Length :) + Link.SendMessage( AString( "\r\n" ).c_str()); + AString DataRecvd; + Link.ReceiveData(DataRecvd); + Link.CloseSocket(); + + std::stringstream ss(DataRecvd); + + // Parse the data received: + std::string temp; + ss >> temp; + bool bRedirect = false; + bool bOK = false; + if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0)) + { + int code; + ss >> code; + if (code == 302) + { + // redirect blabla + LOGINFO("Need to redirect!"); + if (a_Level > MAX_REDIRECTS) + { + LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str()); + return false; + } + bRedirect = true; + } + else if (code == 200) + { + LOGD("cAuthenticator: Received status 200 OK! :D"); + bOK = true; + } + } + else + { + LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + if( bRedirect ) + { + AString Location; + // Search for "Location:" + bool bFoundLocation = false; + while( !bFoundLocation && ss.good() ) + { + char c = 0; + while( c != '\n' ) + { + ss.get( c ); + } + AString Name; + ss >> Name; + if (Name.compare("Location:") == 0) + { + bFoundLocation = true; + ss >> Location; + } + } + if (!bFoundLocation) + { + LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + Location = Location.substr(strlen("http://"), std::string::npos); // Strip http:// + std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address + Location = Location.substr( Server.length(), std::string::npos); + return AuthFromAddress(Server, Location, a_UserName, a_Level + 1); + } + + if (!bOK) + { + LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + // Header says OK, so receive the rest. + // Go past header, double \n means end of headers + char c = 0; + while (ss.good()) + { + while (c != '\n') + { + ss.get(c); + } + ss.get(c); + if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' ) + break; + } + if (!ss.good()) + { + LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + std::string Result; + ss >> Result; + LOGD("cAuthenticator: Authentication result was %s", Result.c_str()); + + if (Result.compare("YES") == 0) //Works well + { + LOGINFO("Authentication result \"YES\", player authentication success!"); + return true; + } + + + LOGINFO("Authentication result was \"%s\", player authentication failure!", Result.c_str()); + return false; +} + + + + diff --git a/src/Authenticator.h b/src/Authenticator.h new file mode 100644 index 000000000..02cd6f4c5 --- /dev/null +++ b/src/Authenticator.h @@ -0,0 +1,93 @@ + +// cAuthenticator.h + +// Interfaces to the cAuthenticator class representing the thread that authenticates users against the official MC server +// Authentication prevents "hackers" from joining with an arbitrary username (possibly impersonating the server admins) +// For more info, see http://wiki.vg/Session#Server_operation +// In MCS, authentication is implemented as a single thread that receives queued auth requests and dispatches them one by one. + + + + + +#pragma once +#ifndef CAUTHENTICATOR_H_INCLUDED +#define CAUTHENTICATOR_H_INCLUDED + +#include "OSSupport/IsThread.h" + + + + + +// fwd: "cRoot.h" +class cRoot; + + + + + +class cAuthenticator : + public cIsThread +{ + typedef cIsThread super; + +public: + cAuthenticator(void); + ~cAuthenticator(); + + /// (Re-)read server and address from INI: + void ReadINI(cIniFile & IniFile); + + /// 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); + + /// Starts the authenticator thread. The thread may be started and stopped repeatedly + void Start(cIniFile & IniFile); + + /// Stops the authenticator thread. The thread may be started and stopped repeatedly + void Stop(void); + +private: + + class cUser + { + public: + int m_ClientID; + AString m_Name; + AString m_ServerID; + + cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) : + m_ClientID(a_ClientID), + m_Name(a_Name), + m_ServerID(a_ServerID) + { + } + } ; + + typedef std::deque<cUser> cUserList; + + cCriticalSection m_CS; + cUserList m_Queue; + cEvent m_QueueNonempty; + + AString m_Server; + AString m_Address; + bool m_ShouldAuthenticate; + + // cIsThread override: + virtual void Execute(void) override; + + // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) + bool AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level = 1); +}; + + + + + +#endif // CAUTHENTICATOR_H_INCLUDED + + + + diff --git a/src/Bindings.cpp b/src/Bindings.cpp new file mode 100644 index 000000000..7fc9be379 --- /dev/null +++ b/src/Bindings.cpp @@ -0,0 +1,31660 @@ +/* +** Lua binding: AllToLua +** Generated automatically by tolua++-1.0.92 on 11/26/13 15:29:19. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "tolua_base.h" +#include "ChunkDef.h" +#include "lib/iniFile/iniFile.h" +#include "OSSupport/File.h" +#include "BlockID.h" +#include "StringUtils.h" +#include "Defines.h" +#include "LuaFunctions.h" +#include "ChatColor.h" +#include "ClientHandle.h" +#include "Entities/Entity.h" +#include "Entities/Pawn.h" +#include "Entities/Player.h" +#include "Entities/Pickup.h" +#include "Entities/ProjectileEntity.h" +#include "PluginManager.h" +#include "Plugin.h" +#include "PluginLua.h" +#include "Server.h" +#include "World.h" +#include "Inventory.h" +#include "Enchantments.h" +#include "Item.h" +#include "ItemGrid.h" +#include "BlockEntities/BlockEntity.h" +#include "BlockEntities/BlockEntityWithItems.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/DropSpenserEntity.h" +#include "BlockEntities/DispenserEntity.h" +#include "BlockEntities/DropperEntity.h" +#include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" +#include "BlockEntities/JukeboxEntity.h" +#include "BlockEntities/NoteEntity.h" +#include "BlockEntities/SignEntity.h" +#include "WebAdmin.h" +#include "WebPlugin.h" +#include "Root.h" +#include "Vector3f.h" +#include "Vector3d.h" +#include "Vector3i.h" +#include "Matrix4f.h" +#include "Cuboid.h" +#include "BoundingBox.h" +#include "Tracer.h" +#include "Group.h" +#include "BlockArea.h" +#include "Generating/ChunkDesc.h" +#include "CraftingRecipes.h" +#include "UI/Window.h" +#include "LuaWindow.h" +#include "Mobs/Monster.h" + +/* function to release collected object via destructor */ +#ifdef __cplusplus + +static int tolua_collect_sWebAdminPage (lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cBoundingBox (lua_State* tolua_S) +{ + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cItem (lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_Vector3f (lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cIniFile (lua_State* tolua_S) +{ + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cPickup (lua_State* tolua_S) +{ + cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cItems (lua_State* tolua_S) +{ + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cBlockArea (lua_State* tolua_S) +{ + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cTracer (lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cCraftingGrid (lua_State* tolua_S) +{ + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cCuboid (lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cBlockEntity (lua_State* tolua_S) +{ + cBlockEntity* self = (cBlockEntity*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_Vector3i (lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cEnchantments (lua_State* tolua_S) +{ + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cLuaWindow (lua_State* tolua_S) +{ + cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_Vector3d (lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} +#endif + + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"cThrownEnderPearlEntity"); + tolua_usertype(tolua_S,"cFurnaceEntity"); + tolua_usertype(tolua_S,"cEntity"); + tolua_usertype(tolua_S,"cExpBottleEntity"); + tolua_usertype(tolua_S,"cEnchantments"); + tolua_usertype(tolua_S,"cMonster"); + tolua_usertype(tolua_S,"cPluginLua"); + tolua_usertype(tolua_S,"cRoot"); + tolua_usertype(tolua_S,"std::vector<cIniFile::key>"); + tolua_usertype(tolua_S,"cPickup"); + tolua_usertype(tolua_S,"sWebAdminPage"); + tolua_usertype(tolua_S,"cFireChargeEntity"); + tolua_usertype(tolua_S,"cClientHandle"); + tolua_usertype(tolua_S,"cChunkDesc"); + tolua_usertype(tolua_S,"cPluginManager"); + tolua_usertype(tolua_S,"Vector3f"); + tolua_usertype(tolua_S,"cCraftingRecipes"); + tolua_usertype(tolua_S,"cJukeboxEntity"); + tolua_usertype(tolua_S,"cChestEntity"); + tolua_usertype(tolua_S,"cDispenserEntity"); + tolua_usertype(tolua_S,"cGhastFireballEntity"); + tolua_usertype(tolua_S,"cLineBlockTracer"); + tolua_usertype(tolua_S,"cListeners"); + tolua_usertype(tolua_S,"cThrownSnowballEntity"); + tolua_usertype(tolua_S,"cFireworkEntity"); + tolua_usertype(tolua_S,"TakeDamageInfo"); + tolua_usertype(tolua_S,"cCraftingRecipe"); + tolua_usertype(tolua_S,"cPlugin"); + tolua_usertype(tolua_S,"cItemGrid"); + tolua_usertype(tolua_S,"cHTTPServer::cCallbacks"); + tolua_usertype(tolua_S,"cLuaWindow"); + tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"cHopperEntity"); + tolua_usertype(tolua_S,"std::vector<AString>"); + tolua_usertype(tolua_S,"cBlockEntityWithItems"); + tolua_usertype(tolua_S,"cWindow"); + tolua_usertype(tolua_S,"cCraftingGrid"); + tolua_usertype(tolua_S,"cWorld"); + tolua_usertype(tolua_S,"cBlockArea"); + tolua_usertype(tolua_S,"cItem"); + tolua_usertype(tolua_S,"cGroup"); + tolua_usertype(tolua_S,"cArrowEntity"); + tolua_usertype(tolua_S,"cDropSpenserEntity"); + tolua_usertype(tolua_S,"cTracer"); + tolua_usertype(tolua_S,"cBoundingBox"); + tolua_usertype(tolua_S,"cCuboid"); + tolua_usertype(tolua_S,"cNoteEntity"); + tolua_usertype(tolua_S,"Vector3i"); + tolua_usertype(tolua_S,"cBlockEntity"); + tolua_usertype(tolua_S,"cCriticalSection"); + tolua_usertype(tolua_S,"HTTPTemplateRequest"); + tolua_usertype(tolua_S,"Vector3d"); + tolua_usertype(tolua_S,"cFile"); + tolua_usertype(tolua_S,"cItems"); + tolua_usertype(tolua_S,"cWebPlugin"); + tolua_usertype(tolua_S,"cWebAdmin"); + tolua_usertype(tolua_S,"cChatColor"); + tolua_usertype(tolua_S,"cIniFile"); + tolua_usertype(tolua_S,"HTTPRequest"); + tolua_usertype(tolua_S,"HTTPFormData"); + tolua_usertype(tolua_S,"cPawn"); + tolua_usertype(tolua_S,"cPlayer"); + tolua_usertype(tolua_S,"cGroupManager"); + tolua_usertype(tolua_S,"cSignEntity"); + tolua_usertype(tolua_S,"cItemGrid::cListener"); + tolua_usertype(tolua_S,"cProjectileEntity"); + tolua_usertype(tolua_S,"cDropperEntity"); + tolua_usertype(tolua_S,"cInventory"); + tolua_usertype(tolua_S,"cThrownEggEntity"); +} + +/* method: new of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00 +static int tolua_AllToLua_cIniFile_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new00_local +static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CaseSensitive of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseSensitive00 +static int tolua_AllToLua_cIniFile_CaseSensitive00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseSensitive'", NULL); +#endif + { + self->CaseSensitive(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CaseSensitive'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CaseInsensitive of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_CaseInsensitive00 +static int tolua_AllToLua_cIniFile_CaseInsensitive00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CaseInsensitive'", NULL); +#endif + { + self->CaseInsensitive(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CaseInsensitive'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReadFile of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_ReadFile00 +static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,3,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->ReadFile(a_FileName,a_AllowExampleRedirect); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReadFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WriteFile of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_WriteFile00 +static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->WriteFile(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WriteFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Clear00 +static int tolua_AllToLua_cIniFile_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FindKey of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindKey00 +static int tolua_AllToLua_cIniFile_FindKey00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindKey'", NULL); +#endif + { + int tolua_ret = (int) self->FindKey(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FindKey'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FindValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_FindValue00 +static int tolua_AllToLua_cIniFile_FindValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindValue'", NULL); +#endif + { + int tolua_ret = (int) self->FindValue(keyID,valuename); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FindValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumKeys of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeys00 +static int tolua_AllToLua_cIniFile_GetNumKeys00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeys'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumKeys(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumKeys'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddKeyName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyName00 +static int tolua_AllToLua_cIniFile_AddKeyName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyName'", NULL); +#endif + { + int tolua_ret = (int) self->AddKeyName(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddKeyName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKeyName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyName00 +static int tolua_AllToLua_cIniFile_GetKeyName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetKeyName(keyID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetKeyName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumValues of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues00 +static int tolua_AllToLua_cIniFile_GetNumValues00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumValues(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumValues'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumValues of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumValues01 +static int tolua_AllToLua_cIniFile_GetNumValues01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumValues'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumValues(keyID); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetNumValues00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName00 +static int tolua_AllToLua_cIniFile_GetValueName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(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 + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueName(keyname,valueID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueName of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueName01 +static int tolua_AllToLua_cIniFile_GetValueName01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",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 + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueName(keyID,valueID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValueName00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue00 +static int tolua_AllToLua_cIniFile_GetValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyname,valuename); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue01 +static int tolua_AllToLua_cIniFile_GetValue01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyname,valuename,defValue); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + tolua_pushcppstring(tolua_S,(const char*)defValue); + } + } + return 4; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValue00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue02 +static int tolua_AllToLua_cIniFile_GetValue02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",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 + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyID,valueID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValue01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValue03 +static int tolua_AllToLua_cIniFile_GetValue03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); + const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValue'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValue(keyID,valueID,defValue); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)defValue); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValue02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueF of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueF00 +static int tolua_AllToLua_cIniFile_GetValueF00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const double defValue = ((const double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueF'", NULL); +#endif + { + double tolua_ret = (double) self->GetValueF(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueF'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueI of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueI00 +static int tolua_AllToLua_cIniFile_GetValueI00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const int defValue = ((const int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueI'", NULL); +#endif + { + int tolua_ret = (int) self->GetValueI(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueI'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueB of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueB00 +static int tolua_AllToLua_cIniFile_GetValueB00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueB'", NULL); +#endif + { + bool tolua_ret = (bool) self->GetValueB(keyname,valuename,defValue); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueB'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSet of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet00 +static int tolua_AllToLua_cIniFile_GetValueSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueSet(keyname,valuename); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSet of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSet01 +static int tolua_AllToLua_cIniFile_GetValueSet01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString defValue = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSet'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetValueSet(keyname,valuename,defValue); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + tolua_pushcppstring(tolua_S,(const char*)defValue); + } + } + return 4; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetValueSet00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSetF of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetF00 +static int tolua_AllToLua_cIniFile_GetValueSetF00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const double defValue = ((const double) tolua_tonumber(tolua_S,4,0.0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetF'", NULL); +#endif + { + double tolua_ret = (double) self->GetValueSetF(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSetF'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSetI of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetI00 +static int tolua_AllToLua_cIniFile_GetValueSetI00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const int defValue = ((const int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetI'", NULL); +#endif + { + int tolua_ret = (int) self->GetValueSetI(keyname,valuename,defValue); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSetI'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetValueSetB of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetValueSetB00 +static int tolua_AllToLua_cIniFile_GetValueSetB00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const bool defValue = ((const bool) tolua_toboolean(tolua_S,4,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetValueSetB'", NULL); +#endif + { + bool tolua_ret = (bool) self->GetValueSetB(keyname,valuename,defValue); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetValueSetB'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue00 +static int tolua_AllToLua_cIniFile_SetValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); + const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValue(keyID,valueID,value); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)value); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValue01 +static int tolua_AllToLua_cIniFile_SetValue01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString value = ((const AString) tolua_tocppstring(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValue'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValue(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + tolua_pushcppstring(tolua_S,(const char*)value); + } + } + return 4; +tolua_lerror: + return tolua_AllToLua_cIniFile_SetValue00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValueI of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueI00 +static int tolua_AllToLua_cIniFile_SetValueI00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const int value = ((const int) tolua_tonumber(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueI'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValueI(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValueI'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValueB of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueB00 +static int tolua_AllToLua_cIniFile_SetValueB00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const bool value = ((const bool) tolua_toboolean(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueB'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValueB(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValueB'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetValueF of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_SetValueF00 +static int tolua_AllToLua_cIniFile_SetValueF00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const double value = ((const double) tolua_tonumber(tolua_S,4,0)); + const bool create = ((const bool) tolua_toboolean(tolua_S,5,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetValueF'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetValueF(keyname,valuename,value,create); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetValueF'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteValueByID of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValueByID00 +static int tolua_AllToLua_cIniFile_DeleteValueByID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",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 + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int valueID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValueByID'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteValueByID(keyID,valueID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteValueByID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteValue of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteValue00 +static int tolua_AllToLua_cIniFile_DeleteValue00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString valuename = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteValue'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteValue(keyname,valuename); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)valuename); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteValue'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKey of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKey00 +static int tolua_AllToLua_cIniFile_DeleteKey00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKey'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKey(keyname); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteKey'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumHeaderComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumHeaderComments00 +static int tolua_AllToLua_cIniFile_GetNumHeaderComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumHeaderComments'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumHeaderComments(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumHeaderComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddHeaderComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddHeaderComment00 +static int tolua_AllToLua_cIniFile_AddHeaderComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString comment = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddHeaderComment'", NULL); +#endif + { + self->AddHeaderComment(comment); + tolua_pushcppstring(tolua_S,(const char*)comment); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddHeaderComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeaderComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetHeaderComment00 +static int tolua_AllToLua_cIniFile_GetHeaderComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int commentID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeaderComment'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetHeaderComment(commentID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeaderComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteHeaderComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComment00 +static int tolua_AllToLua_cIniFile_DeleteHeaderComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + int commentID = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteHeaderComment(commentID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteHeaderComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteHeaderComments00 +static int tolua_AllToLua_cIniFile_DeleteHeaderComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteHeaderComments'", NULL); +#endif + { + self->DeleteHeaderComments(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteHeaderComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments00 +static int tolua_AllToLua_cIniFile_GetNumKeyComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumKeyComments(keyID); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumKeyComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetNumKeyComments01 +static int tolua_AllToLua_cIniFile_GetNumKeyComments01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumKeyComments'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumKeyComments(keyname); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetNumKeyComments00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment00 +static int tolua_AllToLua_cIniFile_AddKeyComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->AddKeyComment(keyID,comment); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)comment); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddKeyComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_AddKeyComment01 +static int tolua_AllToLua_cIniFile_AddKeyComment01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString comment = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->AddKeyComment(keyname,comment); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + tolua_pushcppstring(tolua_S,(const char*)comment); + } + } + return 3; +tolua_lerror: + return tolua_AllToLua_cIniFile_AddKeyComment00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment00 +static int tolua_AllToLua_cIniFile_GetKeyComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",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 + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetKeyComment(keyID,commentID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetKeyComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_GetKeyComment01 +static int tolua_AllToLua_cIniFile_GetKeyComment01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cIniFile",0,&tolua_err) || + !tolua_iscppstring(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 + { + const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKeyComment'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetKeyComment(keyname,commentID); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_GetKeyComment00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment00 +static int tolua_AllToLua_cIniFile_DeleteKeyComment00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",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 + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComment(keyID,commentID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteKeyComment'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComment of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComment01 +static int tolua_AllToLua_cIniFile_DeleteKeyComment01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(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 + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const int commentID = ((const int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComment'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComment(keyname,commentID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_DeleteKeyComment00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments00 +static int tolua_AllToLua_cIniFile_DeleteKeyComments00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const int keyID = ((const int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComments(keyID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeleteKeyComments'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeleteKeyComments of class cIniFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_DeleteKeyComments01 +static int tolua_AllToLua_cIniFile_DeleteKeyComments01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + const AString keyname = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeleteKeyComments'", NULL); +#endif + { + bool tolua_ret = (bool) self->DeleteKeyComments(keyname); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)keyname); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cIniFile_DeleteKeyComments00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Exists of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Exists00 +static int tolua_AllToLua_cFile_Exists00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Exists(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Exists'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Delete of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Delete00 +static int tolua_AllToLua_cFile_Delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::Delete(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Rename of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Rename00 +static int tolua_AllToLua_cFile_Rename00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_OrigPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_NewPath = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Rename(a_OrigPath,a_NewPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_OrigPath); + tolua_pushcppstring(tolua_S,(const char*)a_NewPath); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Rename'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Copy of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_Copy00 +static int tolua_AllToLua_cFile_Copy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_SrcFileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_DstFileName = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + bool tolua_ret = (bool) cFile::Copy(a_SrcFileName,a_DstFileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_SrcFileName); + tolua_pushcppstring(tolua_S,(const char*)a_DstFileName); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Copy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFolder00 +static int tolua_AllToLua_cFile_IsFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFolder(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFile of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_IsFile00 +static int tolua_AllToLua_cFile_IsFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Path = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::IsFile(a_Path); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Path); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSize of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_GetSize00 +static int tolua_AllToLua_cFile_GetSize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) cFile::GetSize(a_FileName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CreateFolder of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_CreateFolder00 +static int tolua_AllToLua_cFile_CreateFolder00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FolderPath = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + bool tolua_ret = (bool) cFile::CreateFolder(a_FolderPath); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FolderPath); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CreateFolder'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReadWholeFile of class cFile */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFile_ReadWholeFile00 +static int tolua_AllToLua_cFile_ReadWholeFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cFile",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + AString tolua_ret = (AString) cFile::ReadWholeFile(a_FileName); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReadWholeFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: BlockStringToType */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_BlockStringToType00 +static int tolua_AllToLua_BlockStringToType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_BlockTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + unsigned char tolua_ret = ( unsigned char) BlockStringToType(a_BlockTypeString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_BlockTypeString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BlockStringToType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToItem00 +static int tolua_AllToLua_StringToItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_ItemTypeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + cItem* a_Item = ((cItem*) tolua_tousertype(tolua_S,2,0)); + { + bool tolua_ret = (bool) StringToItem(a_ItemTypeString,*a_Item); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_ItemTypeString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToString00 +static int tolua_AllToLua_ItemToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0)); + { + AString tolua_ret = (AString) ItemToString(*a_Item); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ItemToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemTypeToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemTypeToString00 +static int tolua_AllToLua_ItemTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + AString tolua_ret = (AString) ItemTypeToString(a_ItemType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ItemTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemToFullString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemToFullString00 +static int tolua_AllToLua_ItemToFullString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,1,0)); + { + AString tolua_ret = (AString) ItemToFullString(*a_Item); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ItemToFullString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToBiome */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToBiome00 +static int tolua_AllToLua_StringToBiome00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_BiomeString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + EMCSBiome tolua_ret = (EMCSBiome) StringToBiome(a_BiomeString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_BiomeString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToMobType */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToMobType00 +static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_MobString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + int tolua_ret = (int) StringToMobType(a_MobString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_MobString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToDimension */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDimension00 +static int tolua_AllToLua_StringToDimension00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_DimensionString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + eDimension tolua_ret = (eDimension) StringToDimension(a_DimensionString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_DimensionString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToDimension'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DamageTypeToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_DamageTypeToString00 +static int tolua_AllToLua_DamageTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,1,0)); + { + AString tolua_ret = (AString) DamageTypeToString(a_DamageType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StringToDamageType */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StringToDamageType00 +static int tolua_AllToLua_StringToDamageType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_DamageString = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + eDamageType tolua_ret = (eDamageType) StringToDamageType(a_DamageString); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_DamageString); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToDamageType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetIniItemSet */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetIniItemSet00 +static int tolua_AllToLua_GetIniItemSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + (tolua_isvaluenil(tolua_S,1,&tolua_err) || !tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err)) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isstring(tolua_S,3,0,&tolua_err) || + !tolua_isstring(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cIniFile* a_IniFile = ((cIniFile*) tolua_tousertype(tolua_S,1,0)); + const char* a_Section = ((const char*) tolua_tostring(tolua_S,2,0)); + const char* a_Key = ((const char*) tolua_tostring(tolua_S,3,0)); + const char* a_Default = ((const char*) tolua_tostring(tolua_S,4,0)); + { + cItem tolua_ret = (cItem) GetIniItemSet(*a_IniFile,a_Section,a_Key,a_Default); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIniItemSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: TrimString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_TrimString00 +static int tolua_AllToLua_TrimString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString str = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + AString tolua_ret = (AString) TrimString(str); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)str); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TrimString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: NoCaseCompare */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_NoCaseCompare00 +static int tolua_AllToLua_NoCaseCompare00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString s1 = ((const AString) tolua_tocppstring(tolua_S,1,0)); + const AString s2 = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) NoCaseCompare(s1,s2); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)s1); + tolua_pushcppstring(tolua_S,(const char*)s2); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'NoCaseCompare'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ReplaceString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ReplaceString00 +static int tolua_AllToLua_ReplaceString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + AString iHayStack = ((AString) tolua_tocppstring(tolua_S,1,0)); + const AString iNeedle = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString iReplaceWith = ((const AString) tolua_tocppstring(tolua_S,3,0)); + { + ReplaceString(iHayStack,iNeedle,iReplaceWith); + tolua_pushcppstring(tolua_S,(const char*)iHayStack); + tolua_pushcppstring(tolua_S,(const char*)iNeedle); + tolua_pushcppstring(tolua_S,(const char*)iReplaceWith); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReplaceString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: EscapeString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_EscapeString00 +static int tolua_AllToLua_EscapeString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + AString tolua_ret = (AString) EscapeString(a_Message); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'EscapeString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: StripColorCodes */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_StripColorCodes00 +static int tolua_AllToLua_StripColorCodes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,1,0)); + { + AString tolua_ret = (AString) StripColorCodes(a_Message); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StripColorCodes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockLightValue */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockLightValue +static int tolua_get_AllToLua_g_BlockLightValue(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)g_BlockLightValue[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockLightValue */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockLightValue +static int tolua_set_AllToLua_g_BlockLightValue(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockLightValue[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockSpreadLightFalloff */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockSpreadLightFalloff +static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)g_BlockSpreadLightFalloff[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockSpreadLightFalloff */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockSpreadLightFalloff +static int tolua_set_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockSpreadLightFalloff[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockTransparent */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockTransparent +static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockTransparent[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockTransparent */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockTransparent +static int tolua_set_AllToLua_g_BlockTransparent(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockTransparent[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockOneHitDig */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockOneHitDig +static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockOneHitDig[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockOneHitDig */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockOneHitDig +static int tolua_set_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockOneHitDig[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockPistonBreakable */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockPistonBreakable +static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockPistonBreakable[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockPistonBreakable */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockPistonBreakable +static int tolua_set_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockPistonBreakable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockIsSnowable */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSnowable +static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockIsSnowable[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockIsSnowable */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSnowable +static int tolua_set_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockIsSnowable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockRequiresSpecialTool */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockRequiresSpecialTool +static int tolua_get_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockRequiresSpecialTool[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockRequiresSpecialTool */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockRequiresSpecialTool +static int tolua_set_AllToLua_g_BlockRequiresSpecialTool(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockRequiresSpecialTool[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockIsSolid */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsSolid +static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockIsSolid[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockIsSolid */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsSolid +static int tolua_set_AllToLua_g_BlockIsSolid(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockIsSolid[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: g_BlockIsTorchPlaceable */ +#ifndef TOLUA_DISABLE_tolua_get_AllToLua_g_BlockIsTorchPlaceable +static int tolua_get_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + tolua_pushboolean(tolua_S,(bool)g_BlockIsTorchPlaceable[tolua_index]); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: g_BlockIsTorchPlaceable */ +#ifndef TOLUA_DISABLE_tolua_set_AllToLua_g_BlockIsTorchPlaceable +static int tolua_set_AllToLua_g_BlockIsTorchPlaceable(lua_State* tolua_S) +{ + int tolua_index; +#ifndef TOLUA_RELEASE + { + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in array indexing.",&tolua_err); + } +#endif + tolua_index = (int)tolua_tonumber(tolua_S,2,0); +#ifndef TOLUA_RELEASE + if (tolua_index<0 || tolua_index>=256) + tolua_error(tolua_S,"array indexing out of range.",NULL); +#endif + g_BlockIsTorchPlaceable[tolua_index] = ((bool) tolua_toboolean(tolua_S,3,0)); + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ClickActionToString */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ClickActionToString00 +static int tolua_AllToLua_ClickActionToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + eClickAction a_ClickAction = ((eClickAction) (int) tolua_tonumber(tolua_S,1,0)); + { + const char* tolua_ret = (const char*) ClickActionToString(a_ClickAction); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ClickActionToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: IsValidBlock */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidBlock00 +static int tolua_AllToLua_IsValidBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_BlockType = ((int) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) IsValidBlock(a_BlockType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsValidBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: IsValidItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_IsValidItem00 +static int tolua_AllToLua_IsValidItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_ItemType = ((int) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) IsValidItem(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsValidItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: AddFaceDirection */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_AddFaceDirection00 +static int tolua_AllToLua_AddFaceDirection00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_BlockX = ((int) tolua_tonumber(tolua_S,1,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); + char a_BlockFace = ((char) tolua_tonumber(tolua_S,4,0)); + bool a_bInverse = ((bool) tolua_toboolean(tolua_S,5,false)); + { + AddFaceDirection(a_BlockX,a_BlockY,a_BlockZ,a_BlockFace,a_bInverse); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockX); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockY); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddFaceDirection'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsPickaxe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsPickaxe00 +static int tolua_AllToLua_ItemCategory_IsPickaxe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsPickaxe(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPickaxe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsAxe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsAxe00 +static int tolua_AllToLua_ItemCategory_IsAxe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsAxe(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsAxe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsSword */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsSword00 +static int tolua_AllToLua_ItemCategory_IsSword00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsSword(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSword'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsHoe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHoe00 +static int tolua_AllToLua_ItemCategory_IsHoe00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsHoe(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsHoe'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsShovel */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsShovel00 +static int tolua_AllToLua_ItemCategory_IsShovel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsShovel(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsShovel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsTool */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsTool00 +static int tolua_AllToLua_ItemCategory_IsTool00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemID = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsTool(a_ItemID); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsTool'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsHelmet */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsHelmet00 +static int tolua_AllToLua_ItemCategory_IsHelmet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsHelmet(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsHelmet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsChestPlate */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsChestPlate00 +static int tolua_AllToLua_ItemCategory_IsChestPlate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsChestPlate(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsChestPlate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsLeggings */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsLeggings00 +static int tolua_AllToLua_ItemCategory_IsLeggings00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsLeggings(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsLeggings'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsBoots */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsBoots00 +static int tolua_AllToLua_ItemCategory_IsBoots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsBoots(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBoots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ItemCategory::IsArmor */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_ItemCategory_IsArmor00 +static int tolua_AllToLua_ItemCategory_IsArmor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,1,0)); + { + bool tolua_ret = (bool) ItemCategory::IsArmor(a_ItemType); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsArmor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetTime */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetTime00 +static int tolua_AllToLua_GetTime00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnoobj(tolua_S,1,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + unsigned int tolua_ret = (unsigned int) GetTime(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTime'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetChar */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_GetChar00 +static int tolua_AllToLua_GetChar00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + std::string a_Str = ((std::string) tolua_tocppstring(tolua_S,1,0)); + unsigned int a_Idx = ((unsigned int) tolua_tonumber(tolua_S,2,0)); + { + std::string tolua_ret = (std::string) GetChar(a_Str,a_Idx); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Str); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChar'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Color of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Color +static int tolua_get_cChatColor_Color(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Color); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Delimiter of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Delimiter +static int tolua_get_cChatColor_Delimiter(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Delimiter); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Black of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Black +static int tolua_get_cChatColor_Black(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Black); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Navy of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Navy +static int tolua_get_cChatColor_Navy(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Navy); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Green of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Green +static int tolua_get_cChatColor_Green(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Green); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Blue of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Blue +static int tolua_get_cChatColor_Blue(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Blue); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Red of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Red +static int tolua_get_cChatColor_Red(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Red); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Purple of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Purple +static int tolua_get_cChatColor_Purple(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Purple); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Gold of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gold +static int tolua_get_cChatColor_Gold(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gold); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightGray of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGray +static int tolua_get_cChatColor_LightGray(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGray); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Gray of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Gray +static int tolua_get_cChatColor_Gray(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Gray); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: DarkPurple of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_DarkPurple +static int tolua_get_cChatColor_DarkPurple(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::DarkPurple); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightGreen of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightGreen +static int tolua_get_cChatColor_LightGreen(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightGreen); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightBlue of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightBlue +static int tolua_get_cChatColor_LightBlue(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightBlue); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Rose of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Rose +static int tolua_get_cChatColor_Rose(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Rose); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: LightPurple of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_LightPurple +static int tolua_get_cChatColor_LightPurple(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::LightPurple); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Yellow of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Yellow +static int tolua_get_cChatColor_Yellow(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Yellow); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: White of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_White +static int tolua_get_cChatColor_White(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::White); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Random of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Random +static int tolua_get_cChatColor_Random(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Random); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Bold of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Bold +static int tolua_get_cChatColor_Bold(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Bold); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Strikethrough of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Strikethrough +static int tolua_get_cChatColor_Strikethrough(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Strikethrough); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Underlined of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Underlined +static int tolua_get_cChatColor_Underlined(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Underlined); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Italic of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Italic +static int tolua_get_cChatColor_Italic(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Italic); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Plain of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Plain +static int tolua_get_cChatColor_Plain(lua_State* tolua_S) +{ + tolua_pushcppstring(tolua_S,(const char*)cChatColor::Plain); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MakeColor of class cChatColor */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChatColor_MakeColor00 +static int tolua_AllToLua_cChatColor_MakeColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cChatColor",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + char a_Color = ((char) tolua_tonumber(tolua_S,2,0)); + { + const std::string tolua_ret = (const std::string) cChatColor::MakeColor(a_Color); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPlayer of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPlayer00 +static int tolua_AllToLua_cClientHandle_GetPlayer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlayer'", NULL); +#endif + { + cPlayer* tolua_ret = (cPlayer*) self->GetPlayer(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlayer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPlayer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Kick of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_Kick00 +static int tolua_AllToLua_cClientHandle_Kick00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + const AString a_Reason = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Kick'", NULL); +#endif + { + self->Kick(a_Reason); + tolua_pushcppstring(tolua_S,(const char*)a_Reason); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Kick'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SendBlockChange of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SendBlockChange00 +static int tolua_AllToLua_cClientHandle_SendBlockChange00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockChange'", NULL); +#endif + { + self->SendBlockChange(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SendBlockChange'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetUsername of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUsername00 +static int tolua_AllToLua_cClientHandle_GetUsername00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUsername'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetUsername(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetUsername'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUsername of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetUsername00 +static int tolua_AllToLua_cClientHandle_SetUsername00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + const AString a_Username = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUsername'", NULL); +#endif + { + self->SetUsername(a_Username); + tolua_pushcppstring(tolua_S,(const char*)a_Username); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUsername'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPing of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetPing00 +static int tolua_AllToLua_cClientHandle_GetPing00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPing'", NULL); +#endif + { + short tolua_ret = (short) self->GetPing(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPing'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetViewDistance of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_SetViewDistance00 +static int tolua_AllToLua_cClientHandle_SetViewDistance00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0); + int a_ViewDistance = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetViewDistance'", NULL); +#endif + { + self->SetViewDistance(a_ViewDistance); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetViewDistance'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetViewDistance of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetViewDistance00 +static int tolua_AllToLua_cClientHandle_GetViewDistance00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetViewDistance'", NULL); +#endif + { + int tolua_ret = (int) self->GetViewDistance(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetViewDistance'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetUniqueID of class cClientHandle */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUniqueID00 +static int tolua_AllToLua_cClientHandle_GetUniqueID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cClientHandle",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cClientHandle* self = (const cClientHandle*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); +#endif + { + int tolua_ret = (int) self->GetUniqueID(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: DamageType of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_DamageType +static int tolua_get_TakeDamageInfo_DamageType(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->DamageType); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: DamageType of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_DamageType +static int tolua_set_TakeDamageInfo_DamageType(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'DamageType'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Attacker of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Attacker_ptr +static int tolua_get_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)self->Attacker,"cEntity"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Attacker of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Attacker_ptr +static int tolua_set_TakeDamageInfo_Attacker_ptr(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Attacker'",NULL); + if (!tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: RawDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_RawDamage +static int tolua_get_TakeDamageInfo_RawDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->RawDamage); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: RawDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_RawDamage +static int tolua_set_TakeDamageInfo_RawDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RawDamage'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->RawDamage = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: FinalDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_FinalDamage +static int tolua_get_TakeDamageInfo_FinalDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->FinalDamage); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: FinalDamage of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_FinalDamage +static int tolua_set_TakeDamageInfo_FinalDamage(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'FinalDamage'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->FinalDamage = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Knockback of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_get_TakeDamageInfo_Knockback +static int tolua_get_TakeDamageInfo_Knockback(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->Knockback,"Vector3d"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Knockback of class TakeDamageInfo */ +#ifndef TOLUA_DISABLE_tolua_set_TakeDamageInfo_Knockback +static int tolua_set_TakeDamageInfo_Knockback(lua_State* tolua_S) +{ + TakeDamageInfo* self = (TakeDamageInfo*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Knockback'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Knockback = *((Vector3d*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEntityType of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEntityType00 +static int tolua_AllToLua_cEntity_GetEntityType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEntityType'", NULL); +#endif + { + cEntity::eEntityType tolua_ret = (cEntity::eEntityType) self->GetEntityType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEntityType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPlayer of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPlayer00 +static int tolua_AllToLua_cEntity_IsPlayer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayer'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPlayer(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPlayer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPickup of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsPickup00 +static int tolua_AllToLua_cEntity_IsPickup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPickup'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPickup(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPickup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsMob of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMob00 +static int tolua_AllToLua_cEntity_IsMob00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMob'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsMob(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsMob'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFallingBlock of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsFallingBlock00 +static int tolua_AllToLua_cEntity_IsFallingBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFallingBlock'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsFallingBlock(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFallingBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsMinecart of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsMinecart00 +static int tolua_AllToLua_cEntity_IsMinecart00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsMinecart'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsMinecart(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsMinecart'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsBoat of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsBoat00 +static int tolua_AllToLua_cEntity_IsBoat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBoat'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsBoat(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBoat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsTNT of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsTNT00 +static int tolua_AllToLua_cEntity_IsTNT00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsTNT'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsTNT(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsTNT'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsProjectile of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsProjectile00 +static int tolua_AllToLua_cEntity_IsProjectile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsProjectile'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsProjectile(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsProjectile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsA of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsA00 +static int tolua_AllToLua_cEntity_IsA00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); + const char* a_ClassName = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsA'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsA(a_ClassName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsA'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetClass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClass00 +static int tolua_AllToLua_cEntity_GetClass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClass'", NULL); +#endif + { + const char* tolua_ret = (const char*) self->GetClass(); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetClass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetClassStatic of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetClassStatic00 +static int tolua_AllToLua_cEntity_GetClassStatic00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + const char* tolua_ret = (const char*) cEntity::GetClassStatic(); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetClassStatic'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetParentClass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetParentClass00 +static int tolua_AllToLua_cEntity_GetParentClass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetParentClass'", NULL); +#endif + { + const char* tolua_ret = (const char*) self->GetParentClass(); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetParentClass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorld of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWorld00 +static int tolua_AllToLua_cEntity_GetWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetWorld(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeadYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeadYaw00 +static int tolua_AllToLua_cEntity_GetHeadYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeadYaw'", NULL); +#endif + { + double tolua_ret = (double) self->GetHeadYaw(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeadYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHeight00 +static int tolua_AllToLua_cEntity_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + double tolua_ret = (double) self->GetHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMass00 +static int tolua_AllToLua_cEntity_GetMass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMass'", NULL); +#endif + { + double tolua_ret = (double) self->GetMass(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosition00 +static int tolua_AllToLua_cEntity_GetPosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosition'", NULL); +#endif + { + const Vector3d& tolua_ret = (const Vector3d&) self->GetPosition(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosX00 +static int tolua_AllToLua_cEntity_GetPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); +#endif + { + double tolua_ret = (double) self->GetPosX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosY00 +static int tolua_AllToLua_cEntity_GetPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); +#endif + { + double tolua_ret = (double) self->GetPosY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPosZ00 +static int tolua_AllToLua_cEntity_GetPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); +#endif + { + double tolua_ret = (double) self->GetPosZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRot of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRot00 +static int tolua_AllToLua_cEntity_GetRot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRot'", NULL); +#endif + { + const Vector3d& tolua_ret = (const Vector3d&) self->GetRot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRotation of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRotation00 +static int tolua_AllToLua_cEntity_GetRotation00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRotation'", NULL); +#endif + { + double tolua_ret = (double) self->GetRotation(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRotation'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetYaw00 +static int tolua_AllToLua_cEntity_GetYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetYaw'", NULL); +#endif + { + double tolua_ret = (double) self->GetYaw(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPitch of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetPitch00 +static int tolua_AllToLua_cEntity_GetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); +#endif + { + double tolua_ret = (double) self->GetPitch(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRoll of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRoll00 +static int tolua_AllToLua_cEntity_GetRoll00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRoll'", NULL); +#endif + { + double tolua_ret = (double) self->GetRoll(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRoll'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLookVector of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetLookVector00 +static int tolua_AllToLua_cEntity_GetLookVector00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLookVector'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetLookVector(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLookVector'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeed00 +static int tolua_AllToLua_cEntity_GetSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeed'", NULL); +#endif + { + const Vector3d& tolua_ret = (const Vector3d&) self->GetSpeed(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeedX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedX00 +static int tolua_AllToLua_cEntity_GetSpeedX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedX'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpeedX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeedX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeedY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedY00 +static int tolua_AllToLua_cEntity_GetSpeedY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedY'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpeedY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeedY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpeedZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetSpeedZ00 +static int tolua_AllToLua_cEntity_GetSpeedZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSpeedZ'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpeedZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpeedZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWidth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetWidth00 +static int tolua_AllToLua_cEntity_GetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); +#endif + { + double tolua_ret = (double) self->GetWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkX00 +static int tolua_AllToLua_cEntity_GetChunkX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkZ00 +static int tolua_AllToLua_cEntity_GetChunkZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHeadYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeadYaw00 +static int tolua_AllToLua_cEntity_SetHeadYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_HeadYaw = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeadYaw'", NULL); +#endif + { + self->SetHeadYaw(a_HeadYaw); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHeadYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHeight of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHeight00 +static int tolua_AllToLua_cEntity_SetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Height = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL); +#endif + { + self->SetHeight(a_Height); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetMass of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMass00 +static int tolua_AllToLua_cEntity_SetMass00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Mass = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMass'", NULL); +#endif + { + self->SetMass(a_Mass); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetMass'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosX00 +static int tolua_AllToLua_cEntity_SetPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosX'", NULL); +#endif + { + self->SetPosX(a_PosX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosY00 +static int tolua_AllToLua_cEntity_SetPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosY'", NULL); +#endif + { + self->SetPosY(a_PosY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosZ00 +static int tolua_AllToLua_cEntity_SetPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosZ'", NULL); +#endif + { + self->SetPosZ(a_PosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition00 +static int tolua_AllToLua_cEntity_SetPosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL); +#endif + { + self->SetPosition(a_PosX,a_PosY,a_PosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosition01 +static int tolua_AllToLua_cEntity_SetPosition01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPosition'", NULL); +#endif + { + self->SetPosition(*a_Pos); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_SetPosition00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRot of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRot00 +static int tolua_AllToLua_cEntity_SetRot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_Rot = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRot'", NULL); +#endif + { + self->SetRot(*a_Rot); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRotation of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotation00 +static int tolua_AllToLua_cEntity_SetRotation00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Rotation = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotation'", NULL); +#endif + { + self->SetRotation(a_Rotation); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRotation'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetYaw of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetYaw00 +static int tolua_AllToLua_cEntity_SetYaw00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Yaw = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetYaw'", NULL); +#endif + { + self->SetYaw(a_Yaw); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetYaw'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPitch of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitch00 +static int tolua_AllToLua_cEntity_SetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Pitch = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL); +#endif + { + self->SetPitch(a_Pitch); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRoll of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRoll00 +static int tolua_AllToLua_cEntity_SetRoll00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Roll = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRoll'", NULL); +#endif + { + self->SetRoll(a_Roll); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRoll'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed00 +static int tolua_AllToLua_cEntity_SetSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_SpeedY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_SpeedZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL); +#endif + { + self->SetSpeed(a_SpeedX,a_SpeedY,a_SpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeed01 +static int tolua_AllToLua_cEntity_SetSpeed01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeed'", NULL); +#endif + { + self->SetSpeed(*a_Speed); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_SetSpeed00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeedX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedX00 +static int tolua_AllToLua_cEntity_SetSpeedX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedX'", NULL); +#endif + { + self->SetSpeedX(a_SpeedX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeedX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeedY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedY00 +static int tolua_AllToLua_cEntity_SetSpeedY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedY'", NULL); +#endif + { + self->SetSpeedY(a_SpeedY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeedY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSpeedZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetSpeedZ00 +static int tolua_AllToLua_cEntity_SetSpeedZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_SpeedZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSpeedZ'", NULL); +#endif + { + self->SetSpeedZ(a_SpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSpeedZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetWidth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetWidth00 +static int tolua_AllToLua_cEntity_SetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_Width = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWidth'", NULL); +#endif + { + self->SetWidth(a_Width); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosX00 +static int tolua_AllToLua_cEntity_AddPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosX'", NULL); +#endif + { + self->AddPosX(a_AddPosX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosY00 +static int tolua_AllToLua_cEntity_AddPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosY'", NULL); +#endif + { + self->AddPosY(a_AddPosY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosZ00 +static int tolua_AllToLua_cEntity_AddPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosZ'", NULL); +#endif + { + self->AddPosZ(a_AddPosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition00 +static int tolua_AllToLua_cEntity_AddPosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddPosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_AddPosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_AddPosZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL); +#endif + { + self->AddPosition(a_AddPosX,a_AddPosY,a_AddPosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPosition of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddPosition01 +static int tolua_AllToLua_cEntity_AddPosition01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_AddPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPosition'", NULL); +#endif + { + self->AddPosition(*a_AddPos); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_AddPosition00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed00 +static int tolua_AllToLua_cEntity_AddSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL); +#endif + { + self->AddSpeed(a_AddSpeedX,a_AddSpeedY,a_AddSpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeed01 +static int tolua_AllToLua_cEntity_AddSpeed01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_AddSpeed = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeed'", NULL); +#endif + { + self->AddSpeed(*a_AddSpeed); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_AddSpeed00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeedX of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedX00 +static int tolua_AllToLua_cEntity_AddSpeedX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedX = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedX'", NULL); +#endif + { + self->AddSpeedX(a_AddSpeedX); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeedX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeedY of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedY00 +static int tolua_AllToLua_cEntity_AddSpeedY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedY = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedY'", NULL); +#endif + { + self->AddSpeedY(a_AddSpeedY); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeedY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddSpeedZ of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_AddSpeedZ00 +static int tolua_AllToLua_cEntity_AddSpeedZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_AddSpeedZ = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddSpeedZ'", NULL); +#endif + { + self->AddSpeedZ(a_AddSpeedZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddSpeedZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SteerVehicle of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SteerVehicle00 +static int tolua_AllToLua_cEntity_SteerVehicle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",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 + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + float a_Forward = ((float) tolua_tonumber(tolua_S,2,0)); + float a_Sideways = ((float) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SteerVehicle'", NULL); +#endif + { + self->SteerVehicle(a_Forward,a_Sideways); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SteerVehicle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetUniqueID of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetUniqueID00 +static int tolua_AllToLua_cEntity_GetUniqueID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); +#endif + { + int tolua_ret = (int) self->GetUniqueID(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetUniqueID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsDestroyed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsDestroyed00 +static int tolua_AllToLua_cEntity_IsDestroyed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDestroyed'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsDestroyed(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsDestroyed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Destroy of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Destroy00 +static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL); +#endif + { + self->Destroy(a_ShouldBroadcast); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Destroy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TakeDamage of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage00 +static int tolua_AllToLua_cEntity_TakeDamage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); +#endif + { + self->TakeDamage(*a_Attacker); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TakeDamage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TakeDamage of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage01 +static int tolua_AllToLua_cEntity_TakeDamage01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)); + cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0)); + int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); + double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); +#endif + { + self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_KnockbackAmount); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_TakeDamage00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TakeDamage of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TakeDamage02 +static int tolua_AllToLua_cEntity_TakeDamage02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,3,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,2,0)); + cEntity* a_Attacker = ((cEntity*) tolua_tousertype(tolua_S,3,0)); + int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); + int a_FinalDamage = ((int) tolua_tonumber(tolua_S,5,0)); + double a_KnockbackAmount = ((double) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TakeDamage'", NULL); +#endif + { + self->TakeDamage(a_DamageType,a_Attacker,a_RawDamage,a_FinalDamage,a_KnockbackAmount); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cEntity_TakeDamage01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGravity of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetGravity00 +static int tolua_AllToLua_cEntity_GetGravity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGravity'", NULL); +#endif + { + float tolua_ret = (float) self->GetGravity(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGravity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetGravity of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetGravity00 +static int tolua_AllToLua_cEntity_SetGravity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + float a_Gravity = ((float) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGravity'", NULL); +#endif + { + self->SetGravity(a_Gravity); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetGravity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRotationFromSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetRotationFromSpeed00 +static int tolua_AllToLua_cEntity_SetRotationFromSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRotationFromSpeed'", NULL); +#endif + { + self->SetRotationFromSpeed(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRotationFromSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPitchFromSpeed of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPitchFromSpeed00 +static int tolua_AllToLua_cEntity_SetPitchFromSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitchFromSpeed'", NULL); +#endif + { + self->SetPitchFromSpeed(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPitchFromSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRawDamageAgainst of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetRawDamageAgainst00 +static int tolua_AllToLua_cEntity_GetRawDamageAgainst00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRawDamageAgainst'", NULL); +#endif + { + int tolua_ret = (int) self->GetRawDamageAgainst(*a_Receiver); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRawDamageAgainst'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetArmorCoverAgainst of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetArmorCoverAgainst00 +static int tolua_AllToLua_cEntity_GetArmorCoverAgainst00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* a_Attacker = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); + eDamageType a_DamageType = ((eDamageType) (int) tolua_tonumber(tolua_S,3,0)); + int a_RawDamage = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorCoverAgainst'", NULL); +#endif + { + int tolua_ret = (int) self->GetArmorCoverAgainst(a_Attacker,a_DamageType,a_RawDamage); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetArmorCoverAgainst'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetKnockbackAmountAgainst of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00 +static int tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* a_Receiver = ((const cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetKnockbackAmountAgainst'", NULL); +#endif + { + double tolua_ret = (double) self->GetKnockbackAmountAgainst(*a_Receiver); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetKnockbackAmountAgainst'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedWeapon of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedWeapon00 +static int tolua_AllToLua_cEntity_GetEquippedWeapon00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedWeapon'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedWeapon(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedWeapon'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedHelmet of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedHelmet00 +static int tolua_AllToLua_cEntity_GetEquippedHelmet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedHelmet(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedChestplate of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedChestplate00 +static int tolua_AllToLua_cEntity_GetEquippedChestplate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedChestplate(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedLeggings of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedLeggings00 +static int tolua_AllToLua_cEntity_GetEquippedLeggings00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedLeggings(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedBoots of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetEquippedBoots00 +static int tolua_AllToLua_cEntity_GetEquippedBoots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->GetEquippedBoots(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: KilledBy of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_KilledBy00 +static int tolua_AllToLua_cEntity_KilledBy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + cEntity* a_Killer = ((cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'KilledBy'", NULL); +#endif + { + self->KilledBy(a_Killer); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'KilledBy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Heal of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_Heal00 +static int tolua_AllToLua_cEntity_Heal00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_HitPoints = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL); +#endif + { + self->Heal(a_HitPoints); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetHealth00 +static int tolua_AllToLua_cEntity_GetHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHealth'", NULL); +#endif + { + int tolua_ret = (int) self->GetHealth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetHealth00 +static int tolua_AllToLua_cEntity_SetHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_Health = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHealth'", NULL); +#endif + { + self->SetHealth(a_Health); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetMaxHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetMaxHealth00 +static int tolua_AllToLua_cEntity_SetMaxHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_MaxHealth = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxHealth'", NULL); +#endif + { + self->SetMaxHealth(a_MaxHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetMaxHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxHealth of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetMaxHealth00 +static int tolua_AllToLua_cEntity_GetMaxHealth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHealth'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxHealth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxHealth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StartBurning of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StartBurning00 +static int tolua_AllToLua_cEntity_StartBurning00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + int a_TicksLeftBurning = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StartBurning'", NULL); +#endif + { + self->StartBurning(a_TicksLeftBurning); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StartBurning'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StopBurning of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_StopBurning00 +static int tolua_AllToLua_cEntity_StopBurning00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'StopBurning'", NULL); +#endif + { + self->StopBurning(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StopBurning'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TeleportToEntity of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToEntity00 +static int tolua_AllToLua_cEntity_TeleportToEntity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEntity",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + cEntity* a_Entity = ((cEntity*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToEntity'", NULL); +#endif + { + self->TeleportToEntity(*a_Entity); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TeleportToEntity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TeleportToCoords of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_TeleportToCoords00 +static int tolua_AllToLua_cEntity_TeleportToCoords00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TeleportToCoords'", NULL); +#endif + { + self->TeleportToCoords(a_PosX,a_PosY,a_PosZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TeleportToCoords'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsOnFire of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsOnFire00 +static int tolua_AllToLua_cEntity_IsOnFire00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnFire'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsOnFire(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsOnFire'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCrouched of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsCrouched00 +static int tolua_AllToLua_cEntity_IsCrouched00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCrouched'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCrouched(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCrouched'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsRiding of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRiding00 +static int tolua_AllToLua_cEntity_IsRiding00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRiding'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsRiding(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsRiding'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSprinting of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsSprinting00 +static int tolua_AllToLua_cEntity_IsSprinting00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSprinting'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSprinting(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSprinting'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsRclking of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsRclking00 +static int tolua_AllToLua_cEntity_IsRclking00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsRclking'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsRclking(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsRclking'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInvisible of class cEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_IsInvisible00 +static int tolua_AllToLua_cEntity_IsInvisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInvisible'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInvisible(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInvisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetCurrentExperience of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetCurrentExperience00 +static int tolua_AllToLua_cPlayer_SetCurrentExperience00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + short a_XpTotal = ((short) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetCurrentExperience'", NULL); +#endif + { + bool tolua_ret = (bool) self->SetCurrentExperience(a_XpTotal); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetCurrentExperience'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DeltaExperience of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_DeltaExperience00 +static int tolua_AllToLua_cPlayer_DeltaExperience00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + short a_Xp_delta = ((short) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DeltaExperience'", NULL); +#endif + { + short tolua_ret = (short) self->DeltaExperience(a_Xp_delta); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DeltaExperience'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetXpLifetimeTotal of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetXpLifetimeTotal00 +static int tolua_AllToLua_cPlayer_GetXpLifetimeTotal00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetXpLifetimeTotal'", NULL); +#endif + { + short tolua_ret = (short) self->GetXpLifetimeTotal(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetXpLifetimeTotal'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCurrentXp of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetCurrentXp00 +static int tolua_AllToLua_cPlayer_GetCurrentXp00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCurrentXp'", NULL); +#endif + { + short tolua_ret = (short) self->GetCurrentXp(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCurrentXp'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetXpLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetXpLevel00 +static int tolua_AllToLua_cPlayer_GetXpLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetXpLevel'", NULL); +#endif + { + short tolua_ret = (short) self->GetXpLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetXpLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetXpPercentage of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetXpPercentage00 +static int tolua_AllToLua_cPlayer_GetXpPercentage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetXpPercentage'", NULL); +#endif + { + float tolua_ret = (float) self->GetXpPercentage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetXpPercentage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: XpForLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_XpForLevel00 +static int tolua_AllToLua_cPlayer_XpForLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short int a_Level = ((short int) tolua_tonumber(tolua_S,2,0)); + { + short tolua_ret = (short) cPlayer::XpForLevel(a_Level); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'XpForLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CalcLevelFromXp of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CalcLevelFromXp00 +static int tolua_AllToLua_cPlayer_CalcLevelFromXp00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + short int a_CurrentXp = ((short int) tolua_tonumber(tolua_S,2,0)); + { + short tolua_ret = (short) cPlayer::CalcLevelFromXp(a_CurrentXp); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CalcLevelFromXp'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEyeHeight of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyeHeight00 +static int tolua_AllToLua_cPlayer_GetEyeHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyeHeight'", NULL); +#endif + { + double tolua_ret = (double) self->GetEyeHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEyeHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEyePosition of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEyePosition00 +static int tolua_AllToLua_cPlayer_GetEyePosition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEyePosition'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetEyePosition(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEyePosition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsOnGround of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsOnGround00 +static int tolua_AllToLua_cPlayer_IsOnGround00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsOnGround'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsOnGround(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsOnGround'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetStance of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetStance00 +static int tolua_AllToLua_cPlayer_GetStance00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStance'", NULL); +#endif + { + const double tolua_ret = (const double) self->GetStance(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetStance'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInventory of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetInventory00 +static int tolua_AllToLua_cPlayer_GetInventory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventory'", NULL); +#endif + { + cInventory& tolua_ret = (cInventory&) self->GetInventory(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cInventory"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInventory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedItem of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEquippedItem00 +static int tolua_AllToLua_cPlayer_GetEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedItem(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetThrowStartPos of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowStartPos00 +static int tolua_AllToLua_cPlayer_GetThrowStartPos00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowStartPos'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetThrowStartPos(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetThrowStartPos'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetThrowSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetThrowSpeed00 +static int tolua_AllToLua_cPlayer_GetThrowSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_SpeedCoeff = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetThrowSpeed'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->GetThrowSpeed(a_SpeedCoeff); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetThrowSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGameMode of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetGameMode00 +static int tolua_AllToLua_cPlayer_GetGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGameMode'", NULL); +#endif + { + eGameMode tolua_ret = (eGameMode) self->GetGameMode(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEffectiveGameMode of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetEffectiveGameMode00 +static int tolua_AllToLua_cPlayer_GetEffectiveGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEffectiveGameMode'", NULL); +#endif + { + eGameMode tolua_ret = (eGameMode) self->GetEffectiveGameMode(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEffectiveGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetGameMode of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetGameMode00 +static int tolua_AllToLua_cPlayer_SetGameMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + eGameMode a_GameMode = ((eGameMode) (int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGameMode'", NULL); +#endif + { + self->SetGameMode(a_GameMode); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeCreative of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeCreative00 +static int tolua_AllToLua_cPlayer_IsGameModeCreative00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeCreative(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeSurvival of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeSurvival00 +static int tolua_AllToLua_cPlayer_IsGameModeSurvival00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeSurvival(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeAdventure of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeAdventure00 +static int tolua_AllToLua_cPlayer_IsGameModeAdventure00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeAdventure(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIP of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetIP00 +static int tolua_AllToLua_cPlayer_GetIP00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIP'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetIP(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIP'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MoveTo of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveTo00 +static int tolua_AllToLua_cPlayer_MoveTo00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_NewPos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveTo'", NULL); +#endif + { + self->MoveTo(*a_NewPos); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MoveTo'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindow of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetWindow00 +static int tolua_AllToLua_cPlayer_GetWindow00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindow'", NULL); +#endif + { + cWindow* tolua_ret = (cWindow*) self->GetWindow(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWindow"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindow'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CloseWindow of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindow00 +static int tolua_AllToLua_cPlayer_CloseWindow00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,2,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindow'", NULL); +#endif + { + self->CloseWindow(a_CanRefuse); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CloseWindow'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CloseWindowIfID of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CloseWindowIfID00 +static int tolua_AllToLua_cPlayer_CloseWindowIfID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + char a_WindowID = ((char) tolua_tonumber(tolua_S,2,0)); + bool a_CanRefuse = ((bool) tolua_toboolean(tolua_S,3,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CloseWindowIfID'", NULL); +#endif + { + self->CloseWindowIfID(a_WindowID,a_CanRefuse); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CloseWindowIfID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetClientHandle of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetClientHandle00 +static int tolua_AllToLua_cPlayer_GetClientHandle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetClientHandle'", NULL); +#endif + { + cClientHandle* tolua_ret = (cClientHandle*) self->GetClientHandle(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cClientHandle"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetClientHandle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SendMessage of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SendMessage00 +static int tolua_AllToLua_cPlayer_SendMessage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendMessage'", NULL); +#endif + { + self->SendMessage(a_Message); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SendMessage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetName00 +static int tolua_AllToLua_cPlayer_GetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetName of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetName00 +static int tolua_AllToLua_cPlayer_SetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); +#endif + { + self->SetName(a_Name); + tolua_pushcppstring(tolua_S,(const char*)a_Name); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddToGroup of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddToGroup00 +static int tolua_AllToLua_cPlayer_AddToGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddToGroup'", NULL); +#endif + { + self->AddToGroup(a_GroupName); + tolua_pushcppstring(tolua_S,(const char*)a_GroupName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddToGroup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveFromGroup of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_RemoveFromGroup00 +static int tolua_AllToLua_cPlayer_RemoveFromGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_GroupName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveFromGroup'", NULL); +#endif + { + self->RemoveFromGroup(a_GroupName); + tolua_pushcppstring(tolua_S,(const char*)a_GroupName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RemoveFromGroup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CanUseCommand of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_CanUseCommand00 +static int tolua_AllToLua_cPlayer_CanUseCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanUseCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->CanUseCommand(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CanUseCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasPermission of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_HasPermission00 +static int tolua_AllToLua_cPlayer_HasPermission00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Permission = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasPermission'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasPermission(a_Permission); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Permission); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasPermission'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInGroup of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsInGroup00 +static int tolua_AllToLua_cPlayer_IsInGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const AString a_Group = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGroup'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInGroup(a_Group); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Group); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInGroup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetColor of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetColor00 +static int tolua_AllToLua_cPlayer_GetColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetColor(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: TossItem of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_TossItem00 +static int tolua_AllToLua_cPlayer_TossItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnumber(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_bDraggingItem = ((bool) tolua_toboolean(tolua_S,2,0)); + char a_Amount = ((char) tolua_tonumber(tolua_S,3,1)); + short a_CreateType = ((short) tolua_tonumber(tolua_S,4,0)); + short a_CreateHealth = ((short) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'TossItem'", NULL); +#endif + { + self->TossItem(a_bDraggingItem,a_Amount,a_CreateType,a_CreateHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'TossItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Heal of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Heal00 +static int tolua_AllToLua_cPlayer_Heal00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_Health = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Heal'", NULL); +#endif + { + self->Heal(a_Health); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Heal'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodLevel00 +static int tolua_AllToLua_cPlayer_GetFoodLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodLevel'", NULL); +#endif + { + int tolua_ret = (int) self->GetFoodLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodSaturationLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodSaturationLevel00 +static int tolua_AllToLua_cPlayer_GetFoodSaturationLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodSaturationLevel'", NULL); +#endif + { + double tolua_ret = (double) self->GetFoodSaturationLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodSaturationLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodTickTimer of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodTickTimer00 +static int tolua_AllToLua_cPlayer_GetFoodTickTimer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodTickTimer'", NULL); +#endif + { + int tolua_ret = (int) self->GetFoodTickTimer(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodTickTimer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodExhaustionLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00 +static int tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodExhaustionLevel'", NULL); +#endif + { + double tolua_ret = (double) self->GetFoodExhaustionLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodExhaustionLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFoodPoisonedTicksRemaining of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00 +static int tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFoodPoisonedTicksRemaining'", NULL); +#endif + { + int tolua_ret = (int) self->GetFoodPoisonedTicksRemaining(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFoodPoisonedTicksRemaining'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetAirLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetAirLevel00 +static int tolua_AllToLua_cPlayer_GetAirLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAirLevel'", NULL); +#endif + { + int tolua_ret = (int) self->GetAirLevel(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetAirLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSatiated of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSatiated00 +static int tolua_AllToLua_cPlayer_IsSatiated00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSatiated'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSatiated(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSatiated'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodLevel00 +static int tolua_AllToLua_cPlayer_SetFoodLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_FoodLevel = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodLevel'", NULL); +#endif + { + self->SetFoodLevel(a_FoodLevel); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodSaturationLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodSaturationLevel00 +static int tolua_AllToLua_cPlayer_SetFoodSaturationLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_FoodSaturationLevel = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodSaturationLevel'", NULL); +#endif + { + self->SetFoodSaturationLevel(a_FoodSaturationLevel); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodSaturationLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodTickTimer of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodTickTimer00 +static int tolua_AllToLua_cPlayer_SetFoodTickTimer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_FoodTickTimer = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodTickTimer'", NULL); +#endif + { + self->SetFoodTickTimer(a_FoodTickTimer); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodTickTimer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodExhaustionLevel of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00 +static int tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_FoodExhaustionLevel = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodExhaustionLevel'", NULL); +#endif + { + self->SetFoodExhaustionLevel(a_FoodExhaustionLevel); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodExhaustionLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFoodPoisonedTicksRemaining of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00 +static int tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_FoodPoisonedTicksRemaining = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFoodPoisonedTicksRemaining'", NULL); +#endif + { + self->SetFoodPoisonedTicksRemaining(a_FoodPoisonedTicksRemaining); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFoodPoisonedTicksRemaining'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Feed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Feed00 +static int tolua_AllToLua_cPlayer_Feed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",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 + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_Food = ((int) tolua_tonumber(tolua_S,2,0)); + double a_Saturation = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Feed'", NULL); +#endif + { + bool tolua_ret = (bool) self->Feed(a_Food,a_Saturation); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Feed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddFoodExhaustion of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_AddFoodExhaustion00 +static int tolua_AllToLua_cPlayer_AddFoodExhaustion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_Exhaustion = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFoodExhaustion'", NULL); +#endif + { + self->AddFoodExhaustion(a_Exhaustion); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddFoodExhaustion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FoodPoison of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_FoodPoison00 +static int tolua_AllToLua_cPlayer_FoodPoison00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + int a_NumTicks = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FoodPoison'", NULL); +#endif + { + self->FoodPoison(a_NumTicks); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FoodPoison'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEating of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsEating00 +static int tolua_AllToLua_cPlayer_IsEating00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEating'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEating(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEating'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Respawn of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Respawn00 +static int tolua_AllToLua_cPlayer_Respawn00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Respawn'", NULL); +#endif + { + self->Respawn(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Respawn'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetVisible of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetVisible00 +static int tolua_AllToLua_cPlayer_SetVisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_bVisible = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVisible'", NULL); +#endif + { + self->SetVisible(a_bVisible); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetVisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsVisible of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsVisible00 +static int tolua_AllToLua_cPlayer_IsVisible00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsVisible'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsVisible(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsVisible'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MoveToWorld of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_MoveToWorld00 +static int tolua_AllToLua_cPlayer_MoveToWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const char* a_WorldName = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MoveToWorld'", NULL); +#endif + { + bool tolua_ret = (bool) self->MoveToWorld(a_WorldName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MoveToWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LoadPermissionsFromDisk of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00 +static int tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPermissionsFromDisk'", NULL); +#endif + { + self->LoadPermissionsFromDisk(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadPermissionsFromDisk'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetMaxSpeed00 +static int tolua_AllToLua_cPlayer_GetMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxSpeed'", NULL); +#endif + { + double tolua_ret = (double) self->GetMaxSpeed(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNormalMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetNormalMaxSpeed00 +static int tolua_AllToLua_cPlayer_GetNormalMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNormalMaxSpeed'", NULL); +#endif + { + double tolua_ret = (double) self->GetNormalMaxSpeed(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNormalMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSprintingMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00 +static int tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSprintingMaxSpeed'", NULL); +#endif + { + double tolua_ret = (double) self->GetSprintingMaxSpeed(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSprintingMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetNormalMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetNormalMaxSpeed00 +static int tolua_AllToLua_cPlayer_SetNormalMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_Speed = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNormalMaxSpeed'", NULL); +#endif + { + self->SetNormalMaxSpeed(a_Speed); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetNormalMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSprintingMaxSpeed of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00 +static int tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + double a_Speed = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprintingMaxSpeed'", NULL); +#endif + { + self->SetSprintingMaxSpeed(a_Speed); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSprintingMaxSpeed'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetCrouch of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetCrouch00 +static int tolua_AllToLua_cPlayer_SetCrouch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_IsCrouched = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetCrouch'", NULL); +#endif + { + self->SetCrouch(a_IsCrouched); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetCrouch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSprint of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetSprint00 +static int tolua_AllToLua_cPlayer_SetSprint00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + bool a_IsSprinting = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSprint'", NULL); +#endif + { + self->SetSprint(a_IsSprinting); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSprint'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSwimming of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSwimming00 +static int tolua_AllToLua_cPlayer_IsSwimming00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSwimming'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSwimming(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSwimming'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSubmerged of class cPlayer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSubmerged00 +static int tolua_AllToLua_cPlayer_IsSubmerged00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSubmerged'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSubmerged(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSubmerged'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00 +static int tolua_AllToLua_cPickup_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,1,&tolua_err) || + !tolua_isnumber(tolua_S,8,1,&tolua_err) || + !tolua_isnumber(tolua_S,9,1,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0)); + float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f)); + float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f)); + float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f)); + { + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_new00_local +static int tolua_AllToLua_cPickup_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,1,&tolua_err) || + !tolua_isnumber(tolua_S,8,1,&tolua_err) || + !tolua_isnumber(tolua_S,9,1,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,5,0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,6,0)); + float a_SpeedX = ((float) tolua_tonumber(tolua_S,7,0.f)); + float a_SpeedY = ((float) tolua_tonumber(tolua_S,8,0.f)); + float a_SpeedZ = ((float) tolua_tonumber(tolua_S,9,0.f)); + { + cPickup* tolua_ret = (cPickup*) Mtolua_new((cPickup)(a_PosX,a_PosY,a_PosZ,*a_Item,IsPlayerCreated,a_SpeedX,a_SpeedY,a_SpeedZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPickup"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetItem of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetItem00 +static int tolua_AllToLua_cPickup_GetItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->GetItem(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CollectedBy of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_CollectedBy00 +static int tolua_AllToLua_cPickup_CollectedBy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPickup",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPickup* self = (cPickup*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Dest = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CollectedBy'", NULL); +#endif + { + bool tolua_ret = (bool) self->CollectedBy(a_Dest); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CollectedBy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetAge of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_GetAge00 +static int tolua_AllToLua_cPickup_GetAge00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetAge'", NULL); +#endif + { + int tolua_ret = (int) self->GetAge(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetAge'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCollected of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsCollected00 +static int tolua_AllToLua_cPickup_IsCollected00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCollected'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCollected(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCollected'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPlayerCreated of class cPickup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPickup_IsPlayerCreated00 +static int tolua_AllToLua_cPickup_IsPlayerCreated00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPickup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPickup* self = (const cPickup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsPlayerCreated'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPlayerCreated(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPlayerCreated'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetProjectileKind of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetProjectileKind00 +static int tolua_AllToLua_cProjectileEntity_GetProjectileKind00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetProjectileKind'", NULL); +#endif + { + cProjectileEntity::eKind tolua_ret = (cProjectileEntity::eKind) self->GetProjectileKind(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetProjectileKind'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCreator of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetCreator00 +static int tolua_AllToLua_cProjectileEntity_GetCreator00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cProjectileEntity* self = (cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCreator'", NULL); +#endif + { + cEntity* tolua_ret = (cEntity*) self->GetCreator(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEntity"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCreator'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMCAClassName of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_GetMCAClassName00 +static int tolua_AllToLua_cProjectileEntity_GetMCAClassName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMCAClassName'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetMCAClassName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMCAClassName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInGround of class cProjectileEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cProjectileEntity_IsInGround00 +static int tolua_AllToLua_cProjectileEntity_IsInGround00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cProjectileEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cProjectileEntity* self = (const cProjectileEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInGround'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInGround(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInGround'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPickupState of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetPickupState00 +static int tolua_AllToLua_cArrowEntity_GetPickupState00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPickupState'", NULL); +#endif + { + cArrowEntity::ePickupState tolua_ret = (cArrowEntity::ePickupState) self->GetPickupState(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPickupState'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPickupState of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetPickupState00 +static int tolua_AllToLua_cArrowEntity_SetPickupState00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); + cArrowEntity::ePickupState a_PickupState = ((cArrowEntity::ePickupState) (int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPickupState'", NULL); +#endif + { + self->SetPickupState(a_PickupState); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPickupState'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDamageCoeff of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_GetDamageCoeff00 +static int tolua_AllToLua_cArrowEntity_GetDamageCoeff00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDamageCoeff'", NULL); +#endif + { + double tolua_ret = (double) self->GetDamageCoeff(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDamageCoeff'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetDamageCoeff of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetDamageCoeff00 +static int tolua_AllToLua_cArrowEntity_SetDamageCoeff00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); + double a_DamageCoeff = ((double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetDamageCoeff'", NULL); +#endif + { + self->SetDamageCoeff(a_DamageCoeff); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetDamageCoeff'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CanPickup of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_CanPickup00 +static int tolua_AllToLua_cArrowEntity_CanPickup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cPlayer",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); + const cPlayer* a_Player = ((const cPlayer*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CanPickup'", NULL); +#endif + { + bool tolua_ret = (bool) self->CanPickup(*a_Player); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CanPickup'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCritical of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_IsCritical00 +static int tolua_AllToLua_cArrowEntity_IsCritical00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cArrowEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cArrowEntity* self = (const cArrowEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCritical'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCritical(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCritical'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetIsCritical of class cArrowEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cArrowEntity_SetIsCritical00 +static int tolua_AllToLua_cArrowEntity_SetIsCritical00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cArrowEntity",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cArrowEntity* self = (cArrowEntity*) tolua_tousertype(tolua_S,1,0); + bool a_IsCritical = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIsCritical'", NULL); +#endif + { + self->SetIsCritical(a_IsCritical); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetIsCritical'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Get of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_Get00 +static int tolua_AllToLua_cPluginManager_Get00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cPluginManager* tolua_ret = (cPluginManager*) cPluginManager::Get(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPlugin of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetPlugin00 +static int tolua_AllToLua_cPluginManager_GetPlugin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Plugin = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlugin'", NULL); +#endif + { + cPlugin* tolua_ret = (cPlugin*) self->GetPlugin(a_Plugin); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPlugin"); + tolua_pushcppstring(tolua_S,(const char*)a_Plugin); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPlugin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FindPlugins of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_FindPlugins00 +static int tolua_AllToLua_cPluginManager_FindPlugins00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FindPlugins'", NULL); +#endif + { + self->FindPlugins(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FindPlugins'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReloadPlugins of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ReloadPlugins00 +static int tolua_AllToLua_cPluginManager_ReloadPlugins00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReloadPlugins'", NULL); +#endif + { + self->ReloadPlugins(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReloadPlugins'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumPlugins of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetNumPlugins00 +static int tolua_AllToLua_cPluginManager_GetNumPlugins00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlugins'", NULL); +#endif + { + unsigned int tolua_ret = (unsigned int) self->GetNumPlugins(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumPlugins'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DisablePlugin of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_DisablePlugin00 +static int tolua_AllToLua_cPluginManager_DisablePlugin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DisablePlugin'", NULL); +#endif + { + bool tolua_ret = (bool) self->DisablePlugin(a_PluginName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_PluginName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DisablePlugin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LoadPlugin of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_LoadPlugin00 +static int tolua_AllToLua_cPluginManager_LoadPlugin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_PluginName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadPlugin'", NULL); +#endif + { + bool tolua_ret = (bool) self->LoadPlugin(a_PluginName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_PluginName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadPlugin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCommandBound of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsCommandBound00 +static int tolua_AllToLua_cPluginManager_IsCommandBound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCommandBound'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCommandBound(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCommandBound'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCommandPermission of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_GetCommandPermission00 +static int tolua_AllToLua_cPluginManager_GetCommandPermission00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCommandPermission'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetCommandPermission(a_Command); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCommandPermission'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ExecuteCommand of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ExecuteCommand00 +static int tolua_AllToLua_cPluginManager_ExecuteCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->ExecuteCommand(a_Player,a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ExecuteCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ForceExecuteCommand of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ForceExecuteCommand00 +static int tolua_AllToLua_cPluginManager_ForceExecuteCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ForceExecuteCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->ForceExecuteCommand(a_Player,a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ForceExecuteCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsConsoleCommandBound of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsConsoleCommandBound00 +static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + const AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsConsoleCommandBound'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsConsoleCommandBound(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsConsoleCommandBound'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00 +static int tolua_AllToLua_cPlugin_GetName00(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 'GetName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetName of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetName00 +static int tolua_AllToLua_cPlugin_SetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); + const AString a_Name = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); +#endif + { + self->SetName(a_Name); + tolua_pushcppstring(tolua_S,(const char*)a_Name); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetVersion of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetVersion00 +static int tolua_AllToLua_cPlugin_GetVersion00(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 'GetVersion'", NULL); +#endif + { + int tolua_ret = (int) self->GetVersion(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetVersion of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_SetVersion00 +static int tolua_AllToLua_cPlugin_SetVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPlugin",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); + int a_Version = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetVersion'", NULL); +#endif + { + self->SetVersion(a_Version); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDirectory of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetDirectory00 +static int tolua_AllToLua_cPlugin_GetDirectory00(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 'GetDirectory'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetDirectory(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDirectory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLocalDirectory of class cPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetLocalDirectory00 +static int tolua_AllToLua_cPlugin_GetLocalDirectory00(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 'GetLocalDirectory'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLocalDirectory(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLocalDirectory'.",&tolua_err); + return 0; +#endif +} +#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) +{ + cPluginLua* self = (cPluginLua*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cWebPlugin__'",NULL); +#endif +#ifdef __cplusplus + tolua_pushusertype(tolua_S,(void*)static_cast<cWebPlugin*>(self), "cWebPlugin"); +#else + tolua_pushusertype(tolua_S,(void*)((cWebPlugin*)self), "cWebPlugin"); +#endif + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDescription of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetDescription00 +static int tolua_AllToLua_cServer_GetDescription00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDescription'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetDescription(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDescription'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxPlayers of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetMaxPlayers00 +static int tolua_AllToLua_cServer_GetMaxPlayers00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxPlayers'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxPlayers(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxPlayers'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumPlayers of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetNumPlayers00 +static int tolua_AllToLua_cServer_GetNumPlayers00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumPlayers(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumPlayers'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetMaxPlayers of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_SetMaxPlayers00 +static int tolua_AllToLua_cServer_SetMaxPlayers00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); + int a_MaxPlayers = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxPlayers'", NULL); +#endif + { + self->SetMaxPlayers(a_MaxPlayers); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetMaxPlayers'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsHardcore of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_IsHardcore00 +static int tolua_AllToLua_cServer_IsHardcore00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsHardcore'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsHardcore(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsHardcore'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetServerID of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetServerID00 +static int tolua_AllToLua_cServer_GetServerID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServerID'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetServerID(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetServerID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTicksUntilWeatherChange of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00 +static int tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00(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 'GetTicksUntilWeatherChange'", NULL); +#endif + { + int tolua_ret = (int) self->GetTicksUntilWeatherChange(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTicksUntilWeatherChange'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorldAge of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWorldAge00 +static int tolua_AllToLua_cWorld_GetWorldAge00(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 'GetWorldAge'", NULL); +#endif + { + long long tolua_ret = ( long long) self->GetWorldAge(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorldAge'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTimeOfDay of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetTimeOfDay00 +static int tolua_AllToLua_cWorld_GetTimeOfDay00(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 'GetTimeOfDay'", NULL); +#endif + { + long long tolua_ret = ( long long) self->GetTimeOfDay(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTimeOfDay'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetTicksUntilWeatherChange of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00 +static int tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_WeatherInterval = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTicksUntilWeatherChange'", NULL); +#endif + { + self->SetTicksUntilWeatherChange(a_WeatherInterval); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetTicksUntilWeatherChange'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetTimeOfDay of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetTimeOfDay00 +static int tolua_AllToLua_cWorld_SetTimeOfDay00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + long long a_TimeOfDay = (( long long) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetTimeOfDay'", NULL); +#endif + { + self->SetTimeOfDay(a_TimeOfDay); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetTimeOfDay'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGameMode of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGameMode00 +static int tolua_AllToLua_cWorld_GetGameMode00(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 'GetGameMode'", NULL); +#endif + { + eGameMode tolua_ret = (eGameMode) self->GetGameMode(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGameMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeCreative of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeCreative00 +static int tolua_AllToLua_cWorld_IsGameModeCreative00(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 'IsGameModeCreative'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeCreative(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeSurvival of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeSurvival00 +static int tolua_AllToLua_cWorld_IsGameModeSurvival00(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 'IsGameModeSurvival'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeSurvival(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsGameModeAdventure of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeAdventure00 +static int tolua_AllToLua_cWorld_IsGameModeAdventure00(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 'IsGameModeAdventure'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsGameModeAdventure(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsPVPEnabled of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsPVPEnabled00 +static int tolua_AllToLua_cWorld_IsPVPEnabled00(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 'IsPVPEnabled'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsPVPEnabled(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsPVPEnabled'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsDeepSnowEnabled of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsDeepSnowEnabled00 +static int tolua_AllToLua_cWorld_IsDeepSnowEnabled00(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 'IsDeepSnowEnabled'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsDeepSnowEnabled(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsDeepSnowEnabled'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDimension of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetDimension00 +static int tolua_AllToLua_cWorld_GetDimension00(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 'GetDimension'", NULL); +#endif + { + eDimension tolua_ret = (eDimension) self->GetDimension(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDimension'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetHeight00 +static int tolua_AllToLua_cWorld_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",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 + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(a_BlockX,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastChat of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastChat00 +static int tolua_AllToLua_cWorld_BroadcastChat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isusertype(tolua_S,3,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,3,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); +#endif + { + self->BroadcastChat(a_Message,a_Exclude); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastSoundEffect of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundEffect00 +static int tolua_AllToLua_cWorld_BroadcastSoundEffect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isusertype(tolua_S,8,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const AString a_SoundName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0)); + float a_Volume = ((float) tolua_tonumber(tolua_S,6,0)); + float a_Pitch = ((float) tolua_tonumber(tolua_S,7,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,8,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundEffect'", NULL); +#endif + { + self->BroadcastSoundEffect(a_SoundName,a_SrcX,a_SrcY,a_SrcZ,a_Volume,a_Pitch,a_Exclude); + tolua_pushcppstring(tolua_S,(const char*)a_SoundName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastSoundEffect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastSoundParticleEffect of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00 +static int tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isusertype(tolua_S,7,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_EffectID = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SrcX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SrcY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_SrcZ = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Data = ((int) tolua_tonumber(tolua_S,6,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,7,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastSoundParticleEffect'", NULL); +#endif + { + self->BroadcastSoundParticleEffect(a_EffectID,a_SrcX,a_SrcY,a_SrcZ,a_Data,a_Exclude); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastSoundParticleEffect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: UnloadUnusedChunks of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UnloadUnusedChunks00 +static int tolua_AllToLua_cWorld_UnloadUnusedChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UnloadUnusedChunks'", NULL); +#endif + { + self->UnloadUnusedChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'UnloadUnusedChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RegenerateChunk of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_RegenerateChunk00 +static int tolua_AllToLua_cWorld_RegenerateChunk00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",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 + { + cWorld* self = (cWorld*) 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 'RegenerateChunk'", NULL); +#endif + { + self->RegenerateChunk(a_ChunkX,a_ChunkZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RegenerateChunk'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GenerateChunk of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GenerateChunk00 +static int tolua_AllToLua_cWorld_GenerateChunk00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",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 + { + cWorld* self = (cWorld*) 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 'GenerateChunk'", NULL); +#endif + { + self->GenerateChunk(a_ChunkX,a_ChunkZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GenerateChunk'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlock00 +static int tolua_AllToLua_cWorld_SetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlock'", NULL); +#endif + { + self->SetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FastSetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock00 +static int tolua_AllToLua_cWorld_FastSetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL); +#endif + { + self->FastSetBlock(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FastSetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueSetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSetBlock00 +static int tolua_AllToLua_cWorld_QueueSetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BLockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); + int a_TickDelay = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSetBlock'", NULL); +#endif + { + self->QueueSetBlock(a_BlockX,a_BLockY,a_BlockZ,a_BlockType,a_BlockMeta,a_TickDelay); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueSetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock00 +static int tolua_AllToLua_cWorld_GetBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlock(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta00 +static int tolua_AllToLua_cWorld_GetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta00 +static int tolua_AllToLua_cWorld_SetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_MetaData); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockSkyLight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockSkyLight00 +static int tolua_AllToLua_cWorld_GetBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockBlockLight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockBlockLight00 +static int tolua_AllToLua_cWorld_GetBlockBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockBlockLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockBlockLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FastSetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_FastSetBlock01 +static int tolua_AllToLua_cWorld_FastSetBlock01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FastSetBlock'", NULL); +#endif + { + self->FastSetBlock(*a_Pos,a_BlockType,a_BlockMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWorld_FastSetBlock00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlock01 +static int tolua_AllToLua_cWorld_GetBlock01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlock'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlock(*a_Pos); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cWorld_GetBlock00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBlockMeta01 +static int tolua_AllToLua_cWorld_GetBlockMeta01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(*a_Pos); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cWorld_GetBlockMeta00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlockMeta01 +static int tolua_AllToLua_cWorld_SetBlockMeta01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const Vector3i* a_Pos = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_MetaData = (( unsigned char) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(*a_Pos,a_MetaData); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWorld_SetBlockMeta00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnItemPickups of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups00 +static int tolua_AllToLua_cWorld_SpawnItemPickups00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isboolean(tolua_S,7,1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0)); + double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); + double a_FlyAwaySpeed = ((double) tolua_tonumber(tolua_S,6,1.0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,7,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL); +#endif + { + self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_FlyAwaySpeed,IsPlayerCreated); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnItemPickups'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnItemPickups of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnItemPickups01 +static int tolua_AllToLua_cWorld_SpawnItemPickups01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItems",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isboolean(tolua_S,9,1,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cItems* a_Pickups = ((const cItems*) tolua_tousertype(tolua_S,2,0)); + double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); + double a_SpeedX = ((double) tolua_tonumber(tolua_S,6,0)); + double a_SpeedY = ((double) tolua_tonumber(tolua_S,7,0)); + double a_SpeedZ = ((double) tolua_tonumber(tolua_S,8,0)); + bool IsPlayerCreated = ((bool) tolua_toboolean(tolua_S,9,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnItemPickups'", NULL); +#endif + { + self->SpawnItemPickups(*a_Pickups,a_BlockX,a_BlockY,a_BlockZ,a_SpeedX,a_SpeedY,a_SpeedZ,IsPlayerCreated); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWorld_SpawnItemPickups00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnExperienceOrb of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnExperienceOrb00 +static int tolua_AllToLua_cWorld_SpawnExperienceOrb00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,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)); + int a_Reward = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnExperienceOrb'", NULL); +#endif + { + int tolua_ret = (int) self->SpawnExperienceOrb(a_X,a_Y,a_Z,a_Reward); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnExperienceOrb'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnPrimedTNT of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnPrimedTNT00 +static int tolua_AllToLua_cWorld_SpawnPrimedTNT00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,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)); + double a_FuseTimeInSec = ((double) tolua_tonumber(tolua_S,5,0)); + double a_InitialVelocityCoeff = ((double) tolua_tonumber(tolua_S,6,1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnPrimedTNT'", NULL); +#endif + { + self->SpawnPrimedTNT(a_X,a_Y,a_Z,a_FuseTimeInSec,a_InitialVelocityCoeff); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnPrimedTNT'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DigBlock of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DigBlock00 +static int tolua_AllToLua_cWorld_DigBlock00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DigBlock'", NULL); +#endif + { + bool tolua_ret = (bool) self->DigBlock(a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DigBlock'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SendBlockTo of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SendBlockTo00 +static int tolua_AllToLua_cWorld_SendBlockTo00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isusertype(tolua_S,5,"cPlayer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SendBlockTo'", NULL); +#endif + { + self->SendBlockTo(a_X,a_Y,a_Z,a_Player); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SendBlockTo'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnX of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnX00 +static int tolua_AllToLua_cWorld_GetSpawnX00(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 'GetSpawnX'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpawnX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnY of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnY00 +static int tolua_AllToLua_cWorld_GetSpawnY00(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 'GetSpawnY'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpawnY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnZ of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetSpawnZ00 +static int tolua_AllToLua_cWorld_GetSpawnZ00(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 'GetSpawnZ'", NULL); +#endif + { + double tolua_ret = (double) self->GetSpawnZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WakeUpSimulators of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulators00 +static int tolua_AllToLua_cWorld_WakeUpSimulators00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulators'", NULL); +#endif + { + self->WakeUpSimulators(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WakeUpSimulators'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WakeUpSimulatorsInArea of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00 +static int tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WakeUpSimulatorsInArea'", NULL); +#endif + { + self->WakeUpSimulatorsInArea(a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WakeUpSimulatorsInArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DoExplosionAt of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_DoExplosionAt00 +static int tolua_AllToLua_cWorld_DoExplosionAt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isuserdata(tolua_S,8,0,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_ExplosionSize = ((double) tolua_tonumber(tolua_S,2,0)); + double a_BlockX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_BlockY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_BlockZ = ((double) tolua_tonumber(tolua_S,5,0)); + bool a_CanCauseFire = ((bool) tolua_toboolean(tolua_S,6,0)); + eExplosionSource a_Source = ((eExplosionSource) (int) tolua_tonumber(tolua_S,7,0)); + void* a_SourceData = ((void*) tolua_touserdata(tolua_S,8,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoExplosionAt'", NULL); +#endif + { + self->DoExplosionAt(a_ExplosionSize,a_BlockX,a_BlockY,a_BlockZ,a_CanCauseFire,a_Source,a_SourceData); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DoExplosionAt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: UseBlockEntity of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_UseBlockEntity00 +static int tolua_AllToLua_cWorld_UseBlockEntity00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_BlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'UseBlockEntity'", NULL); +#endif + { + self->UseBlockEntity(a_Player,a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'UseBlockEntity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowTree of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTree00 +static int tolua_AllToLua_cWorld_GrowTree00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTree'", NULL); +#endif + { + self->GrowTree(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowTree'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowTreeFromSapling of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeFromSapling00 +static int tolua_AllToLua_cWorld_GrowTreeFromSapling00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_SaplingMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL); +#endif + { + self->GrowTreeFromSapling(a_BlockX,a_BlockY,a_BlockZ,a_SaplingMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowTreeFromSapling'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowTreeByBiome of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeByBiome00 +static int tolua_AllToLua_cWorld_GrowTreeByBiome00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeByBiome'", NULL); +#endif + { + self->GrowTreeByBiome(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowTreeByBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowRipePlant of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowRipePlant00 +static int tolua_AllToLua_cWorld_GrowRipePlant00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + bool a_IsByBonemeal = ((bool) tolua_toboolean(tolua_S,5,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowRipePlant'", NULL); +#endif + { + bool tolua_ret = (bool) self->GrowRipePlant(a_BlockX,a_BlockY,a_BlockZ,a_IsByBonemeal); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowRipePlant'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowCactus of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowCactus00 +static int tolua_AllToLua_cWorld_GrowCactus00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowCactus'", NULL); +#endif + { + self->GrowCactus(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowCactus'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowMelonPumpkin of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowMelonPumpkin00 +static int tolua_AllToLua_cWorld_GrowMelonPumpkin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowMelonPumpkin'", NULL); +#endif + { + self->GrowMelonPumpkin(a_BlockX,a_BlockY,a_BlockZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowMelonPumpkin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GrowSugarcane of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowSugarcane00 +static int tolua_AllToLua_cWorld_GrowSugarcane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_NumBlocksToGrow = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowSugarcane'", NULL); +#endif + { + self->GrowSugarcane(a_BlockX,a_BlockY,a_BlockZ,a_NumBlocksToGrow); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GrowSugarcane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBiomeAt of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBiomeAt00 +static int tolua_AllToLua_cWorld_GetBiomeAt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",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 + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiomeAt'", NULL); +#endif + { + int tolua_ret = (int) self->GetBiomeAt(a_BlockX,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBiomeAt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetName00 +static int tolua_AllToLua_cWorld_GetName00(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 'GetName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIniFileName of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetIniFileName00 +static int tolua_AllToLua_cWorld_GetIniFileName00(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 'GetIniFileName'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetIniFileName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIniFileName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueSaveAllChunks of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSaveAllChunks00 +static int tolua_AllToLua_cWorld_QueueSaveAllChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSaveAllChunks'", NULL); +#endif + { + self->QueueSaveAllChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueSaveAllChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumChunks of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetNumChunks00 +static int tolua_AllToLua_cWorld_GetNumChunks00(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 'GetNumChunks'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumChunks(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGeneratorQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetGeneratorQueueLength00 +static int tolua_AllToLua_cWorld_GetGeneratorQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGeneratorQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetGeneratorQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGeneratorQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLightingQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetLightingQueueLength00 +static int tolua_AllToLua_cWorld_GetLightingQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLightingQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetLightingQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLightingQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetStorageLoadQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageLoadQueueLength00 +static int tolua_AllToLua_cWorld_GetStorageLoadQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageLoadQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetStorageLoadQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetStorageLoadQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetStorageSaveQueueLength of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetStorageSaveQueueLength00 +static int tolua_AllToLua_cWorld_GetStorageSaveQueueLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetStorageSaveQueueLength'", NULL); +#endif + { + int tolua_ret = (int) self->GetStorageSaveQueueLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetStorageSaveQueueLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueBlockForTick of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueBlockForTick00 +static int tolua_AllToLua_cWorld_QueueBlockForTick00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_TicksToWait = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueBlockForTick'", NULL); +#endif + { + self->QueueBlockForTick(a_BlockX,a_BlockY,a_BlockZ,a_TicksToWait); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueBlockForTick'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CastThunderbolt of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CastThunderbolt00 +static int tolua_AllToLua_cWorld_CastThunderbolt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CastThunderbolt'", NULL); +#endif + { + self->CastThunderbolt(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CastThunderbolt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetWeather of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetWeather00 +static int tolua_AllToLua_cWorld_SetWeather00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + eWeather a_NewWeather = ((eWeather) (int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWeather'", NULL); +#endif + { + self->SetWeather(a_NewWeather); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetWeather'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeWeather of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_ChangeWeather00 +static int tolua_AllToLua_cWorld_ChangeWeather00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeWeather'", NULL); +#endif + { + self->ChangeWeather(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ChangeWeather'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWeather of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWeather00 +static int tolua_AllToLua_cWorld_GetWeather00(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 'GetWeather'", NULL); +#endif + { + eWeather tolua_ret = (eWeather) self->GetWeather(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWeather'.",&tolua_err); + return 0; +#endif +} +#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) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetNextBlockTick'", NULL); +#endif + { + self->SetNextBlockTick(a_BlockX,a_BlockY,a_BlockZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetNextBlockTick'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxSugarcaneHeight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00 +static int tolua_AllToLua_cWorld_GetMaxSugarcaneHeight00(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 'GetMaxSugarcaneHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxSugarcaneHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxSugarcaneHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxCactusHeight of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxCactusHeight00 +static int tolua_AllToLua_cWorld_GetMaxCactusHeight00(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 'GetMaxCactusHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetMaxCactusHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxCactusHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsBlockDirectlyWatered of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsBlockDirectlyWatered00 +static int tolua_AllToLua_cWorld_IsBlockDirectlyWatered00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsBlockDirectlyWatered'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsBlockDirectlyWatered(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsBlockDirectlyWatered'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SpawnMob of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SpawnMob00 +static int tolua_AllToLua_cWorld_SpawnMob00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + cMonster::eType a_MonsterType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnMob'", NULL); +#endif + { + int tolua_ret = (int) self->SpawnMob(a_PosX,a_PosY,a_PosZ,a_MonsterType); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SpawnMob'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CreateProjectile of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_CreateProjectile00 +static int tolua_AllToLua_cWorld_CreateProjectile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isusertype(tolua_S,6,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,7,"const Vector3d",1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + double a_PosX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_PosY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_PosZ = ((double) tolua_tonumber(tolua_S,4,0)); + cProjectileEntity::eKind a_Kind = ((cProjectileEntity::eKind) (int) tolua_tonumber(tolua_S,5,0)); + cEntity* a_Creator = ((cEntity*) tolua_tousertype(tolua_S,6,0)); + const Vector3d* a_Speed = ((const Vector3d*) tolua_tousertype(tolua_S,7,NULL)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CreateProjectile'", NULL); +#endif + { + int tolua_ret = (int) self->CreateProjectile(a_PosX,a_PosY,a_PosZ,a_Kind,a_Creator,a_Speed); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CreateProjectile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_Clear00 +static int tolua_AllToLua_cInventory_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyCanFit of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit00 +static int tolua_AllToLua_cInventory_HowManyCanFit00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_ConsiderEmptySlots); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyCanFit of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyCanFit01 +static int tolua_AllToLua_cInventory_HowManyCanFit01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + int a_BeginSlotNum = ((int) tolua_tonumber(tolua_S,3,0)); + int a_EndSlotNum = ((int) tolua_tonumber(tolua_S,4,0)); + bool a_ConsiderEmptySlots = ((bool) tolua_toboolean(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_BeginSlotNum,a_EndSlotNum,a_ConsiderEmptySlots); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cInventory_HowManyCanFit00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItem00 +static int tolua_AllToLua_cInventory_AddItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isboolean(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); + bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,false)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL); +#endif + { + int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_tryToFillEquippedFirst); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_AddItems00 +static int tolua_AllToLua_cInventory_AddItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,0)); + bool a_tryToFillEquippedFirst = ((bool) tolua_toboolean(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL); +#endif + { + int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_tryToFillEquippedFirst); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveOneEquippedItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_RemoveOneEquippedItem00 +static int tolua_AllToLua_cInventory_RemoveOneEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneEquippedItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->RemoveOneEquippedItem(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RemoveOneEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HowManyItems00 +static int tolua_AllToLua_cInventory_HowManyItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyItems(*a_Item); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_HasItems00 +static int tolua_AllToLua_cInventory_HasItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasItems(*a_ItemStack); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetArmorGrid of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorGrid00 +static int tolua_AllToLua_cInventory_GetArmorGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorGrid'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetArmorGrid(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetArmorGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInventoryGrid of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventoryGrid00 +static int tolua_AllToLua_cInventory_GetInventoryGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventoryGrid'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetInventoryGrid(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInventoryGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHotbarGrid of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarGrid00 +static int tolua_AllToLua_cInventory_GetHotbarGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarGrid'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetHotbarGrid(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHotbarGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOwner of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetOwner00 +static int tolua_AllToLua_cInventory_GetOwner00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOwner'", NULL); +#endif + { + cPlayer& tolua_ret = (cPlayer&) self->GetOwner(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cPlayer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOwner'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyToItems of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_CopyToItems00 +static int tolua_AllToLua_cInventory_CopyToItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL); +#endif + { + self->CopyToItems(*a_Items); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetSlot00 +static int tolua_AllToLua_cInventory_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetArmorSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetArmorSlot00 +static int tolua_AllToLua_cInventory_GetArmorSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetArmorSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetArmorSlot(a_ArmorSlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetArmorSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInventorySlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetInventorySlot00 +static int tolua_AllToLua_cInventory_GetInventorySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInventorySlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetInventorySlot(a_InventorySlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInventorySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHotbarSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetHotbarSlot00 +static int tolua_AllToLua_cInventory_GetHotbarSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); + int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHotbarSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetHotbarSlot(a_HotBarSlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHotbarSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedItem00 +static int tolua_AllToLua_cInventory_GetEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedItem'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedItem(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetSlot00 +static int tolua_AllToLua_cInventory_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetArmorSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetArmorSlot00 +static int tolua_AllToLua_cInventory_SetArmorSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_ArmorSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetArmorSlot'", NULL); +#endif + { + self->SetArmorSlot(a_ArmorSlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetArmorSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetInventorySlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetInventorySlot00 +static int tolua_AllToLua_cInventory_SetInventorySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_InventorySlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInventorySlot'", NULL); +#endif + { + self->SetInventorySlot(a_InventorySlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetInventorySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHotbarSlot of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetHotbarSlot00 +static int tolua_AllToLua_cInventory_SetHotbarSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_HotBarSlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHotbarSlot'", NULL); +#endif + { + self->SetHotbarSlot(a_HotBarSlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHotbarSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetEquippedSlotNum of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_SetEquippedSlotNum00 +static int tolua_AllToLua_cInventory_SetEquippedSlotNum00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetEquippedSlotNum'", NULL); +#endif + { + self->SetEquippedSlotNum(a_SlotNum); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetEquippedSlotNum'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedSlotNum of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedSlotNum00 +static int tolua_AllToLua_cInventory_GetEquippedSlotNum00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedSlotNum'", NULL); +#endif + { + int tolua_ret = (int) self->GetEquippedSlotNum(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedSlotNum'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeSlotCount of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_ChangeSlotCount00 +static int tolua_AllToLua_cInventory_ChangeSlotCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",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 + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); +#endif + { + int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageItem00 +static int tolua_AllToLua_cInventory_DamageItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",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 + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + short a_Amount = ((short) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageEquippedItem of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_DamageEquippedItem00 +static int tolua_AllToLua_cInventory_DamageEquippedItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cInventory",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cInventory* self = (cInventory*) tolua_tousertype(tolua_S,1,0); + short a_Amount = ((short) tolua_tonumber(tolua_S,2,1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageEquippedItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageEquippedItem(a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageEquippedItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedHelmet of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedHelmet00 +static int tolua_AllToLua_cInventory_GetEquippedHelmet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedHelmet'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedHelmet(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedHelmet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedChestplate of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedChestplate00 +static int tolua_AllToLua_cInventory_GetEquippedChestplate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedChestplate'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedChestplate(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedChestplate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedLeggings of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedLeggings00 +static int tolua_AllToLua_cInventory_GetEquippedLeggings00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedLeggings'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedLeggings(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedLeggings'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetEquippedBoots of class cInventory */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cInventory_GetEquippedBoots00 +static int tolua_AllToLua_cInventory_GetEquippedBoots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cInventory",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cInventory* self = (const cInventory*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetEquippedBoots'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetEquippedBoots(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetEquippedBoots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00 +static int tolua_AllToLua_cEnchantments_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new00_local +static int tolua_AllToLua_cEnchantments_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01 +static int tolua_AllToLua_cEnchantments_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cEnchantments_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_new01_local +static int tolua_AllToLua_cEnchantments_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cEnchantments* tolua_ret = (cEnchantments*) Mtolua_new((cEnchantments)(a_StringSpec)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cEnchantments"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cEnchantments_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddFromString of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_AddFromString00 +static int tolua_AllToLua_cEnchantments_AddFromString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); + const AString a_StringSpec = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddFromString'", NULL); +#endif + { + self->AddFromString(a_StringSpec); + tolua_pushcppstring(tolua_S,(const char*)a_StringSpec); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddFromString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ToString of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_ToString00 +static int tolua_AllToLua_cEnchantments_ToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ToString'", NULL); +#endif + { + AString tolua_ret = (AString) self->ToString(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLevel of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_GetLevel00 +static int tolua_AllToLua_cEnchantments_GetLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); + int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLevel'", NULL); +#endif + { + int tolua_ret = (int) self->GetLevel(a_EnchantmentID); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetLevel of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_SetLevel00 +static int tolua_AllToLua_cEnchantments_SetLevel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEnchantments",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 + { + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); + int a_EnchantmentID = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Level = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLevel'", NULL); +#endif + { + self->SetLevel(a_EnchantmentID,a_Level); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetLevel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_Clear00 +static int tolua_AllToLua_cEnchantments_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cEnchantments* self = (cEnchantments*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEmpty of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_IsEmpty00 +static int tolua_AllToLua_cEnchantments_IsEmpty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEmpty(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StringToEnchantmentID of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments_StringToEnchantmentID00 +static int tolua_AllToLua_cEnchantments_StringToEnchantmentID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cEnchantments",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_EnchantmentName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + int tolua_ret = (int) cEnchantments::StringToEnchantmentID(a_EnchantmentName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_EnchantmentName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToEnchantmentID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator== of class cEnchantments */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cEnchantments__eq00 +static int tolua_AllToLua_cEnchantments__eq00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cEnchantments",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cEnchantments",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cEnchantments* self = (const cEnchantments*) tolua_tousertype(tolua_S,1,0); + const cEnchantments* a_Other = ((const cEnchantments*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator=='", NULL); +#endif + { + bool tolua_ret = (bool) self->operator==(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.eq'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00 +static int tolua_AllToLua_cItem_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new00_local +static int tolua_AllToLua_cItem_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01 +static int tolua_AllToLua_cItem_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new01_local +static int tolua_AllToLua_cItem_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,1)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02 +static int tolua_AllToLua_cItem_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_pushcppstring(tolua_S,(const char*)a_Enchantments); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cItem_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new02_local +static int tolua_AllToLua_cItem_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + short a_ItemType = ((short) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); + const AString a_Enchantments = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(a_ItemType,a_ItemCount,a_ItemDamage,a_Enchantments)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + tolua_pushcppstring(tolua_S,(const char*)a_Enchantments); + } + } + return 2; +tolua_lerror: + return tolua_AllToLua_cItem_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03 +static int tolua_AllToLua_cItem_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_new03_local +static int tolua_AllToLua_cItem_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItem* a_CopyFrom = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + { + cItem* tolua_ret = (cItem*) Mtolua_new((cItem)(*a_CopyFrom)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItem_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Empty of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Empty00 +static int tolua_AllToLua_cItem_Empty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Empty'", NULL); +#endif + { + self->Empty(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Empty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_Clear00 +static int tolua_AllToLua_cItem_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEmpty of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEmpty00 +static int tolua_AllToLua_cItem_IsEmpty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEmpty(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEmpty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsEqual of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsEqual00 +static int tolua_AllToLua_cItem_IsEqual00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEqual'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsEqual(*a_Item); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsEqual'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSameType of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsSameType00 +static int tolua_AllToLua_cItem_IsSameType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSameType'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSameType(*a_Item); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSameType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyOne of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_CopyOne00 +static int tolua_AllToLua_cItem_CopyOne00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyOne'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->CopyOne(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyOne'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddCount of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_AddCount00 +static int tolua_AllToLua_cItem_AddCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + char a_AmountToAdd = ((char) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCount'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->AddCount(a_AmountToAdd); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxDamage of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxDamage00 +static int tolua_AllToLua_cItem_GetMaxDamage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxDamage'", NULL); +#endif + { + short tolua_ret = (short) self->GetMaxDamage(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxDamage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_DamageItem00 +static int tolua_AllToLua_cItem_DamageItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + short a_Amount = ((short) tolua_tonumber(tolua_S,2,1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsDamageable of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsDamageable00 +static int tolua_AllToLua_cItem_IsDamageable00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDamageable'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsDamageable(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsDamageable'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsStackableWith of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsStackableWith00 +static int tolua_AllToLua_cItem_IsStackableWith00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* a_OtherStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsStackableWith'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsStackableWith(*a_OtherStack); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsStackableWith'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsFullStack of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_IsFullStack00 +static int tolua_AllToLua_cItem_IsFullStack00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsFullStack'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsFullStack(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsFullStack'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxStackSize of class cItem */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItem_GetMaxStackSize00 +static int tolua_AllToLua_cItem_GetMaxStackSize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxStackSize'", NULL); +#endif + { + char tolua_ret = (char) self->GetMaxStackSize(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxStackSize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_ItemType of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemType +static int tolua_get_cItem_m_ItemType(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemType); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_ItemType of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemType +static int tolua_set_cItem_m_ItemType(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemType'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_ItemType = ((short) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_ItemCount of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemCount +static int tolua_get_cItem_m_ItemCount(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemCount); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_ItemCount of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemCount +static int tolua_set_cItem_m_ItemCount(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemCount'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_ItemCount = ((char) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_ItemDamage of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_ItemDamage +static int tolua_get_cItem_m_ItemDamage(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->m_ItemDamage); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_ItemDamage of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_ItemDamage +static int tolua_set_cItem_m_ItemDamage(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_ItemDamage'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_ItemDamage = ((short) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: m_Enchantments of class cItem */ +#ifndef TOLUA_DISABLE_tolua_get_cItem_m_Enchantments +static int tolua_get_cItem_m_Enchantments(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->m_Enchantments,"cEnchantments"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: m_Enchantments of class cItem */ +#ifndef TOLUA_DISABLE_tolua_set_cItem_m_Enchantments +static int tolua_set_cItem_m_Enchantments(lua_State* tolua_S) +{ + cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_Enchantments'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cEnchantments",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->m_Enchantments = *((cEnchantments*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00 +static int tolua_AllToLua_cItems_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItems* tolua_ret = (cItems*) Mtolua_new((cItems)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_new00_local +static int tolua_AllToLua_cItems_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cItems* tolua_ret = (cItems*) Mtolua_new((cItems)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItems"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Get of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Get00 +static int tolua_AllToLua_cItems_Get00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Get'", NULL); +#endif + { + cItem* tolua_ret = (cItem*) self->Get(a_Idx); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set00 +static int tolua_AllToLua_cItems_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_Idx,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Add of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add00 +static int tolua_AllToLua_cItems_Add00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL); +#endif + { + self->Add(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Add'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Delete of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Delete00 +static int tolua_AllToLua_cItems_Delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Delete'", NULL); +#endif + { + self->Delete(a_Idx); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Clear00 +static int tolua_AllToLua_cItems_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Size of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Size00 +static int tolua_AllToLua_cItems_Size00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Size'", NULL); +#endif + { + int tolua_ret = (int) self->Size(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Size'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Set01 +static int tolua_AllToLua_cItems_Set01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + int a_Idx = ((int) tolua_tonumber(tolua_S,2,0)); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,3,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_Idx,a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItems_Set00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Add of class cItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItems_Add01 +static int tolua_AllToLua_cItems_Add01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,3,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Add'", NULL); +#endif + { + self->Add(a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItems_Add00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWidth of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetWidth00 +static int tolua_AllToLua_cItemGrid_GetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); +#endif + { + int tolua_ret = (int) self->GetWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetHeight00 +static int tolua_AllToLua_cItemGrid_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNumSlots of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNumSlots00 +static int tolua_AllToLua_cItemGrid_GetNumSlots00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumSlots'", NULL); +#endif + { + int tolua_ret = (int) self->GetNumSlots(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNumSlots'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlotNum of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlotNum00 +static int tolua_AllToLua_cItemGrid_GetSlotNum00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",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 + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlotNum'", NULL); +#endif + { + int tolua_ret = (int) self->GetSlotNum(a_X,a_Y); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlotNum'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot00 +static int tolua_AllToLua_cItemGrid_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",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 + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetSlot01 +static int tolua_AllToLua_cItemGrid_GetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_GetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot00 +static int tolua_AllToLua_cItemGrid_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_X,a_Y,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot01 +static int tolua_AllToLua_cItemGrid_SetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + short a_ItemType = ((short) tolua_tonumber(tolua_S,4,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,5,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_X,a_Y,a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_SetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot02 +static int tolua_AllToLua_cItemGrid_SetSlot02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_SetSlot01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_SetSlot03 +static int tolua_AllToLua_cItemGrid_SetSlot03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + short a_ItemType = ((short) tolua_tonumber(tolua_S,3,0)); + char a_ItemCount = ((char) tolua_tonumber(tolua_S,4,0)); + short a_ItemDamage = ((short) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,a_ItemType,a_ItemCount,a_ItemDamage); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_SetSlot02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: EmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot00 +static int tolua_AllToLua_cItemGrid_EmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",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 + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL); +#endif + { + self->EmptySlot(a_X,a_Y); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'EmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: EmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_EmptySlot01 +static int tolua_AllToLua_cItemGrid_EmptySlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EmptySlot'", NULL); +#endif + { + self->EmptySlot(a_SlotNum); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cItemGrid_EmptySlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotEmpty of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty00 +static int tolua_AllToLua_cItemGrid_IsSlotEmpty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotEmpty(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotEmpty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotEmpty of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_IsSlotEmpty01 +static int tolua_AllToLua_cItemGrid_IsSlotEmpty01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",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 + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotEmpty'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotEmpty(a_X,a_Y); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_IsSlotEmpty00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_Clear00 +static int tolua_AllToLua_cItemGrid_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyCanFit of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyCanFit00 +static int tolua_AllToLua_cItemGrid_HowManyCanFit00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyCanFit'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyCanFit(*a_ItemStack,a_AllowNewStacks); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyCanFit'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItem00 +static int tolua_AllToLua_cItemGrid_AddItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + cItem* a_ItemStack = ((cItem*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); + int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItem'", NULL); +#endif + { + int tolua_ret = (int) self->AddItem(*a_ItemStack,a_AllowNewStacks,a_PrioritarySlot); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_AddItems00 +static int tolua_AllToLua_cItemGrid_AddItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isboolean(tolua_S,3,1,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + cItems* a_ItemStackList = ((cItems*) tolua_tousertype(tolua_S,2,0)); + bool a_AllowNewStacks = ((bool) tolua_toboolean(tolua_S,3,true)); + int a_PrioritarySlot = ((int) tolua_tonumber(tolua_S,4,-1)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddItems'", NULL); +#endif + { + int tolua_ret = (int) self->AddItems(*a_ItemStackList,a_AllowNewStacks,a_PrioritarySlot); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeSlotCount of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount00 +static int tolua_AllToLua_cItemGrid_ChangeSlotCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",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 + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + int a_AddToCount = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); +#endif + { + int tolua_ret = (int) self->ChangeSlotCount(a_SlotNum,a_AddToCount); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ChangeSlotCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ChangeSlotCount of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_ChangeSlotCount01 +static int tolua_AllToLua_cItemGrid_ChangeSlotCount01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_AddToCount = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ChangeSlotCount'", NULL); +#endif + { + int tolua_ret = (int) self->ChangeSlotCount(a_X,a_Y,a_AddToCount); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_ChangeSlotCount00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveOneItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem00 +static int tolua_AllToLua_cItemGrid_RemoveOneItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->RemoveOneItem(a_SlotNum); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RemoveOneItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RemoveOneItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_RemoveOneItem01 +static int tolua_AllToLua_cItemGrid_RemoveOneItem01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",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 + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RemoveOneItem'", NULL); +#endif + { + cItem tolua_ret = (cItem) self->RemoveOneItem(a_X,a_Y); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cItem)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cItem)); + tolua_pushusertype(tolua_S,tolua_obj,"cItem"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_RemoveOneItem00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HowManyItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HowManyItems00 +static int tolua_AllToLua_cItemGrid_HowManyItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HowManyItems'", NULL); +#endif + { + int tolua_ret = (int) self->HowManyItems(*a_Item); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HowManyItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_HasItems00 +static int tolua_AllToLua_cItemGrid_HasItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + const cItem* a_ItemStack = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasItems'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasItems(*a_ItemStack); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFirstEmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstEmptySlot00 +static int tolua_AllToLua_cItemGrid_GetFirstEmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstEmptySlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetFirstEmptySlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFirstEmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFirstUsedSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetFirstUsedSlot00 +static int tolua_AllToLua_cItemGrid_GetFirstUsedSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFirstUsedSlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetFirstUsedSlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFirstUsedSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLastEmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastEmptySlot00 +static int tolua_AllToLua_cItemGrid_GetLastEmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastEmptySlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetLastEmptySlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLastEmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLastUsedSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetLastUsedSlot00 +static int tolua_AllToLua_cItemGrid_GetLastUsedSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastUsedSlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetLastUsedSlot(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLastUsedSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNextEmptySlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextEmptySlot00 +static int tolua_AllToLua_cItemGrid_GetNextEmptySlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextEmptySlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetNextEmptySlot(a_StartFrom); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNextEmptySlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetNextUsedSlot of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_GetNextUsedSlot00 +static int tolua_AllToLua_cItemGrid_GetNextUsedSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_StartFrom = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNextUsedSlot'", NULL); +#endif + { + int tolua_ret = (int) self->GetNextUsedSlot(a_StartFrom); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNextUsedSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyToItems of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_CopyToItems00 +static int tolua_AllToLua_cItemGrid_CopyToItems00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cItemGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItems",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItemGrid* self = (const cItemGrid*) tolua_tousertype(tolua_S,1,0); + cItems* a_Items = ((cItems*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyToItems'", NULL); +#endif + { + self->CopyToItems(*a_Items); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyToItems'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem00 +static int tolua_AllToLua_cItemGrid_DamageItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",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 + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + short a_Amount = ((short) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_SlotNum,a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DamageItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DamageItem of class cItemGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cItemGrid_DamageItem01 +static int tolua_AllToLua_cItemGrid_DamageItem01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cItemGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cItemGrid* self = (cItemGrid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + short a_Amount = ((short) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DamageItem'", NULL); +#endif + { + bool tolua_ret = (bool) self->DamageItem(a_X,a_Y,a_Amount); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cItemGrid_DamageItem00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosX of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosX00 +static int tolua_AllToLua_cBlockEntity_GetPosX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); +#endif + { + int tolua_ret = (int) self->GetPosX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosY of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosY00 +static int tolua_AllToLua_cBlockEntity_GetPosY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); +#endif + { + int tolua_ret = (int) self->GetPosY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPosZ of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetPosZ00 +static int tolua_AllToLua_cBlockEntity_GetPosZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetPosZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPosZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockType of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetBlockType00 +static int tolua_AllToLua_cBlockEntity_GetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorld of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetWorld00 +static int tolua_AllToLua_cBlockEntity_GetWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetWorld(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkX of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkX00 +static int tolua_AllToLua_cBlockEntity_GetChunkX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkZ of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetChunkZ00 +static int tolua_AllToLua_cBlockEntity_GetChunkZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelX of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelX00 +static int tolua_AllToLua_cBlockEntity_GetRelX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelX'", NULL); +#endif + { + int tolua_ret = (int) self->GetRelX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelZ of class cBlockEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntity_GetRelZ00 +static int tolua_AllToLua_cBlockEntity_GetRelZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntity* self = (const cBlockEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetRelZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot00 +static int tolua_AllToLua_cBlockEntityWithItems_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_SlotNum); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetSlot01 +static int tolua_AllToLua_cBlockEntityWithItems_GetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockEntityWithItems",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 + { + const cBlockEntityWithItems* self = (const cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetSlot(a_X,a_Y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBlockEntityWithItems_GetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot00 +static int tolua_AllToLua_cBlockEntityWithItems_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_SlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_SetSlot01 +static int tolua_AllToLua_cBlockEntityWithItems_SetSlot01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(a_X,a_Y,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cBlockEntityWithItems_SetSlot00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetContents of class cBlockEntityWithItems */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockEntityWithItems_GetContents00 +static int tolua_AllToLua_cBlockEntityWithItems_GetContents00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockEntityWithItems",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockEntityWithItems* self = (cBlockEntityWithItems*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetContents(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddDropSpenserDir of class cDropSpenserEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00 +static int tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_Direction = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddDropSpenserDir'", NULL); +#endif + { + self->AddDropSpenserDir(a_BlockX,a_BlockY,a_BlockZ,a_Direction); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockX); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockY); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockZ); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddDropSpenserDir'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Activate of class cDropSpenserEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_Activate00 +static int tolua_AllToLua_cDropSpenserEntity_Activate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Activate'", NULL); +#endif + { + self->Activate(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Activate'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRedstonePower of class cDropSpenserEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00 +static int tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cDropSpenserEntity",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); + bool a_IsPowered = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRedstonePower'", NULL); +#endif + { + self->SetRedstonePower(a_IsPowered); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRedstonePower'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetInputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetInputSlot00 +static int tolua_AllToLua_cFurnaceEntity_GetInputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetInputSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetInputSlot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetInputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFuelSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelSlot00 +static int tolua_AllToLua_cFurnaceEntity_GetFuelSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetFuelSlot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFuelSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOutputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetOutputSlot00 +static int tolua_AllToLua_cFurnaceEntity_GetOutputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOutputSlot'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetOutputSlot(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOutputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetInputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetInputSlot00 +static int tolua_AllToLua_cFurnaceEntity_SetInputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetInputSlot'", NULL); +#endif + { + self->SetInputSlot(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetInputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetFuelSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetFuelSlot00 +static int tolua_AllToLua_cFurnaceEntity_SetFuelSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetFuelSlot'", NULL); +#endif + { + self->SetFuelSlot(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFuelSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetOutputSlot of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_SetOutputSlot00 +static int tolua_AllToLua_cFurnaceEntity_SetOutputSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cFurnaceEntity",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOutputSlot'", NULL); +#endif + { + self->SetOutputSlot(*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetOutputSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTimeCooked of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetTimeCooked00 +static int tolua_AllToLua_cFurnaceEntity_GetTimeCooked00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTimeCooked'", NULL); +#endif + { + int tolua_ret = (int) self->GetTimeCooked(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTimeCooked'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCookTimeLeft of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00 +static int tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCookTimeLeft'", NULL); +#endif + { + int tolua_ret = (int) self->GetCookTimeLeft(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCookTimeLeft'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFuelBurnTimeLeft of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00 +static int tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetFuelBurnTimeLeft'", NULL); +#endif + { + int tolua_ret = (int) self->GetFuelBurnTimeLeft(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFuelBurnTimeLeft'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasFuelTimeLeft of class cFurnaceEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00 +static int tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cFurnaceEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cFurnaceEntity* self = (const cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasFuelTimeLeft'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasFuelTimeLeft(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasFuelTimeLeft'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_GetRecord00 +static int tolua_AllToLua_cJukeboxEntity_GetRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRecord'", NULL); +#endif + { + int tolua_ret = (int) self->GetRecord(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_SetRecord00 +static int tolua_AllToLua_cJukeboxEntity_SetRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); + int a_Record = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRecord'", NULL); +#endif + { + self->SetRecord(a_Record); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: PlayRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_PlayRecord00 +static int tolua_AllToLua_cJukeboxEntity_PlayRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'PlayRecord'", NULL); +#endif + { + self->PlayRecord(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'PlayRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: EjectRecord of class cJukeboxEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cJukeboxEntity_EjectRecord00 +static int tolua_AllToLua_cJukeboxEntity_EjectRecord00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cJukeboxEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cJukeboxEntity* self = (cJukeboxEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'EjectRecord'", NULL); +#endif + { + self->EjectRecord(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'EjectRecord'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPitch of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_GetPitch00 +static int tolua_AllToLua_cNoteEntity_GetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); +#endif + { + char tolua_ret = (char) self->GetPitch(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPitch of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_SetPitch00 +static int tolua_AllToLua_cNoteEntity_SetPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); + char a_Pitch = ((char) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPitch'", NULL); +#endif + { + self->SetPitch(a_Pitch); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IncrementPitch of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_IncrementPitch00 +static int tolua_AllToLua_cNoteEntity_IncrementPitch00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IncrementPitch'", NULL); +#endif + { + self->IncrementPitch(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IncrementPitch'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MakeSound of class cNoteEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cNoteEntity_MakeSound00 +static int tolua_AllToLua_cNoteEntity_MakeSound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cNoteEntity",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cNoteEntity* self = (cNoteEntity*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MakeSound'", NULL); +#endif + { + self->MakeSound(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeSound'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetLines of class cSignEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLines00 +static int tolua_AllToLua_cSignEntity_SetLines00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_iscppstring(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0); + const AString a_Line1 = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_Line2 = ((const AString) tolua_tocppstring(tolua_S,3,0)); + const AString a_Line3 = ((const AString) tolua_tocppstring(tolua_S,4,0)); + const AString a_Line4 = ((const AString) tolua_tocppstring(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLines'", NULL); +#endif + { + self->SetLines(a_Line1,a_Line2,a_Line3,a_Line4); + tolua_pushcppstring(tolua_S,(const char*)a_Line1); + tolua_pushcppstring(tolua_S,(const char*)a_Line2); + tolua_pushcppstring(tolua_S,(const char*)a_Line3); + tolua_pushcppstring(tolua_S,(const char*)a_Line4); + } + } + return 4; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetLines'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetLine of class cSignEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_SetLine00 +static int tolua_AllToLua_cSignEntity_SetLine00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cSignEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cSignEntity* self = (cSignEntity*) tolua_tousertype(tolua_S,1,0); + int a_Index = ((int) tolua_tonumber(tolua_S,2,0)); + const AString a_Line = ((const AString) tolua_tocppstring(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLine'", NULL); +#endif + { + self->SetLine(a_Index,a_Line); + tolua_pushcppstring(tolua_S,(const char*)a_Line); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetLine'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetLine of class cSignEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cSignEntity_GetLine00 +static int tolua_AllToLua_cSignEntity_GetLine00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cSignEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cSignEntity* self = (const cSignEntity*) tolua_tousertype(tolua_S,1,0); + int a_Index = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLine'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetLine(a_Index); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetLine'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Name of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Name +static int tolua_get_HTTPFormData_Name(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Name); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Name of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Name +static int tolua_set_HTTPFormData_Name(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Name'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Name = ((std::string) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Value of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Value +static int tolua_get_HTTPFormData_Value(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Value); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Value of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Value +static int tolua_set_HTTPFormData_Value(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Value'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Value = ((std::string) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Type of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Type +static int tolua_get_HTTPFormData_Type(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Type); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Type of class HTTPFormData */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPFormData_Type +static int tolua_set_HTTPFormData_Type(lua_State* tolua_S) +{ + HTTPFormData* self = (HTTPFormData*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Type'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Type = ((std::string) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Method of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Method +static int tolua_get_HTTPRequest_Method(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Method); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Method of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Method +static int tolua_set_HTTPRequest_Method(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Method'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Method = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Path of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Path +static int tolua_get_HTTPRequest_Path(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Path); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Path of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Path +static int tolua_set_HTTPRequest_Path(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Path'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Path = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Username of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPRequest_Username +static int tolua_get_HTTPRequest_Username(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Username); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Username of class HTTPRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPRequest_Username +static int tolua_set_HTTPRequest_Username(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Username'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Username = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Request of class HTTPTemplateRequest */ +#ifndef TOLUA_DISABLE_tolua_get_HTTPTemplateRequest_Request +static int tolua_get_HTTPTemplateRequest_Request(lua_State* tolua_S) +{ + HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->Request,"HTTPRequest"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Request of class HTTPTemplateRequest */ +#ifndef TOLUA_DISABLE_tolua_set_HTTPTemplateRequest_Request +static int tolua_set_HTTPTemplateRequest_Request(lua_State* tolua_S) +{ + HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"HTTPRequest",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Request = *((HTTPRequest*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Content of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_Content +static int tolua_get_sWebAdminPage_Content(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->Content); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Content of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_Content +static int tolua_set_sWebAdminPage_Content(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->Content = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: PluginName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_PluginName +static int tolua_get_sWebAdminPage_PluginName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->PluginName); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: PluginName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_PluginName +static int tolua_set_sWebAdminPage_PluginName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->PluginName = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: TabName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_TabName +static int tolua_get_sWebAdminPage_TabName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL); +#endif + tolua_pushcppstring(tolua_S,(const char*)self->TabName); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: TabName of class sWebAdminPage */ +#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_TabName +static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S) +{ + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL); + if (!tolua_iscppstring(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->TabName = ((AString) tolua_tocppstring(tolua_S,2,0)) +; + return 0; +} +#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) +{ +#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) + ) + 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); +#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 + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* 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_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultPage'", NULL); +#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 'GetDefaultPage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBaseURL of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetBaseURL00 +static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,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 AString a_URL = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBaseURL'", NULL); +#endif + { + AString tolua_ret = (AString) self->GetBaseURL(a_URL); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_URL); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBaseURL'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHTMLEscapedString of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00 +static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cWebAdmin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_Input = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + AString tolua_ret = (AString) cWebAdmin::GetHTMLEscapedString(a_Input); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Input); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHTMLEscapedString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWebTitle of class cWebPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_GetWebTitle00 +static int tolua_AllToLua_cWebPlugin_GetWebTitle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWebPlugin",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWebPlugin* self = (const cWebPlugin*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebTitle'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetWebTitle(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWebTitle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HandleWebRequest of class cWebPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_HandleWebRequest00 +static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWebPlugin",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWebPlugin* self = (cWebPlugin*) 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 'HandleWebRequest'", NULL); +#endif + { + AString tolua_ret = (AString) self->HandleWebRequest(a_Request); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HandleWebRequest'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SafeString of class cWebPlugin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_SafeString00 +static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cWebPlugin",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_String = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + AString tolua_ret = (AString) cWebPlugin::SafeString(a_String); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_String); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SafeString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Get of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_Get00 +static int tolua_AllToLua_cRoot_Get00(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 + { + { + cRoot* tolua_ret = (cRoot*) cRoot::Get(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cRoot"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Get'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetServer of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetServer00 +static int tolua_AllToLua_cRoot_GetServer00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServer'", NULL); +#endif + { + cServer* tolua_ret = (cServer*) self->GetServer(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cServer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetServer'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDefaultWorld of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetDefaultWorld00 +static int tolua_AllToLua_cRoot_GetDefaultWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDefaultWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetDefaultWorld(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDefaultWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWorld of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWorld00 +static int tolua_AllToLua_cRoot_GetWorld00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + const AString a_WorldName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); +#endif + { + cWorld* tolua_ret = (cWorld*) self->GetWorld(a_WorldName); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); + tolua_pushcppstring(tolua_S,(const char*)a_WorldName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWorld'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPrimaryServerVersion of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPrimaryServerVersion00 +static int tolua_AllToLua_cRoot_GetPrimaryServerVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cRoot* self = (const cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPrimaryServerVersion'", NULL); +#endif + { + int tolua_ret = (int) self->GetPrimaryServerVersion(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPrimaryServerVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetPrimaryServerVersion of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SetPrimaryServerVersion00 +static int tolua_AllToLua_cRoot_SetPrimaryServerVersion00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + int a_Version = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetPrimaryServerVersion'", NULL); +#endif + { + self->SetPrimaryServerVersion(a_Version); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetPrimaryServerVersion'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetGroupManager of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetGroupManager00 +static int tolua_AllToLua_cRoot_GetGroupManager00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetGroupManager'", NULL); +#endif + { + cGroupManager* tolua_ret = (cGroupManager*) self->GetGroupManager(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cGroupManager"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetGroupManager'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetCraftingRecipes of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetCraftingRecipes00 +static int tolua_AllToLua_cRoot_GetCraftingRecipes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetCraftingRecipes'", NULL); +#endif + { + cCraftingRecipes* tolua_ret = (cCraftingRecipes*) self->GetCraftingRecipes(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingRecipes"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetCraftingRecipes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetFurnaceFuelBurnTime of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetFurnaceFuelBurnTime00 +static int tolua_AllToLua_cRoot_GetFurnaceFuelBurnTime00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cItem* a_Fuel = ((const cItem*) tolua_tousertype(tolua_S,2,0)); + { + int tolua_ret = (int) cRoot::GetFurnaceFuelBurnTime(*a_Fuel); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFurnaceFuelBurnTime'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWebAdmin of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetWebAdmin00 +static int tolua_AllToLua_cRoot_GetWebAdmin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebAdmin'", NULL); +#endif + { + cWebAdmin* tolua_ret = (cWebAdmin*) self->GetWebAdmin(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWebAdmin"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWebAdmin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetPluginManager of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetPluginManager00 +static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPluginManager'", NULL); +#endif + { + cPluginManager* tolua_ret = (cPluginManager*) self->GetPluginManager(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cPluginManager"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetPluginManager'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: QueueExecuteConsoleCommand of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00 +static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueExecuteConsoleCommand'", NULL); +#endif + { + self->QueueExecuteConsoleCommand(a_Cmd); + tolua_pushcppstring(tolua_S,(const char*)a_Cmd); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetTotalChunkCount of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetTotalChunkCount00 +static int tolua_AllToLua_cRoot_GetTotalChunkCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTotalChunkCount'", NULL); +#endif + { + int tolua_ret = (int) self->GetTotalChunkCount(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTotalChunkCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SaveAllChunks of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SaveAllChunks00 +static int tolua_AllToLua_cRoot_SaveAllChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveAllChunks'", NULL); +#endif + { + self->SaveAllChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SaveAllChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: BroadcastChat of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_BroadcastChat00 +static int tolua_AllToLua_cRoot_BroadcastChat00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); +#endif + { + self->BroadcastChat(a_Message); + tolua_pushcppstring(tolua_S,(const char*)a_Message); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetProtocolVersionTextFromInt of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00 +static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_ProtocolVersionNum = ((int) tolua_tonumber(tolua_S,2,0)); + { + AString tolua_ret = (AString) cRoot::GetProtocolVersionTextFromInt(a_ProtocolVersionNum); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetProtocolVersionTextFromInt'.",&tolua_err); + return 0; +#endif +} +#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) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new00_local +static int tolua_AllToLua_Vector3f_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01 +static int tolua_AllToLua_Vector3f_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new01_local +static int tolua_AllToLua_Vector3f_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02 +static int tolua_AllToLua_Vector3f_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new02_local +static int tolua_AllToLua_Vector3f_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03 +static int tolua_AllToLua_Vector3f_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new03_local +static int tolua_AllToLua_Vector3f_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04 +static int tolua_AllToLua_Vector3f_new04(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new03(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new04_local +static int tolua_AllToLua_Vector3f_new04_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new03_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05 +static int tolua_AllToLua_Vector3f_new05(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + float a_x = ((float) tolua_tonumber(tolua_S,2,0)); + float a_y = ((float) tolua_tonumber(tolua_S,3,0)); + float a_z = ((float) tolua_tonumber(tolua_S,4,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new04(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_new05_local +static int tolua_AllToLua_Vector3f_new05_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + float a_x = ((float) tolua_tonumber(tolua_S,2,0)); + float a_y = ((float) tolua_tonumber(tolua_S,3,0)); + float a_z = ((float) tolua_tonumber(tolua_S,4,0)); + { + Vector3f* tolua_ret = (Vector3f*) Mtolua_new((Vector3f)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f_new04_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Set00 +static int tolua_AllToLua_Vector3f_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); + float a_x = ((float) tolua_tonumber(tolua_S,2,0)); + float a_y = ((float) tolua_tonumber(tolua_S,3,0)); + float a_z = ((float) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_x,a_y,a_z); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Normalize of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Normalize00 +static int tolua_AllToLua_Vector3f_Normalize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL); +#endif + { + self->Normalize(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy00 +static int tolua_AllToLua_Vector3f_NormalizeCopy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->NormalizeCopy(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_NormalizeCopy01 +static int tolua_AllToLua_Vector3f_NormalizeCopy01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + Vector3f* a_V = ((Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + self->NormalizeCopy(*a_V); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_Vector3f_NormalizeCopy00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Length of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Length00 +static int tolua_AllToLua_Vector3f_Length00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); +#endif + { + float tolua_ret = (float) self->Length(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SqrLength of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_SqrLength00 +static int tolua_AllToLua_Vector3f_SqrLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); +#endif + { + float tolua_ret = (float) self->SqrLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dot of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Dot00 +static int tolua_AllToLua_Vector3f_Dot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_V = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL); +#endif + { + float tolua_ret = (float) self->Dot(*a_V); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Cross of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Cross00 +static int tolua_AllToLua_Vector3f_Cross00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->Cross(*v); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f_Equals00 +static int tolua_AllToLua_Vector3f_Equals00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add00 +static int tolua_AllToLua_Vector3f__add00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator+(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__add01 +static int tolua_AllToLua_Vector3f__add01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator+(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f__add00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub00 +static int tolua_AllToLua_Vector3f__sub00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator-(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__sub01 +static int tolua_AllToLua_Vector3f__sub01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator-(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f__sub00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul00 +static int tolua_AllToLua_Vector3f__mul00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const float f = ((const float) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator*(f); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3f__mul01 +static int tolua_AllToLua_Vector3f__mul01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3f",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* self = (const Vector3f*) tolua_tousertype(tolua_S,1,0); + const Vector3f* v2 = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3f tolua_ret = (Vector3f) self->operator*(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3f)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3f)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3f"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3f__mul00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: x of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3f_x +static int tolua_get_Vector3f_x(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3f_x +static int tolua_set_Vector3f_x(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((float) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3f_y +static int tolua_get_Vector3f_y(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3f_y +static int tolua_set_Vector3f_y(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((float) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: z of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3f_z +static int tolua_get_Vector3f_z(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->z); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: z of class Vector3f */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3f_z +static int tolua_set_Vector3f_z(lua_State* tolua_S) +{ + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->z = ((float) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00 +static int tolua_AllToLua_Vector3d_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new00_local +static int tolua_AllToLua_Vector3d_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01 +static int tolua_AllToLua_Vector3d_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new01_local +static int tolua_AllToLua_Vector3d_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3f* v = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02 +static int tolua_AllToLua_Vector3d_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new02_local +static int tolua_AllToLua_Vector3d_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03 +static int tolua_AllToLua_Vector3d_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + 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)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_new03_local +static int tolua_AllToLua_Vector3d_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + 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)); + { + Vector3d* tolua_ret = (Vector3d*) Mtolua_new((Vector3d)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Set00 +static int tolua_AllToLua_Vector3d_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,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)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_x,a_y,a_z); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Normalize of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Normalize00 +static int tolua_AllToLua_Vector3d_Normalize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Normalize'", NULL); +#endif + { + self->Normalize(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Normalize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy00 +static int tolua_AllToLua_Vector3d_NormalizeCopy00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->NormalizeCopy(); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'NormalizeCopy'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: NormalizeCopy of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_NormalizeCopy01 +static int tolua_AllToLua_Vector3d_NormalizeCopy01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); + Vector3d* a_V = ((Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'NormalizeCopy'", NULL); +#endif + { + self->NormalizeCopy(*a_V); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_Vector3d_NormalizeCopy00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Length of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Length00 +static int tolua_AllToLua_Vector3d_Length00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); +#endif + { + double tolua_ret = (double) self->Length(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SqrLength of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_SqrLength00 +static int tolua_AllToLua_Vector3d_SqrLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); +#endif + { + double tolua_ret = (double) self->SqrLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dot of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Dot00 +static int tolua_AllToLua_Vector3d_Dot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_V = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dot'", NULL); +#endif + { + double tolua_ret = (double) self->Dot(*a_V); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Cross of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Cross00 +static int tolua_AllToLua_Vector3d_Cross00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Cross'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->Cross(*v); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Cross'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LineCoeffToXYPlane of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXYPlane00 +static int tolua_AllToLua_Vector3d_LineCoeffToXYPlane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXYPlane'", NULL); +#endif + { + double tolua_ret = (double) self->LineCoeffToXYPlane(*a_OtherEnd,a_Z); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LineCoeffToXYPlane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LineCoeffToXZPlane of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToXZPlane00 +static int tolua_AllToLua_Vector3d_LineCoeffToXZPlane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToXZPlane'", NULL); +#endif + { + double tolua_ret = (double) self->LineCoeffToXZPlane(*a_OtherEnd,a_Y); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LineCoeffToXZPlane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LineCoeffToYZPlane of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_LineCoeffToYZPlane00 +static int tolua_AllToLua_Vector3d_LineCoeffToYZPlane00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_OtherEnd = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_X = ((double) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LineCoeffToYZPlane'", NULL); +#endif + { + double tolua_ret = (double) self->LineCoeffToYZPlane(*a_OtherEnd,a_X); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LineCoeffToYZPlane'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d_Equals00 +static int tolua_AllToLua_Vector3d_Equals00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add00 +static int tolua_AllToLua_Vector3d__add00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator+(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.add'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator+ of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__add01 +static int tolua_AllToLua_Vector3d__add01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator+'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator+(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d__add00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub00 +static int tolua_AllToLua_Vector3d__sub00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator-(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.sub'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator- of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__sub01 +static int tolua_AllToLua_Vector3d__sub01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator-'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator-(v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d__sub00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul00 +static int tolua_AllToLua_Vector3d__mul00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const double f = ((const double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator*(f); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.mul'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator* of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__mul01 +static int tolua_AllToLua_Vector3d__mul01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v2 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator*'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator*(*v2); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3d__mul00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator/ of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3d__div00 +static int tolua_AllToLua_Vector3d__div00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3d",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* self = (const Vector3d*) tolua_tousertype(tolua_S,1,0); + const double f = ((const double) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator/'", NULL); +#endif + { + Vector3d tolua_ret = (Vector3d) self->operator/(f); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((Vector3d)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Vector3d)); + tolua_pushusertype(tolua_S,tolua_obj,"Vector3d"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.div'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: x of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_x +static int tolua_get_Vector3d_x(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3d_x +static int tolua_set_Vector3d_x(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_y +static int tolua_get_Vector3d_y(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3d_y +static int tolua_set_Vector3d_y(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: z of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_z +static int tolua_get_Vector3d_z(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->z); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: z of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3d_z +static int tolua_set_Vector3d_z(lua_State* tolua_S) +{ + Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->z = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: EPS of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_EPS +static int tolua_get_Vector3d_EPS(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)Vector3d::EPS); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: NO_INTERSECTION of class Vector3d */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3d_NO_INTERSECTION +static int tolua_get_Vector3d_NO_INTERSECTION(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)Vector3d::NO_INTERSECTION); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00 +static int tolua_AllToLua_Vector3i_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new00_local +static int tolua_AllToLua_Vector3i_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(*v)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01 +static int tolua_AllToLua_Vector3i_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new01_local +static int tolua_AllToLua_Vector3i_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else + { + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02 +static int tolua_AllToLua_Vector3i_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_x = ((int) tolua_tonumber(tolua_S,2,0)); + int a_y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_z = ((int) tolua_tonumber(tolua_S,4,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_new02_local +static int tolua_AllToLua_Vector3i_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_x = ((int) tolua_tonumber(tolua_S,2,0)); + int a_y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_z = ((int) tolua_tonumber(tolua_S,4,0)); + { + Vector3i* tolua_ret = (Vector3i*) Mtolua_new((Vector3i)(a_x,a_y,a_z)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Vector3i"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Set of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Set00 +static int tolua_AllToLua_Vector3i_Set00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Vector3i",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); + int a_x = ((int) tolua_tonumber(tolua_S,2,0)); + int a_y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_z = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Set'", NULL); +#endif + { + self->Set(a_x,a_y,a_z); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Set'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Length of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Length00 +static int tolua_AllToLua_Vector3i_Length00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Length'", NULL); +#endif + { + float tolua_ret = (float) self->Length(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Length'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SqrLength of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_SqrLength00 +static int tolua_AllToLua_Vector3i_SqrLength00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SqrLength'", NULL); +#endif + { + int tolua_ret = (int) self->SqrLength(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SqrLength'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals00 +static int tolua_AllToLua_Vector3i_Equals00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Equals'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Equals of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_Vector3i_Equals01 +static int tolua_AllToLua_Vector3i_Equals01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Vector3i",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* self = (const Vector3i*) tolua_tousertype(tolua_S,1,0); + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); +#endif + { + bool tolua_ret = (bool) self->Equals(v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_Vector3i_Equals00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: x of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3i_x +static int tolua_get_Vector3i_x(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3i_x +static int tolua_set_Vector3i_x(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3i_y +static int tolua_get_Vector3i_y(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3i_y +static int tolua_set_Vector3i_y(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: z of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_get_Vector3i_z +static int tolua_get_Vector3i_z(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->z); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: z of class Vector3i */ +#ifndef TOLUA_DISABLE_tolua_set_Vector3i_z +static int tolua_set_Vector3i_z(lua_State* tolua_S) +{ + Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'z'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->z = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: p1 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p1 +static int tolua_get_cCuboid_p1(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->p1,"Vector3i"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: p1 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p1 +static int tolua_set_cCuboid_p1(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p1'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->p1 = *((Vector3i*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: p2 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_get_cCuboid_p2 +static int tolua_get_cCuboid_p2(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->p2,"Vector3i"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: p2 of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_set_cCuboid_p2 +static int tolua_set_cCuboid_p2(lua_State* tolua_S) +{ + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'p2'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3i",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->p2 = *((Vector3i*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00 +static int tolua_AllToLua_cCuboid_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new00_local +static int tolua_AllToLua_cCuboid_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01 +static int tolua_AllToLua_cCuboid_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new01_local +static int tolua_AllToLua_cCuboid_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* a_Cuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_Cuboid)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02 +static int tolua_AllToLua_cCuboid_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new02_local +static int tolua_AllToLua_cCuboid_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3i* a_p1 = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); + const Vector3i* a_p2 = ((const Vector3i*) tolua_tousertype(tolua_S,3,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(*a_p1,*a_p2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03 +static int tolua_AllToLua_cCuboid_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new03_local +static int tolua_AllToLua_cCuboid_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04 +static int tolua_AllToLua_cCuboid_new04(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new03(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_new04_local +static int tolua_AllToLua_cCuboid_new04_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else + { + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); + { + cCuboid* tolua_ret = (cCuboid*) Mtolua_new((cCuboid)(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCuboid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_new03_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Assign of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Assign00 +static int tolua_AllToLua_cCuboid_Assign00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); + int a_X1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_X2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_Y2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_Z2 = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Assign'", NULL); +#endif + { + self->Assign(a_X1,a_Y1,a_Z1,a_X2,a_Y2,a_Z2); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Assign'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Sort of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Sort00 +static int tolua_AllToLua_cCuboid_Sort00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Sort'", NULL); +#endif + { + self->Sort(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Sort'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DifX of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifX00 +static int tolua_AllToLua_cCuboid_DifX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifX'", NULL); +#endif + { + int tolua_ret = (int) self->DifX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DifX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DifY of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifY00 +static int tolua_AllToLua_cCuboid_DifY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifY'", NULL); +#endif + { + int tolua_ret = (int) self->DifY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DifY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DifZ of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DifZ00 +static int tolua_AllToLua_cCuboid_DifZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DifZ'", NULL); +#endif + { + int tolua_ret = (int) self->DifZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DifZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DoesIntersect of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_DoesIntersect00 +static int tolua_AllToLua_cCuboid_DoesIntersect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_Other = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL); +#endif + { + bool tolua_ret = (bool) self->DoesIntersect(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside00 +static int tolua_AllToLua_cCuboid_IsInside00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3i",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const Vector3i* v = ((const Vector3i*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside01 +static int tolua_AllToLua_cCuboid_IsInside01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + int a_X = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Y = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Z = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_IsInside00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsInside02 +static int tolua_AllToLua_cCuboid_IsInside02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const Vector3d* v = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*v); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cCuboid_IsInside01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsCompletelyInside of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsCompletelyInside00 +static int tolua_AllToLua_cCuboid_IsCompletelyInside00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_Outer = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsCompletelyInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsCompletelyInside(*a_Outer); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsCompletelyInside'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Move of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_Move00 +static int tolua_AllToLua_cCuboid_Move00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCuboid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCuboid* self = (cCuboid*) tolua_tousertype(tolua_S,1,0); + int a_OfsX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_OfsY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_OfsZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); +#endif + { + self->Move(a_OfsX,a_OfsY,a_OfsZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSorted of class cCuboid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCuboid_IsSorted00 +static int tolua_AllToLua_cCuboid_IsSorted00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCuboid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCuboid* self = (const cCuboid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSorted'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSorted(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSorted'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00 +static int tolua_AllToLua_cBoundingBox_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_MinX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_MinY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0)); + double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0)); + double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new00_local +static int tolua_AllToLua_cBoundingBox_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double a_MinX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_MaxX = ((double) tolua_tonumber(tolua_S,3,0)); + double a_MinY = ((double) tolua_tonumber(tolua_S,4,0)); + double a_MaxY = ((double) tolua_tonumber(tolua_S,5,0)); + double a_MinZ = ((double) tolua_tonumber(tolua_S,6,0)); + double a_MaxZ = ((double) tolua_tonumber(tolua_S,7,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01 +static int tolua_AllToLua_cBoundingBox_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new01_local +static int tolua_AllToLua_cBoundingBox_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Min,*a_Max)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02 +static int tolua_AllToLua_cBoundingBox_new02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Radius = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Height = ((double) tolua_tonumber(tolua_S,4,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new02_local +static int tolua_AllToLua_cBoundingBox_new02_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Pos = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + double a_Radius = ((double) tolua_tonumber(tolua_S,3,0)); + double a_Height = ((double) tolua_tonumber(tolua_S,4,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Pos,a_Radius,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new01_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03 +static int tolua_AllToLua_cBoundingBox_new03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_new03_local +static int tolua_AllToLua_cBoundingBox_new03_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const cBoundingBox* a_Orig = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); + { + cBoundingBox* tolua_ret = (cBoundingBox*) Mtolua_new((cBoundingBox)(*a_Orig)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_new02_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Move of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move00 +static int tolua_AllToLua_cBoundingBox_Move00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + double a_OffX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_OffY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_OffZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); +#endif + { + self->Move(a_OffX,a_OffY,a_OffZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Move'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Move of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Move01 +static int tolua_AllToLua_cBoundingBox_Move01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Off = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Move'", NULL); +#endif + { + self->Move(*a_Off); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_Move00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Expand of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Expand00 +static int tolua_AllToLua_cBoundingBox_Expand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + double a_ExpandX = ((double) tolua_tonumber(tolua_S,2,0)); + double a_ExpandY = ((double) tolua_tonumber(tolua_S,3,0)); + double a_ExpandZ = ((double) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL); +#endif + { + self->Expand(a_ExpandX,a_ExpandY,a_ExpandZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DoesIntersect of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_DoesIntersect00 +static int tolua_AllToLua_cBoundingBox_DoesIntersect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DoesIntersect'", NULL); +#endif + { + bool tolua_ret = (bool) self->DoesIntersect(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DoesIntersect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Union of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_Union00 +static int tolua_AllToLua_cBoundingBox_Union00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const cBoundingBox* a_Other = ((const cBoundingBox*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Union'", NULL); +#endif + { + cBoundingBox tolua_ret = (cBoundingBox) self->Union(*a_Other); + { +#ifdef __cplusplus + void* tolua_obj = Mtolua_new((cBoundingBox)(tolua_ret)); + tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(cBoundingBox)); + tolua_pushusertype(tolua_S,tolua_obj,"cBoundingBox"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Union'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside00 +static int tolua_AllToLua_cBoundingBox_IsInside00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*a_Point); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsInside'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside01 +static int tolua_AllToLua_cBoundingBox_IsInside01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,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)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside02 +static int tolua_AllToLua_cBoundingBox_IsInside02(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBoundingBox",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + cBoundingBox* a_Other = ((cBoundingBox*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*a_Other); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside01(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside03 +static int tolua_AllToLua_cBoundingBox_IsInside03(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsInside'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsInside(*a_Min,*a_Max); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside02(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside04 +static int tolua_AllToLua_cBoundingBox_IsInside04(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + const Vector3d* a_Point = ((const Vector3d*) tolua_tousertype(tolua_S,4,0)); + { + bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,*a_Point); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside03(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsInside of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_IsInside05 +static int tolua_AllToLua_cBoundingBox_IsInside05(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + double a_X = ((double) tolua_tonumber(tolua_S,4,0)); + double a_Y = ((double) tolua_tonumber(tolua_S,5,0)); + double a_Z = ((double) tolua_tonumber(tolua_S,6,0)); + { + bool tolua_ret = (bool) cBoundingBox::IsInside(*a_Min,*a_Max,a_X,a_Y,a_Z); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_IsInside04(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CalcLineIntersection of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection00 +static int tolua_AllToLua_cBoundingBox_CalcLineIntersection00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBoundingBox* self = (cBoundingBox*) tolua_tousertype(tolua_S,1,0); + const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + double a_LineCoeff = ((double) tolua_tonumber(tolua_S,4,0)); + char a_Face = ((char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CalcLineIntersection'", NULL); +#endif + { + bool tolua_ret = (bool) self->CalcLineIntersection(*a_Line1,*a_Line2,a_LineCoeff,a_Face); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff); + tolua_pushnumber(tolua_S,(lua_Number)a_Face); + } + } + return 3; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CalcLineIntersection'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CalcLineIntersection of class cBoundingBox */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBoundingBox_CalcLineIntersection01 +static int tolua_AllToLua_cBoundingBox_CalcLineIntersection01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBoundingBox",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const Vector3d",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,5,&tolua_err) || !tolua_isusertype(tolua_S,5,"const Vector3d",0,&tolua_err)) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else + { + const Vector3d* a_Min = ((const Vector3d*) tolua_tousertype(tolua_S,2,0)); + const Vector3d* a_Max = ((const Vector3d*) tolua_tousertype(tolua_S,3,0)); + const Vector3d* a_Line1 = ((const Vector3d*) tolua_tousertype(tolua_S,4,0)); + const Vector3d* a_Line2 = ((const Vector3d*) tolua_tousertype(tolua_S,5,0)); + double a_LineCoeff = ((double) tolua_tonumber(tolua_S,6,0)); + char a_Face = ((char) tolua_tonumber(tolua_S,7,0)); + { + bool tolua_ret = (bool) cBoundingBox::CalcLineIntersection(*a_Min,*a_Max,*a_Line1,*a_Line2,a_LineCoeff,a_Face); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushnumber(tolua_S,(lua_Number)a_LineCoeff); + tolua_pushnumber(tolua_S,(lua_Number)a_Face); + } + } + return 3; +tolua_lerror: + return tolua_AllToLua_cBoundingBox_CalcLineIntersection00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: BlockHitPosition of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_get_cTracer_BlockHitPosition +static int tolua_get_cTracer_BlockHitPosition(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->BlockHitPosition,"Vector3f"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: BlockHitPosition of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_set_cTracer_BlockHitPosition +static int tolua_set_cTracer_BlockHitPosition(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'BlockHitPosition'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->BlockHitPosition = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: HitNormal of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_get_cTracer_HitNormal +static int tolua_get_cTracer_HitNormal(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->HitNormal,"Vector3f"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: HitNormal of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_set_cTracer_HitNormal +static int tolua_set_cTracer_HitNormal(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'HitNormal'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->HitNormal = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: RealHit of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_get_cTracer_RealHit +static int tolua_get_cTracer_RealHit(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->RealHit,"Vector3f"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: RealHit of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_set_cTracer_RealHit +static int tolua_set_cTracer_RealHit(lua_State* tolua_S) +{ + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'RealHit'",NULL); + if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"Vector3f",0,&tolua_err))) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->RealHit = *((Vector3f*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00 +static int tolua_AllToLua_cTracer_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + { + cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_new00_local +static int tolua_AllToLua_cTracer_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cTracer",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + { + cTracer* tolua_ret = (cTracer*) Mtolua_new((cTracer)(a_World)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cTracer"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_delete00 +static int tolua_AllToLua_cTracer_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); +#endif + Mtolua_delete(self); + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Trace of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace00 +static int tolua_AllToLua_cTracer_Trace00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0)); + int a_Distance = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL); +#endif + { + bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Trace'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Trace of class cTracer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cTracer_Trace01 +static int tolua_AllToLua_cTracer_Trace01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cTracer",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const Vector3f",0,&tolua_err)) || + (tolua_isvaluenil(tolua_S,3,&tolua_err) || !tolua_isusertype(tolua_S,3,"const Vector3f",0,&tolua_err)) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cTracer* self = (cTracer*) tolua_tousertype(tolua_S,1,0); + const Vector3f* a_Start = ((const Vector3f*) tolua_tousertype(tolua_S,2,0)); + const Vector3f* a_Direction = ((const Vector3f*) tolua_tousertype(tolua_S,3,0)); + int a_Distance = ((int) tolua_tonumber(tolua_S,4,0)); + bool a_LineOfSight = ((bool) tolua_toboolean(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Trace'", NULL); +#endif + { + bool tolua_ret = (bool) self->Trace(*a_Start,*a_Direction,a_Distance,a_LineOfSight); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cTracer_Trace00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetName of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetName00 +static int tolua_AllToLua_cGroup_SetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Name = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetName'", NULL); +#endif + { + self->SetName(a_Name); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetName of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetName00 +static int tolua_AllToLua_cGroup_GetName00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); +#endif + { + const std::string tolua_ret = (const std::string) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetName'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetColor of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_SetColor00 +static int tolua_AllToLua_cGroup_SetColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Color = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetColor'", NULL); +#endif + { + self->SetColor(a_Color); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddCommand of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddCommand00 +static int tolua_AllToLua_cGroup_AddCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddCommand'", NULL); +#endif + { + self->AddCommand(a_Command); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: AddPermission of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_AddPermission00 +static int tolua_AllToLua_cGroup_AddPermission00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Permission = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'AddPermission'", NULL); +#endif + { + self->AddPermission(a_Permission); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddPermission'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: InheritFrom of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_InheritFrom00 +static int tolua_AllToLua_cGroup_InheritFrom00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cGroup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + cGroup* a_Group = ((cGroup*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'InheritFrom'", NULL); +#endif + { + self->InheritFrom(a_Group); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'InheritFrom'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasCommand of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_HasCommand00 +static int tolua_AllToLua_cGroup_HasCommand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cGroup",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cGroup* self = (cGroup*) tolua_tousertype(tolua_S,1,0); + std::string a_Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasCommand'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasCommand(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasCommand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetColor of class cGroup */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cGroup_GetColor00 +static int tolua_AllToLua_cGroup_GetColor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cGroup",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cGroup* self = (const cGroup*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetColor(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetColor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00 +static int tolua_AllToLua_cBlockArea_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_new00_local +static int tolua_AllToLua_cBlockArea_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + cBlockArea* tolua_ret = (cBlockArea*) Mtolua_new((cBlockArea)()); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockArea"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_delete00 +static int tolua_AllToLua_cBlockArea_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); +#endif + Mtolua_delete(self); + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Clear00 +static int tolua_AllToLua_cBlockArea_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Create of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create00 +static int tolua_AllToLua_cBlockArea_Create00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL); +#endif + { + self->Create(a_SizeX,a_SizeY,a_SizeZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Create'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Create of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Create01 +static int tolua_AllToLua_cBlockArea_Create01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_SizeX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SizeY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SizeZ = ((int) tolua_tonumber(tolua_S,4,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Create'", NULL); +#endif + { + self->Create(a_SizeX,a_SizeY,a_SizeZ,a_DataTypes); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cBlockArea_Create00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetOrigin of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetOrigin00 +static int tolua_AllToLua_cBlockArea_SetOrigin00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_OriginX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_OriginY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_OriginZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetOrigin'", NULL); +#endif + { + self->SetOrigin(a_OriginX,a_OriginY,a_OriginZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetOrigin'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Read of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read00 +static int tolua_AllToLua_cBlockArea_Read00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL); +#endif + { + bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Read'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Read of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Read01 +static int tolua_AllToLua_cBlockArea_Read01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MaxBlockX = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MaxBlockY = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_MaxBlockZ = ((int) tolua_tonumber(tolua_S,8,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,9,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Read'", NULL); +#endif + { + bool tolua_ret = (bool) self->Read(a_World,a_MinBlockX,a_MaxBlockX,a_MinBlockY,a_MaxBlockY,a_MinBlockZ,a_MaxBlockZ,a_DataTypes); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBlockArea_Read00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Write of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write00 +static int tolua_AllToLua_cBlockArea_Write00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL); +#endif + { + bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Write'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Write of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Write01 +static int tolua_AllToLua_cBlockArea_Write01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"cWorld",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + cWorld* a_World = ((cWorld*) tolua_tousertype(tolua_S,2,0)); + int a_MinBlockX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinBlockY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinBlockZ = ((int) tolua_tonumber(tolua_S,5,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Write'", NULL); +#endif + { + bool tolua_ret = (bool) self->Write(a_World,a_MinBlockX,a_MinBlockY,a_MinBlockZ,a_DataTypes); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_AllToLua_cBlockArea_Write00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyTo of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyTo00 +static int tolua_AllToLua_cBlockArea_CopyTo00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + cBlockArea* a_Into = ((cBlockArea*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyTo'", NULL); +#endif + { + self->CopyTo(*a_Into); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyTo'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: CopyFrom of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_CopyFrom00 +static int tolua_AllToLua_cBlockArea_CopyFrom00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const cBlockArea* a_From = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'CopyFrom'", NULL); +#endif + { + self->CopyFrom(*a_From); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CopyFrom'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: DumpToRawFile of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_DumpToRawFile00 +static int tolua_AllToLua_cBlockArea_DumpToRawFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'DumpToRawFile'", NULL); +#endif + { + self->DumpToRawFile(a_FileName); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DumpToRawFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: LoadFromSchematicFile of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_LoadFromSchematicFile00 +static int tolua_AllToLua_cBlockArea_LoadFromSchematicFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'LoadFromSchematicFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->LoadFromSchematicFile(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadFromSchematicFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SaveToSchematicFile of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SaveToSchematicFile00 +static int tolua_AllToLua_cBlockArea_SaveToSchematicFile00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const AString a_FileName = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveToSchematicFile'", NULL); +#endif + { + bool tolua_ret = (bool) self->SaveToSchematicFile(a_FileName); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_FileName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SaveToSchematicFile'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Crop of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Crop00 +static int tolua_AllToLua_cBlockArea_Crop00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_AddMinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_SubMaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_AddMinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_SubMaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_AddMinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_SubMaxZ = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Crop'", NULL); +#endif + { + self->Crop(a_AddMinX,a_SubMaxX,a_AddMinY,a_SubMaxY,a_AddMinZ,a_SubMaxZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Crop'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Expand of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Expand00 +static int tolua_AllToLua_cBlockArea_Expand00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_SubMinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_AddMaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SubMinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_AddMaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_SubMinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_AddMaxZ = ((int) tolua_tonumber(tolua_S,7,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Expand'", NULL); +#endif + { + self->Expand(a_SubMinX,a_AddMaxX,a_SubMinY,a_AddMaxY,a_SubMinZ,a_AddMaxZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Expand'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Merge of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Merge00 +static int tolua_AllToLua_cBlockArea_Merge00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + const cBlockArea* a_Src = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); + int a_RelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0)); + cBlockArea::eMergeStrategy a_Strategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Merge'", NULL); +#endif + { + self->Merge(*a_Src,a_RelX,a_RelY,a_RelZ,a_Strategy); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Merge'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Fill of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_Fill00 +static int tolua_AllToLua_cBlockArea_Fill00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,1,&tolua_err) || + !tolua_isnumber(tolua_S,5,1,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,6,0x0f)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Fill'", NULL); +#endif + { + self->Fill(a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Fill'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillRelCuboid of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_FillRelCuboid00 +static int tolua_AllToLua_cBlockArea_FillRelCuboid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,1,&tolua_err) || + !tolua_isnumber(tolua_S,11,1,&tolua_err) || + !tolua_isnumber(tolua_S,12,1,&tolua_err) || + !tolua_isnoobj(tolua_S,13,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_MinRelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxRelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinRelY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxRelY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinRelZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); +#endif + { + self->FillRelCuboid(a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RelLine of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RelLine00 +static int tolua_AllToLua_cBlockArea_RelLine00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,1,&tolua_err) || + !tolua_isnumber(tolua_S,11,1,&tolua_err) || + !tolua_isnumber(tolua_S,12,1,&tolua_err) || + !tolua_isnoobj(tolua_S,13,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX1 = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY1 = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ1 = ((int) tolua_tonumber(tolua_S,4,0)); + int a_RelX2 = ((int) tolua_tonumber(tolua_S,5,0)); + int a_RelY2 = ((int) tolua_tonumber(tolua_S,6,0)); + int a_RelZ2 = ((int) tolua_tonumber(tolua_S,7,0)); + int a_DataTypes = ((int) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,10,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,11,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,12,0x0f)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RelLine'", NULL); +#endif + { + self->RelLine(a_RelX1,a_RelY1,a_RelZ1,a_RelX2,a_RelY2,a_RelZ2,a_DataTypes,a_BlockType,a_BlockMeta,a_BlockLight,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RelLine'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCCW of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCW00 +static int tolua_AllToLua_cBlockArea_RotateCCW00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCW'", NULL); +#endif + { + self->RotateCCW(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCCW'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCW of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCW00 +static int tolua_AllToLua_cBlockArea_RotateCW00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCW'", NULL); +#endif + { + self->RotateCW(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCW'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXY of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXY00 +static int tolua_AllToLua_cBlockArea_MirrorXY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXY'", NULL); +#endif + { + self->MirrorXY(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZ00 +static int tolua_AllToLua_cBlockArea_MirrorXZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZ'", NULL); +#endif + { + self->MirrorXZ(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorYZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZ00 +static int tolua_AllToLua_cBlockArea_MirrorYZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZ'", NULL); +#endif + { + self->MirrorYZ(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorYZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCCWNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCCWNoMeta00 +static int tolua_AllToLua_cBlockArea_RotateCCWNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCCWNoMeta'", NULL); +#endif + { + self->RotateCCWNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCCWNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RotateCWNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_RotateCWNoMeta00 +static int tolua_AllToLua_cBlockArea_RotateCWNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RotateCWNoMeta'", NULL); +#endif + { + self->RotateCWNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RotateCWNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXYNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXYNoMeta00 +static int tolua_AllToLua_cBlockArea_MirrorXYNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXYNoMeta'", NULL); +#endif + { + self->MirrorXYNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXYNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorXZNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorXZNoMeta00 +static int tolua_AllToLua_cBlockArea_MirrorXZNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorXZNoMeta'", NULL); +#endif + { + self->MirrorXZNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorXZNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MirrorYZNoMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_MirrorYZNoMeta00 +static int tolua_AllToLua_cBlockArea_MirrorYZNoMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'MirrorYZNoMeta'", NULL); +#endif + { + self->MirrorYZNoMeta(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MirrorYZNoMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockType00 +static int tolua_AllToLua_cBlockArea_SetRelBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockType'", NULL); +#endif + { + self->SetRelBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockType00 +static int tolua_AllToLua_cBlockArea_SetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL); +#endif + { + self->SetBlockType(a_BlockX,a_BlockY,a_BlockZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockMeta00 +static int tolua_AllToLua_cBlockArea_SetRelBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockMeta'", NULL); +#endif + { + self->SetRelBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockMeta00 +static int tolua_AllToLua_cBlockArea_SetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockLight00 +static int tolua_AllToLua_cBlockArea_SetRelBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockLight'", NULL); +#endif + { + self->SetRelBlockLight(a_RelX,a_RelY,a_RelZ,a_BlockLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockLight00 +static int tolua_AllToLua_cBlockArea_SetBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockLight'", NULL); +#endif + { + self->SetBlockLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockSkyLight'", NULL); +#endif + { + self->SetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_SetBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockSkyLight = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockSkyLight'", NULL); +#endif + { + self->SetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ,a_BlockSkyLight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockType00 +static int tolua_AllToLua_cBlockArea_GetRelBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockType(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockType of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockType00 +static int tolua_AllToLua_cBlockArea_GetBlockType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockMeta00 +static int tolua_AllToLua_cBlockArea_GetRelBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockMeta(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockMeta00 +static int tolua_AllToLua_cBlockArea_GetBlockMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockLight00 +static int tolua_AllToLua_cBlockArea_GetRelBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockLight(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockLight00 +static int tolua_AllToLua_cBlockArea_GetBlockLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockSkyLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetRelBlockSkyLight(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockSkyLight of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockSkyLight00 +static int tolua_AllToLua_cBlockArea_GetBlockSkyLight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockSkyLight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockSkyLight(a_BlockX,a_BlockY,a_BlockZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockSkyLight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_SetBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL); +#endif + { + self->SetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetRelBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cBlockArea* self = (cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetRelBlockTypeMeta'", NULL); +#endif + { + self->SetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetRelBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_GetBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); +#endif + { + self->GetBlockTypeMeta(a_BlockX,a_BlockY,a_BlockZ,a_BlockType,a_BlockMeta); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetRelBlockTypeMeta of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00 +static int tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRelBlockTypeMeta'", NULL); +#endif + { + self->GetRelBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetRelBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSizeX of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeX00 +static int tolua_AllToLua_cBlockArea_GetSizeX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeX'", NULL); +#endif + { + int tolua_ret = (int) self->GetSizeX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSizeX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSizeY of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeY00 +static int tolua_AllToLua_cBlockArea_GetSizeY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeY'", NULL); +#endif + { + int tolua_ret = (int) self->GetSizeY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSizeY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSizeZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetSizeZ00 +static int tolua_AllToLua_cBlockArea_GetSizeZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSizeZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetSizeZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSizeZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOriginX of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginX00 +static int tolua_AllToLua_cBlockArea_GetOriginX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginX'", NULL); +#endif + { + int tolua_ret = (int) self->GetOriginX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOriginX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOriginY of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginY00 +static int tolua_AllToLua_cBlockArea_GetOriginY00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginY'", NULL); +#endif + { + int tolua_ret = (int) self->GetOriginY(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOriginY'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetOriginZ of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetOriginZ00 +static int tolua_AllToLua_cBlockArea_GetOriginZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetOriginZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetOriginZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetOriginZ'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetDataTypes of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_GetDataTypes00 +static int tolua_AllToLua_cBlockArea_GetDataTypes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDataTypes'", NULL); +#endif + { + int tolua_ret = (int) self->GetDataTypes(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDataTypes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockTypes of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockTypes00 +static int tolua_AllToLua_cBlockArea_HasBlockTypes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockTypes'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockTypes(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockTypes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockMetas of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockMetas00 +static int tolua_AllToLua_cBlockArea_HasBlockMetas00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockMetas'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockMetas(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockMetas'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockLights of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockLights00 +static int tolua_AllToLua_cBlockArea_HasBlockLights00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockLights'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockLights(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockLights'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: HasBlockSkyLights of class cBlockArea */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cBlockArea_HasBlockSkyLights00 +static int tolua_AllToLua_cBlockArea_HasBlockSkyLights00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cBlockArea",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cBlockArea* self = (const cBlockArea*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HasBlockSkyLights'", NULL); +#endif + { + bool tolua_ret = (bool) self->HasBlockSkyLights(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HasBlockSkyLights'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkX of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkX00 +static int tolua_AllToLua_cChunkDesc_GetChunkX00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkX(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetChunkZ of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetChunkZ00 +static int tolua_AllToLua_cChunkDesc_GetChunkZ00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL); +#endif + { + int tolua_ret = (int) self->GetChunkZ(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&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) +{ +#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); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,2,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillBlocks'", NULL); +#endif + { + self->FillBlocks(a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FillBlocks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockTypeMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00 +static int tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockTypeMeta'", NULL); +#endif + { + self->SetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockTypeMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00 +static int tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockTypeMeta'", NULL); +#endif + { + self->GetBlockTypeMeta(a_RelX,a_RelY,a_RelZ,a_BlockType,a_BlockMeta); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockType); + tolua_pushnumber(tolua_S,(lua_Number)a_BlockMeta); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockTypeMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockType of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockType00 +static int tolua_AllToLua_cChunkDesc_SetBlockType00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockType'", NULL); +#endif + { + self->SetBlockType(a_RelX,a_RelY,a_RelZ,a_BlockType); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockType of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockType00 +static int tolua_AllToLua_cChunkDesc_GetBlockType00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockType'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockType(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBlockMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBlockMeta00 +static int tolua_AllToLua_cChunkDesc_SetBlockMeta00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBlockMeta'", NULL); +#endif + { + self->SetBlockMeta(a_RelX,a_RelY,a_RelZ,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockMeta of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockMeta00 +static int tolua_AllToLua_cChunkDesc_GetBlockMeta00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockMeta'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetBlockMeta(a_RelX,a_RelY,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockMeta'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetBiome of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetBiome00 +static int tolua_AllToLua_cChunkDesc_SetBiome00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BiomeID = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetBiome'", NULL); +#endif + { + self->SetBiome(a_RelX,a_RelZ,a_BiomeID); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBiome of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBiome00 +static int tolua_AllToLua_cChunkDesc_GetBiome00(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_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiome'", NULL); +#endif + { + EMCSBiome tolua_ret = (EMCSBiome) self->GetBiome(a_RelX,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBiome'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetHeight00 +static int tolua_AllToLua_cChunkDesc_SetHeight00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); + int a_Height = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetHeight'", NULL); +#endif + { + self->SetHeight(a_RelX,a_RelZ,a_Height); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetHeight00 +static int tolua_AllToLua_cChunkDesc_GetHeight00(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_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(a_RelX,a_RelZ); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultBiomes of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultBiomes = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultBiomes'", NULL); +#endif + { + self->SetUseDefaultBiomes(a_bUseDefaultBiomes); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultBiomes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultBiomes of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultBiomes'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultBiomes(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultBiomes'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultHeight = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultHeight'", NULL); +#endif + { + self->SetUseDefaultHeight(a_bUseDefaultHeight); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultHeight'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultHeight(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultComposition of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultComposition = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultComposition'", NULL); +#endif + { + self->SetUseDefaultComposition(a_bUseDefaultComposition); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultComposition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultComposition of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultComposition'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultComposition(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultComposition'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultStructures of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultStructures = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultStructures'", NULL); +#endif + { + self->SetUseDefaultStructures(a_bUseDefaultStructures); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultStructures'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultStructures of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultStructures'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultStructures(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultStructures'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetUseDefaultFinish of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00 +static int tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + !tolua_isboolean(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + bool a_bUseDefaultFinish = ((bool) tolua_toboolean(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetUseDefaultFinish'", NULL); +#endif + { + self->SetUseDefaultFinish(a_bUseDefaultFinish); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetUseDefaultFinish'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsUsingDefaultFinish of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00 +static int tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsUsingDefaultFinish'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsUsingDefaultFinish(); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsUsingDefaultFinish'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: WriteBlockArea of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_WriteBlockArea00 +static int tolua_AllToLua_cChunkDesc_WriteBlockArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cBlockArea",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cBlockArea* a_BlockArea = ((const cBlockArea*) tolua_tousertype(tolua_S,2,0)); + int a_RelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,5,0)); + cBlockArea::eMergeStrategy a_MergeStrategy = ((cBlockArea::eMergeStrategy) (int) tolua_tonumber(tolua_S,6,cBlockArea::msOverwrite)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteBlockArea'", NULL); +#endif + { + self->WriteBlockArea(*a_BlockArea,a_RelX,a_RelY,a_RelZ,a_MergeStrategy); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'WriteBlockArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReadBlockArea of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReadBlockArea00 +static int tolua_AllToLua_cChunkDesc_ReadBlockArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cBlockArea",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnoobj(tolua_S,9,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + cBlockArea* a_Dest = ((cBlockArea*) tolua_tousertype(tolua_S,2,0)); + int a_MinRelX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MaxRelX = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MinRelY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MaxRelY = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MinRelZ = ((int) tolua_tonumber(tolua_S,7,0)); + int a_MaxRelZ = ((int) tolua_tonumber(tolua_S,8,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadBlockArea'", NULL); +#endif + { + self->ReadBlockArea(*a_Dest,a_MinRelX,a_MaxRelX,a_MinRelY,a_MaxRelY,a_MinRelZ,a_MaxRelZ); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReadBlockArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMaxHeight of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetMaxHeight00 +static int tolua_AllToLua_cChunkDesc_GetMaxHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cChunkDesc",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cChunkDesc* self = (const cChunkDesc*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxHeight'", NULL); +#endif + { + unsigned char tolua_ret = ( unsigned char) self->GetMaxHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMaxHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid00 +static int tolua_AllToLua_cChunkDesc_FillRelCuboid00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); +#endif + { + self->FillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FillRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FillRelCuboid01 +static int tolua_AllToLua_cChunkDesc_FillRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FillRelCuboid'", NULL); +#endif + { + self->FillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_FillRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReplaceRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00 +static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,0,&tolua_err) || + !tolua_isnumber(tolua_S,11,0,&tolua_err) || + !tolua_isnoobj(tolua_S,12,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,10,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,11,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL); +#endif + { + self->ReplaceRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ReplaceRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ReplaceRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01 +static int tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_SrcType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_SrcMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,5,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReplaceRelCuboid'", NULL); +#endif + { + self->ReplaceRelCuboid(*a_RelCuboid,a_SrcType,a_SrcMeta,a_DstType,a_DstMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FloorRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid00 +static int tolua_AllToLua_cChunkDesc_FloorRelCuboid00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnoobj(tolua_S,10,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL); +#endif + { + self->FloorRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_DstType,a_DstMeta); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FloorRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FloorRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_FloorRelCuboid01 +static int tolua_AllToLua_cChunkDesc_FloorRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_DstType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_DstMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'FloorRelCuboid'", NULL); +#endif + { + self->FloorRelCuboid(*a_RelCuboid,a_DstType,a_DstMeta); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_FloorRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RandomFillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00 +static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,0,&tolua_err) || + !tolua_isnumber(tolua_S,11,0,&tolua_err) || + !tolua_isnoobj(tolua_S,12,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_MinX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_MaxX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_MinY = ((int) tolua_tonumber(tolua_S,4,0)); + int a_MaxY = ((int) tolua_tonumber(tolua_S,5,0)); + int a_MinZ = ((int) tolua_tonumber(tolua_S,6,0)); + int a_MaxZ = ((int) tolua_tonumber(tolua_S,7,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,8,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,9,0)); + int a_RandomSeed = ((int) tolua_tonumber(tolua_S,10,0)); + int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,11,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL); +#endif + { + self->RandomFillRelCuboid(a_MinX,a_MaxX,a_MinY,a_MaxY,a_MinZ,a_MaxZ,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RandomFillRelCuboid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: RandomFillRelCuboid of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01 +static int tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cChunkDesc",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCuboid",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + const cCuboid* a_RelCuboid = ((const cCuboid*) tolua_tousertype(tolua_S,2,0)); + unsigned char a_BlockType = (( unsigned char) tolua_tonumber(tolua_S,3,0)); + unsigned char a_BlockMeta = (( unsigned char) tolua_tonumber(tolua_S,4,0)); + int a_RandomSeed = ((int) tolua_tonumber(tolua_S,5,0)); + int a_ChanceOutOf10k = ((int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RandomFillRelCuboid'", NULL); +#endif + { + self->RandomFillRelCuboid(*a_RelCuboid,a_BlockType,a_BlockMeta,a_RandomSeed,a_ChanceOutOf10k); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetBlockEntity of class cChunkDesc */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cChunkDesc_GetBlockEntity00 +static int tolua_AllToLua_cChunkDesc_GetBlockEntity00(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_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cChunkDesc* self = (cChunkDesc*) tolua_tousertype(tolua_S,1,0); + int a_RelX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_RelY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_RelZ = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBlockEntity'", NULL); +#endif + { + cBlockEntity* tolua_ret = (cBlockEntity*) self->GetBlockEntity(a_RelX,a_RelY,a_RelZ); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cBlockEntity"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetBlockEntity'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00 +static int tolua_AllToLua_cCraftingGrid_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCraftingGrid",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 + { + int a_Width = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Height = ((int) tolua_tonumber(tolua_S,3,0)); + { + cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_new00_local +static int tolua_AllToLua_cCraftingGrid_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cCraftingGrid",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 + { + int a_Width = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Height = ((int) tolua_tonumber(tolua_S,3,0)); + { + cCraftingGrid* tolua_ret = (cCraftingGrid*) Mtolua_new((cCraftingGrid)(a_Width,a_Height)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cCraftingGrid"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWidth of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetWidth00 +static int tolua_AllToLua_cCraftingGrid_GetWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWidth'", NULL); +#endif + { + int tolua_ret = (int) self->GetWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetHeight of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetHeight00 +static int tolua_AllToLua_cCraftingGrid_GetHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetItem of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_GetItem00 +static int tolua_AllToLua_cCraftingGrid_GetItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingGrid",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 + { + const cCraftingGrid* self = (const cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetItem'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->GetItem(x,y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetItem of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem00 +static int tolua_AllToLua_cCraftingGrid_SetItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0)); + int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0)); + short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL); +#endif + { + self->SetItem(x,y,a_ItemType,a_ItemCount,a_ItemHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetItem of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_SetItem01 +static int tolua_AllToLua_cCraftingGrid_SetItem01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetItem'", NULL); +#endif + { + self->SetItem(x,y,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cCraftingGrid_SetItem00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Clear00 +static int tolua_AllToLua_cCraftingGrid_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ConsumeGrid of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_ConsumeGrid00 +static int tolua_AllToLua_cCraftingGrid_ConsumeGrid00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cCraftingGrid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); + const cCraftingGrid* a_Grid = ((const cCraftingGrid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeGrid'", NULL); +#endif + { + self->ConsumeGrid(*a_Grid); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ConsumeGrid'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dump of class cCraftingGrid */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingGrid_Dump00 +static int tolua_AllToLua_cCraftingGrid_Dump00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingGrid",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingGrid* self = (cCraftingGrid*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL); +#endif + { + self->Dump(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Clear of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Clear00 +static int tolua_AllToLua_cCraftingRecipe_Clear00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Clear'", NULL); +#endif + { + self->Clear(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Clear'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIngredientsWidth of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00 +static int tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsWidth'", NULL); +#endif + { + int tolua_ret = (int) self->GetIngredientsWidth(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIngredientsWidth'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIngredientsHeight of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00 +static int tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredientsHeight'", NULL); +#endif + { + int tolua_ret = (int) self->GetIngredientsHeight(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIngredientsHeight'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetIngredient of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetIngredient00 +static int tolua_AllToLua_cCraftingRecipe_GetIngredient00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",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 + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIngredient'", NULL); +#endif + { + cItem& tolua_ret = (cItem&) self->GetIngredient(x,y); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetIngredient'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetResult of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_GetResult00 +static int tolua_AllToLua_cCraftingRecipe_GetResult00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cCraftingRecipe* self = (const cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetResult'", NULL); +#endif + { + const cItem& tolua_ret = (const cItem&) self->GetResult(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetResult'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetResult of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult00 +static int tolua_AllToLua_cCraftingRecipe_SetResult00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,2,0)); + int a_ItemCount = ((int) tolua_tonumber(tolua_S,3,0)); + short a_ItemHealth = ((short) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL); +#endif + { + self->SetResult(a_ItemType,a_ItemCount,a_ItemHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetResult'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetResult of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetResult01 +static int tolua_AllToLua_cCraftingRecipe_SetResult01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetResult'", NULL); +#endif + { + self->SetResult(*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cCraftingRecipe_SetResult00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetIngredient of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient00 +static int tolua_AllToLua_cCraftingRecipe_SetIngredient00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + ENUM_ITEM_ID a_ItemType = ((ENUM_ITEM_ID) (int) tolua_tonumber(tolua_S,4,0)); + int a_ItemCount = ((int) tolua_tonumber(tolua_S,5,0)); + short a_ItemHealth = ((short) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL); +#endif + { + self->SetIngredient(x,y,a_ItemType,a_ItemCount,a_ItemHealth); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetIngredient'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetIngredient of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_SetIngredient01 +static int tolua_AllToLua_cCraftingRecipe_SetIngredient01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetIngredient'", NULL); +#endif + { + self->SetIngredient(x,y,*a_Item); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cCraftingRecipe_SetIngredient00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: ConsumeIngredients of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00 +static int tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cCraftingGrid",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); + cCraftingGrid* a_CraftingGrid = ((cCraftingGrid*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ConsumeIngredients'", NULL); +#endif + { + self->ConsumeIngredients(*a_CraftingGrid); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ConsumeIngredients'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: Dump of class cCraftingRecipe */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cCraftingRecipe_Dump00 +static int tolua_AllToLua_cCraftingRecipe_Dump00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cCraftingRecipe",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cCraftingRecipe* self = (cCraftingRecipe*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Dump'", NULL); +#endif + { + self->Dump(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Dump'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindowID of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowID00 +static int tolua_AllToLua_cWindow_GetWindowID00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowID'", NULL); +#endif + { + char tolua_ret = (char) self->GetWindowID(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindowID'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindowType of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowType00 +static int tolua_AllToLua_cWindow_GetWindowType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowType'", NULL); +#endif + { + int tolua_ret = (int) self->GetWindowType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindowType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSlot of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetSlot00 +static int tolua_AllToLua_cWindow_GetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetSlot'", NULL); +#endif + { + const cItem* tolua_ret = (const cItem*) self->GetSlot(*a_Player,a_SlotNum); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"const cItem"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetSlot of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetSlot00 +static int tolua_AllToLua_cWindow_SetSlot00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cPlayer",0,&tolua_err)) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"const cItem",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,2,0)); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,3,0)); + const cItem* a_Item = ((const cItem*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetSlot'", NULL); +#endif + { + self->SetSlot(*a_Player,a_SlotNum,*a_Item); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetSlot'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotInPlayerMainInventory of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00 +static int tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerMainInventory'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotInPlayerMainInventory(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerMainInventory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotInPlayerHotbar of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00 +static int tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerHotbar'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotInPlayerHotbar(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerHotbar'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: IsSlotInPlayerInventory of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_IsSlotInPlayerInventory00 +static int tolua_AllToLua_cWindow_IsSlotInPlayerInventory00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); + int a_SlotNum = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSlotInPlayerInventory'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsSlotInPlayerInventory(a_SlotNum); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsSlotInPlayerInventory'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetWindowTitle of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_GetWindowTitle00 +static int tolua_AllToLua_cWindow_GetWindowTitle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWindow* self = (const cWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWindowTitle'", NULL); +#endif + { + const AString tolua_ret = (const AString) self->GetWindowTitle(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetWindowTitle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetWindowTitle of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetWindowTitle00 +static int tolua_AllToLua_cWindow_SetWindowTitle00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + const AString a_WindowTitle = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetWindowTitle'", NULL); +#endif + { + self->SetWindowTitle(a_WindowTitle); + tolua_pushcppstring(tolua_S,(const char*)a_WindowTitle); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetWindowTitle'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetProperty of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty00 +static int tolua_AllToLua_cWindow_SetProperty00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",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 + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + int a_Property = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Value = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL); +#endif + { + self->SetProperty(a_Property,a_Value); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetProperty'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: SetProperty of class cWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWindow_SetProperty01 +static int tolua_AllToLua_cWindow_SetProperty01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + (tolua_isvaluenil(tolua_S,4,&tolua_err) || !tolua_isusertype(tolua_S,4,"cPlayer",0,&tolua_err)) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else + { + cWindow* self = (cWindow*) tolua_tousertype(tolua_S,1,0); + int a_Property = ((int) tolua_tonumber(tolua_S,2,0)); + int a_Value = ((int) tolua_tonumber(tolua_S,3,0)); + cPlayer* a_Player = ((cPlayer*) tolua_tousertype(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetProperty'", NULL); +#endif + { + self->SetProperty(a_Property,a_Value,*a_Player); + } + } + return 0; +tolua_lerror: + return tolua_AllToLua_cWindow_SetProperty00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00 +static int tolua_AllToLua_cLuaWindow_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0)); + int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0)); + const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow"); + tolua_pushcppstring(tolua_S,(const char*)a_Title); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_new00_local +static int tolua_AllToLua_cLuaWindow_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_iscppstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cWindow::WindowType a_WindowType = ((cWindow::WindowType) (int) tolua_tonumber(tolua_S,2,0)); + int a_SlotsX = ((int) tolua_tonumber(tolua_S,3,0)); + int a_SlotsY = ((int) tolua_tonumber(tolua_S,4,0)); + const AString a_Title = ((const AString) tolua_tocppstring(tolua_S,5,0)); + { + cLuaWindow* tolua_ret = (cLuaWindow*) Mtolua_new((cLuaWindow)(a_WindowType,a_SlotsX,a_SlotsY,a_Title)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cLuaWindow"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + tolua_pushcppstring(tolua_S,(const char*)a_Title); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_delete00 +static int tolua_AllToLua_cLuaWindow_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'", NULL); +#endif + Mtolua_delete(self); + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetContents of class cLuaWindow */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cLuaWindow_GetContents00 +static int tolua_AllToLua_cLuaWindow_GetContents00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cLuaWindow",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetContents'", NULL); +#endif + { + cItemGrid& tolua_ret = (cItemGrid&) self->GetContents(); + tolua_pushusertype(tolua_S,(void*)&tolua_ret,"cItemGrid"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetContents'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00 +static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL); +#endif + { + cMonster::eType tolua_ret = (cMonster::eType) self->GetMobType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetMobFamily of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobFamily00 +static int tolua_AllToLua_cMonster_GetMobFamily00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobFamily'", NULL); +#endif + { + cMonster::eFamily tolua_ret = (cMonster::eFamily) self->GetMobFamily(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMobFamily'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MobTypeToString of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_MobTypeToString00 +static int tolua_AllToLua_cMonster_MobTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); + { + AString tolua_ret = (AString) cMonster::MobTypeToString(a_MobType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MobTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StringToMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_StringToMobType00 +static int tolua_AllToLua_cMonster_StringToMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_MobTypeName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cMonster::eType tolua_ret = (cMonster::eType) cMonster::StringToMobType(a_MobTypeName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_MobTypeName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FamilyFromType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_FamilyFromType00 +static int tolua_AllToLua_cMonster_FamilyFromType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); + { + cMonster::eFamily tolua_ret = (cMonster::eFamily) cMonster::FamilyFromType(a_MobType); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FamilyFromType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnDelay of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnDelay00 +static int tolua_AllToLua_cMonster_GetSpawnDelay00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eFamily a_MobFamily = ((cMonster::eFamily) (int) tolua_tonumber(tolua_S,2,0)); + { + int tolua_ret = (int) cMonster::GetSpawnDelay(a_MobFamily); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnDelay'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* Open function */ +TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) +{ + tolua_open(tolua_S); + tolua_reg_types(tolua_S); + tolua_module(tolua_S,NULL,1); + tolua_beginmodule(tolua_S,NULL); + tolua_constant(tolua_S,"biOcean",biOcean); + tolua_constant(tolua_S,"biPlains",biPlains); + tolua_constant(tolua_S,"biDesert",biDesert); + tolua_constant(tolua_S,"biExtremeHills",biExtremeHills); + tolua_constant(tolua_S,"biForest",biForest); + tolua_constant(tolua_S,"biTaiga",biTaiga); + tolua_constant(tolua_S,"biSwampland",biSwampland); + tolua_constant(tolua_S,"biRiver",biRiver); + tolua_constant(tolua_S,"biHell",biHell); + tolua_constant(tolua_S,"biNether",biNether); + tolua_constant(tolua_S,"biSky",biSky); + tolua_constant(tolua_S,"biEnd",biEnd); + tolua_constant(tolua_S,"biFrozenOcean",biFrozenOcean); + tolua_constant(tolua_S,"biFrozenRiver",biFrozenRiver); + tolua_constant(tolua_S,"biIcePlains",biIcePlains); + tolua_constant(tolua_S,"biTundra",biTundra); + tolua_constant(tolua_S,"biIceMountains",biIceMountains); + tolua_constant(tolua_S,"biMushroomIsland",biMushroomIsland); + tolua_constant(tolua_S,"biMushroomShore",biMushroomShore); + tolua_constant(tolua_S,"biBeach",biBeach); + tolua_constant(tolua_S,"biDesertHills",biDesertHills); + tolua_constant(tolua_S,"biForestHills",biForestHills); + tolua_constant(tolua_S,"biTaigaHills",biTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsEdge",biExtremeHillsEdge); + tolua_constant(tolua_S,"biJungle",biJungle); + tolua_constant(tolua_S,"biJungleHills",biJungleHills); + tolua_constant(tolua_S,"biJungleEdge",biJungleEdge); + tolua_constant(tolua_S,"biDeepOcean",biDeepOcean); + tolua_constant(tolua_S,"biStoneBeach",biStoneBeach); + tolua_constant(tolua_S,"biColdBeach",biColdBeach); + tolua_constant(tolua_S,"biBirchForest",biBirchForest); + tolua_constant(tolua_S,"biBirchForestHills",biBirchForestHills); + tolua_constant(tolua_S,"biRoofedForest",biRoofedForest); + tolua_constant(tolua_S,"biColdTaiga",biColdTaiga); + tolua_constant(tolua_S,"biColdTaigaHills",biColdTaigaHills); + tolua_constant(tolua_S,"biMegaTaiga",biMegaTaiga); + tolua_constant(tolua_S,"biMegaTaigaHills",biMegaTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlus",biExtremeHillsPlus); + tolua_constant(tolua_S,"biSavanna",biSavanna); + tolua_constant(tolua_S,"biSavannaPlateau",biSavannaPlateau); + tolua_constant(tolua_S,"biMesa",biMesa); + tolua_constant(tolua_S,"biMesaPlateauF",biMesaPlateauF); + tolua_constant(tolua_S,"biMesaPlateau",biMesaPlateau); + tolua_constant(tolua_S,"biNumBiomes",biNumBiomes); + tolua_constant(tolua_S,"biMaxBiome",biMaxBiome); + tolua_constant(tolua_S,"biVariant",biVariant); + tolua_constant(tolua_S,"biSunflowerPlains",biSunflowerPlains); + tolua_constant(tolua_S,"biDesertM",biDesertM); + tolua_constant(tolua_S,"biExtremeHillsM",biExtremeHillsM); + tolua_constant(tolua_S,"biFlowerForest",biFlowerForest); + tolua_constant(tolua_S,"biTaigaM",biTaigaM); + tolua_constant(tolua_S,"biSwamplandM",biSwamplandM); + tolua_constant(tolua_S,"biIcePlainsSpikes",biIcePlainsSpikes); + tolua_constant(tolua_S,"biJungleM",biJungleM); + tolua_constant(tolua_S,"biJungleEdgeM",biJungleEdgeM); + tolua_constant(tolua_S,"biBirchForestM",biBirchForestM); + tolua_constant(tolua_S,"biBirchForestHillsM",biBirchForestHillsM); + tolua_constant(tolua_S,"biRoofedForestM",biRoofedForestM); + tolua_constant(tolua_S,"biColdTaigaM",biColdTaigaM); + tolua_constant(tolua_S,"biMegaSpruceTaiga",biMegaSpruceTaiga); + tolua_constant(tolua_S,"biMegaSpruceTaigaHills",biMegaSpruceTaigaHills); + tolua_constant(tolua_S,"biExtremeHillsPlusM",biExtremeHillsPlusM); + tolua_constant(tolua_S,"biSavannaM",biSavannaM); + tolua_constant(tolua_S,"biSavannaPlateauM",biSavannaPlateauM); + tolua_constant(tolua_S,"biMesaBryce",biMesaBryce); + tolua_constant(tolua_S,"biMesaPlateauFM",biMesaPlateauFM); + tolua_constant(tolua_S,"biMesaPlateauM",biMesaPlateauM); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cIniFile","cIniFile","",tolua_collect_cIniFile); + #else + tolua_cclass(tolua_S,"cIniFile","cIniFile","",NULL); + #endif + tolua_beginmodule(tolua_S,"cIniFile"); + tolua_constant(tolua_S,"noID",cIniFile::noID); + tolua_function(tolua_S,"new",tolua_AllToLua_cIniFile_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new00_local); + tolua_function(tolua_S,"CaseSensitive",tolua_AllToLua_cIniFile_CaseSensitive00); + tolua_function(tolua_S,"CaseInsensitive",tolua_AllToLua_cIniFile_CaseInsensitive00); + tolua_function(tolua_S,"ReadFile",tolua_AllToLua_cIniFile_ReadFile00); + tolua_function(tolua_S,"WriteFile",tolua_AllToLua_cIniFile_WriteFile00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cIniFile_Clear00); + tolua_function(tolua_S,"FindKey",tolua_AllToLua_cIniFile_FindKey00); + tolua_function(tolua_S,"FindValue",tolua_AllToLua_cIniFile_FindValue00); + tolua_function(tolua_S,"GetNumKeys",tolua_AllToLua_cIniFile_GetNumKeys00); + tolua_function(tolua_S,"AddKeyName",tolua_AllToLua_cIniFile_AddKeyName00); + tolua_function(tolua_S,"GetKeyName",tolua_AllToLua_cIniFile_GetKeyName00); + tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues00); + tolua_function(tolua_S,"GetNumValues",tolua_AllToLua_cIniFile_GetNumValues01); + tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName00); + tolua_function(tolua_S,"GetValueName",tolua_AllToLua_cIniFile_GetValueName01); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue00); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue01); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue02); + tolua_function(tolua_S,"GetValue",tolua_AllToLua_cIniFile_GetValue03); + tolua_function(tolua_S,"GetValueF",tolua_AllToLua_cIniFile_GetValueF00); + tolua_function(tolua_S,"GetValueI",tolua_AllToLua_cIniFile_GetValueI00); + tolua_function(tolua_S,"GetValueB",tolua_AllToLua_cIniFile_GetValueB00); + tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet00); + tolua_function(tolua_S,"GetValueSet",tolua_AllToLua_cIniFile_GetValueSet01); + tolua_function(tolua_S,"GetValueSetF",tolua_AllToLua_cIniFile_GetValueSetF00); + tolua_function(tolua_S,"GetValueSetI",tolua_AllToLua_cIniFile_GetValueSetI00); + tolua_function(tolua_S,"GetValueSetB",tolua_AllToLua_cIniFile_GetValueSetB00); + tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue00); + tolua_function(tolua_S,"SetValue",tolua_AllToLua_cIniFile_SetValue01); + tolua_function(tolua_S,"SetValueI",tolua_AllToLua_cIniFile_SetValueI00); + tolua_function(tolua_S,"SetValueB",tolua_AllToLua_cIniFile_SetValueB00); + tolua_function(tolua_S,"SetValueF",tolua_AllToLua_cIniFile_SetValueF00); + tolua_function(tolua_S,"DeleteValueByID",tolua_AllToLua_cIniFile_DeleteValueByID00); + tolua_function(tolua_S,"DeleteValue",tolua_AllToLua_cIniFile_DeleteValue00); + tolua_function(tolua_S,"DeleteKey",tolua_AllToLua_cIniFile_DeleteKey00); + tolua_function(tolua_S,"GetNumHeaderComments",tolua_AllToLua_cIniFile_GetNumHeaderComments00); + tolua_function(tolua_S,"AddHeaderComment",tolua_AllToLua_cIniFile_AddHeaderComment00); + tolua_function(tolua_S,"GetHeaderComment",tolua_AllToLua_cIniFile_GetHeaderComment00); + tolua_function(tolua_S,"DeleteHeaderComment",tolua_AllToLua_cIniFile_DeleteHeaderComment00); + tolua_function(tolua_S,"DeleteHeaderComments",tolua_AllToLua_cIniFile_DeleteHeaderComments00); + tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments00); + tolua_function(tolua_S,"GetNumKeyComments",tolua_AllToLua_cIniFile_GetNumKeyComments01); + tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment00); + tolua_function(tolua_S,"AddKeyComment",tolua_AllToLua_cIniFile_AddKeyComment01); + tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment00); + tolua_function(tolua_S,"GetKeyComment",tolua_AllToLua_cIniFile_GetKeyComment01); + tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment00); + tolua_function(tolua_S,"DeleteKeyComment",tolua_AllToLua_cIniFile_DeleteKeyComment01); + tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments00); + tolua_function(tolua_S,"DeleteKeyComments",tolua_AllToLua_cIniFile_DeleteKeyComments01); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFile","cFile","",NULL); + tolua_beginmodule(tolua_S,"cFile"); + tolua_function(tolua_S,"Exists",tolua_AllToLua_cFile_Exists00); + tolua_function(tolua_S,"Delete",tolua_AllToLua_cFile_Delete00); + tolua_function(tolua_S,"Rename",tolua_AllToLua_cFile_Rename00); + tolua_function(tolua_S,"Copy",tolua_AllToLua_cFile_Copy00); + tolua_function(tolua_S,"IsFolder",tolua_AllToLua_cFile_IsFolder00); + tolua_function(tolua_S,"IsFile",tolua_AllToLua_cFile_IsFile00); + tolua_function(tolua_S,"GetSize",tolua_AllToLua_cFile_GetSize00); + tolua_function(tolua_S,"CreateFolder",tolua_AllToLua_cFile_CreateFolder00); + tolua_function(tolua_S,"ReadWholeFile",tolua_AllToLua_cFile_ReadWholeFile00); + tolua_endmodule(tolua_S); + tolua_constant(tolua_S,"E_BLOCK_AIR",E_BLOCK_AIR); + tolua_constant(tolua_S,"E_BLOCK_STONE",E_BLOCK_STONE); + tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS); + tolua_constant(tolua_S,"E_BLOCK_DIRT",E_BLOCK_DIRT); + tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE",E_BLOCK_COBBLESTONE); + tolua_constant(tolua_S,"E_BLOCK_PLANKS",E_BLOCK_PLANKS); + tolua_constant(tolua_S,"E_BLOCK_SAPLING",E_BLOCK_SAPLING); + tolua_constant(tolua_S,"E_BLOCK_BEDROCK",E_BLOCK_BEDROCK); + tolua_constant(tolua_S,"E_BLOCK_WATER",E_BLOCK_WATER); + tolua_constant(tolua_S,"E_BLOCK_STATIONARY_WATER",E_BLOCK_STATIONARY_WATER); + tolua_constant(tolua_S,"E_BLOCK_LAVA",E_BLOCK_LAVA); + tolua_constant(tolua_S,"E_BLOCK_STATIONARY_LAVA",E_BLOCK_STATIONARY_LAVA); + tolua_constant(tolua_S,"E_BLOCK_SAND",E_BLOCK_SAND); + tolua_constant(tolua_S,"E_BLOCK_GRAVEL",E_BLOCK_GRAVEL); + tolua_constant(tolua_S,"E_BLOCK_GOLD_ORE",E_BLOCK_GOLD_ORE); + tolua_constant(tolua_S,"E_BLOCK_IRON_ORE",E_BLOCK_IRON_ORE); + tolua_constant(tolua_S,"E_BLOCK_COAL_ORE",E_BLOCK_COAL_ORE); + tolua_constant(tolua_S,"E_BLOCK_LOG",E_BLOCK_LOG); + tolua_constant(tolua_S,"E_BLOCK_LEAVES",E_BLOCK_LEAVES); + tolua_constant(tolua_S,"E_BLOCK_SPONGE",E_BLOCK_SPONGE); + tolua_constant(tolua_S,"E_BLOCK_GLASS",E_BLOCK_GLASS); + tolua_constant(tolua_S,"E_BLOCK_LAPIS_ORE",E_BLOCK_LAPIS_ORE); + tolua_constant(tolua_S,"E_BLOCK_LAPIS_BLOCK",E_BLOCK_LAPIS_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_DISPENSER",E_BLOCK_DISPENSER); + tolua_constant(tolua_S,"E_BLOCK_SANDSTONE",E_BLOCK_SANDSTONE); + tolua_constant(tolua_S,"E_BLOCK_NOTE_BLOCK",E_BLOCK_NOTE_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_BED",E_BLOCK_BED); + tolua_constant(tolua_S,"E_BLOCK_POWERED_RAIL",E_BLOCK_POWERED_RAIL); + tolua_constant(tolua_S,"E_BLOCK_DETECTOR_RAIL",E_BLOCK_DETECTOR_RAIL); + tolua_constant(tolua_S,"E_BLOCK_STICKY_PISTON",E_BLOCK_STICKY_PISTON); + tolua_constant(tolua_S,"E_BLOCK_COBWEB",E_BLOCK_COBWEB); + tolua_constant(tolua_S,"E_BLOCK_TALL_GRASS",E_BLOCK_TALL_GRASS); + tolua_constant(tolua_S,"E_BLOCK_DEAD_BUSH",E_BLOCK_DEAD_BUSH); + tolua_constant(tolua_S,"E_BLOCK_PISTON",E_BLOCK_PISTON); + tolua_constant(tolua_S,"E_BLOCK_PISTON_EXTENSION",E_BLOCK_PISTON_EXTENSION); + tolua_constant(tolua_S,"E_BLOCK_WOOL",E_BLOCK_WOOL); + tolua_constant(tolua_S,"E_BLOCK_PISTON_MOVED_BLOCK",E_BLOCK_PISTON_MOVED_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_DANDELION",E_BLOCK_DANDELION); + tolua_constant(tolua_S,"E_BLOCK_FLOWER",E_BLOCK_FLOWER); + tolua_constant(tolua_S,"E_BLOCK_BROWN_MUSHROOM",E_BLOCK_BROWN_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_RED_MUSHROOM",E_BLOCK_RED_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_GOLD_BLOCK",E_BLOCK_GOLD_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_IRON_BLOCK",E_BLOCK_IRON_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_DOUBLE_STONE_SLAB",E_BLOCK_DOUBLE_STONE_SLAB); + tolua_constant(tolua_S,"E_BLOCK_STONE_SLAB",E_BLOCK_STONE_SLAB); + tolua_constant(tolua_S,"E_BLOCK_BRICK",E_BLOCK_BRICK); + tolua_constant(tolua_S,"E_BLOCK_TNT",E_BLOCK_TNT); + tolua_constant(tolua_S,"E_BLOCK_BOOKCASE",E_BLOCK_BOOKCASE); + tolua_constant(tolua_S,"E_BLOCK_MOSSY_COBBLESTONE",E_BLOCK_MOSSY_COBBLESTONE); + tolua_constant(tolua_S,"E_BLOCK_OBSIDIAN",E_BLOCK_OBSIDIAN); + tolua_constant(tolua_S,"E_BLOCK_TORCH",E_BLOCK_TORCH); + tolua_constant(tolua_S,"E_BLOCK_FIRE",E_BLOCK_FIRE); + tolua_constant(tolua_S,"E_BLOCK_MOB_SPAWNER",E_BLOCK_MOB_SPAWNER); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_STAIRS",E_BLOCK_WOODEN_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_CHEST",E_BLOCK_CHEST); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_WIRE",E_BLOCK_REDSTONE_WIRE); + tolua_constant(tolua_S,"E_BLOCK_DIAMOND_ORE",E_BLOCK_DIAMOND_ORE); + tolua_constant(tolua_S,"E_BLOCK_DIAMOND_BLOCK",E_BLOCK_DIAMOND_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_CRAFTING_TABLE",E_BLOCK_CRAFTING_TABLE); + tolua_constant(tolua_S,"E_BLOCK_WORKBENCH",E_BLOCK_WORKBENCH); + tolua_constant(tolua_S,"E_BLOCK_CROPS",E_BLOCK_CROPS); + tolua_constant(tolua_S,"E_BLOCK_FARMLAND",E_BLOCK_FARMLAND); + tolua_constant(tolua_S,"E_BLOCK_FURNACE",E_BLOCK_FURNACE); + tolua_constant(tolua_S,"E_BLOCK_LIT_FURNACE",E_BLOCK_LIT_FURNACE); + tolua_constant(tolua_S,"E_BLOCK_BURNING_FURNACE",E_BLOCK_BURNING_FURNACE); + tolua_constant(tolua_S,"E_BLOCK_SIGN_POST",E_BLOCK_SIGN_POST); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_DOOR",E_BLOCK_WOODEN_DOOR); + tolua_constant(tolua_S,"E_BLOCK_LADDER",E_BLOCK_LADDER); + tolua_constant(tolua_S,"E_BLOCK_RAIL",E_BLOCK_RAIL); + tolua_constant(tolua_S,"E_BLOCK_MINECART_TRACKS",E_BLOCK_MINECART_TRACKS); + tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_STAIRS",E_BLOCK_COBBLESTONE_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_WALLSIGN",E_BLOCK_WALLSIGN); + tolua_constant(tolua_S,"E_BLOCK_LEVER",E_BLOCK_LEVER); + tolua_constant(tolua_S,"E_BLOCK_STONE_PRESSURE_PLATE",E_BLOCK_STONE_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_IRON_DOOR",E_BLOCK_IRON_DOOR); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_PRESSURE_PLATE",E_BLOCK_WOODEN_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE",E_BLOCK_REDSTONE_ORE); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_ORE_GLOWING",E_BLOCK_REDSTONE_ORE_GLOWING); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_OFF",E_BLOCK_REDSTONE_TORCH_OFF); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_TORCH_ON",E_BLOCK_REDSTONE_TORCH_ON); + tolua_constant(tolua_S,"E_BLOCK_STONE_BUTTON",E_BLOCK_STONE_BUTTON); + tolua_constant(tolua_S,"E_BLOCK_SNOW",E_BLOCK_SNOW); + tolua_constant(tolua_S,"E_BLOCK_ICE",E_BLOCK_ICE); + tolua_constant(tolua_S,"E_BLOCK_SNOW_BLOCK",E_BLOCK_SNOW_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_CACTUS",E_BLOCK_CACTUS); + tolua_constant(tolua_S,"E_BLOCK_CLAY",E_BLOCK_CLAY); + tolua_constant(tolua_S,"E_BLOCK_SUGARCANE",E_BLOCK_SUGARCANE); + tolua_constant(tolua_S,"E_BLOCK_REEDS",E_BLOCK_REEDS); + tolua_constant(tolua_S,"E_BLOCK_JUKEBOX",E_BLOCK_JUKEBOX); + tolua_constant(tolua_S,"E_BLOCK_FENCE",E_BLOCK_FENCE); + tolua_constant(tolua_S,"E_BLOCK_PUMPKIN",E_BLOCK_PUMPKIN); + tolua_constant(tolua_S,"E_BLOCK_NETHERRACK",E_BLOCK_NETHERRACK); + tolua_constant(tolua_S,"E_BLOCK_SOULSAND",E_BLOCK_SOULSAND); + tolua_constant(tolua_S,"E_BLOCK_GLOWSTONE",E_BLOCK_GLOWSTONE); + tolua_constant(tolua_S,"E_BLOCK_NETHER_PORTAL",E_BLOCK_NETHER_PORTAL); + tolua_constant(tolua_S,"E_BLOCK_JACK_O_LANTERN",E_BLOCK_JACK_O_LANTERN); + tolua_constant(tolua_S,"E_BLOCK_CAKE",E_BLOCK_CAKE); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_OFF",E_BLOCK_REDSTONE_REPEATER_OFF); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_ON",E_BLOCK_REDSTONE_REPEATER_ON); + tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS",E_BLOCK_STAINED_GLASS); + tolua_constant(tolua_S,"E_BLOCK_TRAPDOOR",E_BLOCK_TRAPDOOR); + tolua_constant(tolua_S,"E_BLOCK_SILVERFISH_EGG",E_BLOCK_SILVERFISH_EGG); + tolua_constant(tolua_S,"E_BLOCK_STONE_BRICKS",E_BLOCK_STONE_BRICKS); + tolua_constant(tolua_S,"E_BLOCK_HUGE_BROWN_MUSHROOM",E_BLOCK_HUGE_BROWN_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_HUGE_RED_MUSHROOM",E_BLOCK_HUGE_RED_MUSHROOM); + tolua_constant(tolua_S,"E_BLOCK_IRON_BARS",E_BLOCK_IRON_BARS); + tolua_constant(tolua_S,"E_BLOCK_GLASS_PANE",E_BLOCK_GLASS_PANE); + tolua_constant(tolua_S,"E_BLOCK_MELON",E_BLOCK_MELON); + tolua_constant(tolua_S,"E_BLOCK_PUMPKIN_STEM",E_BLOCK_PUMPKIN_STEM); + tolua_constant(tolua_S,"E_BLOCK_MELON_STEM",E_BLOCK_MELON_STEM); + tolua_constant(tolua_S,"E_BLOCK_VINES",E_BLOCK_VINES); + tolua_constant(tolua_S,"E_BLOCK_FENCE_GATE",E_BLOCK_FENCE_GATE); + tolua_constant(tolua_S,"E_BLOCK_BRICK_STAIRS",E_BLOCK_BRICK_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_STONE_BRICK_STAIRS",E_BLOCK_STONE_BRICK_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_MYCELIUM",E_BLOCK_MYCELIUM); + tolua_constant(tolua_S,"E_BLOCK_LILY_PAD",E_BLOCK_LILY_PAD); + tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK",E_BLOCK_NETHER_BRICK); + tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_FENCE",E_BLOCK_NETHER_BRICK_FENCE); + tolua_constant(tolua_S,"E_BLOCK_NETHER_BRICK_STAIRS",E_BLOCK_NETHER_BRICK_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_NETHER_WART",E_BLOCK_NETHER_WART); + tolua_constant(tolua_S,"E_BLOCK_ENCHANTMENT_TABLE",E_BLOCK_ENCHANTMENT_TABLE); + tolua_constant(tolua_S,"E_BLOCK_BREWING_STAND",E_BLOCK_BREWING_STAND); + tolua_constant(tolua_S,"E_BLOCK_CAULDRON",E_BLOCK_CAULDRON); + tolua_constant(tolua_S,"E_BLOCK_END_PORTAL",E_BLOCK_END_PORTAL); + tolua_constant(tolua_S,"E_BLOCK_END_PORTAL_FRAME",E_BLOCK_END_PORTAL_FRAME); + tolua_constant(tolua_S,"E_BLOCK_END_STONE",E_BLOCK_END_STONE); + tolua_constant(tolua_S,"E_BLOCK_DRAGON_EGG",E_BLOCK_DRAGON_EGG); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_OFF",E_BLOCK_REDSTONE_LAMP_OFF); + tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_ON",E_BLOCK_REDSTONE_LAMP_ON); + tolua_constant(tolua_S,"E_BLOCK_DOUBLE_WOODEN_SLAB",E_BLOCK_DOUBLE_WOODEN_SLAB); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_SLAB",E_BLOCK_WOODEN_SLAB); + tolua_constant(tolua_S,"E_BLOCK_COCOA_POD",E_BLOCK_COCOA_POD); + tolua_constant(tolua_S,"E_BLOCK_SANDSTONE_STAIRS",E_BLOCK_SANDSTONE_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_EMERALD_ORE",E_BLOCK_EMERALD_ORE); + tolua_constant(tolua_S,"E_BLOCK_ENDER_CHEST",E_BLOCK_ENDER_CHEST); + tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE_HOOK",E_BLOCK_TRIPWIRE_HOOK); + tolua_constant(tolua_S,"E_BLOCK_TRIPWIRE",E_BLOCK_TRIPWIRE); + tolua_constant(tolua_S,"E_BLOCK_EMERALD_BLOCK",E_BLOCK_EMERALD_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_SPRUCE_WOOD_STAIRS",E_BLOCK_SPRUCE_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_BIRCH_WOOD_STAIRS",E_BLOCK_BIRCH_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_JUNGLE_WOOD_STAIRS",E_BLOCK_JUNGLE_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_COMMAND_BLOCK",E_BLOCK_COMMAND_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_BEACON",E_BLOCK_BEACON); + tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE_WALL",E_BLOCK_COBBLESTONE_WALL); + tolua_constant(tolua_S,"E_BLOCK_FLOWER_POT",E_BLOCK_FLOWER_POT); + tolua_constant(tolua_S,"E_BLOCK_CARROTS",E_BLOCK_CARROTS); + tolua_constant(tolua_S,"E_BLOCK_POTATOES",E_BLOCK_POTATOES); + tolua_constant(tolua_S,"E_BLOCK_WOODEN_BUTTON",E_BLOCK_WOODEN_BUTTON); + tolua_constant(tolua_S,"E_BLOCK_HEAD",E_BLOCK_HEAD); + tolua_constant(tolua_S,"E_BLOCK_ANVIL",E_BLOCK_ANVIL); + tolua_constant(tolua_S,"E_BLOCK_TRAPPED_CHEST",E_BLOCK_TRAPPED_CHEST); + tolua_constant(tolua_S,"E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE",E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE",E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE); + tolua_constant(tolua_S,"E_BLOCK_INACTIVE_COMPARATOR",E_BLOCK_INACTIVE_COMPARATOR); + tolua_constant(tolua_S,"E_BLOCK_ACTIVE_COMPARATOR",E_BLOCK_ACTIVE_COMPARATOR); + tolua_constant(tolua_S,"E_BLOCK_DAYLIGHT_SENSOR",E_BLOCK_DAYLIGHT_SENSOR); + tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_REDSTONE",E_BLOCK_BLOCK_OF_REDSTONE); + tolua_constant(tolua_S,"E_BLOCK_NETHER_QUARTZ_ORE",E_BLOCK_NETHER_QUARTZ_ORE); + tolua_constant(tolua_S,"E_BLOCK_HOPPER",E_BLOCK_HOPPER); + tolua_constant(tolua_S,"E_BLOCK_QUARTZ_BLOCK",E_BLOCK_QUARTZ_BLOCK); + tolua_constant(tolua_S,"E_BLOCK_QUARTZ_STAIRS",E_BLOCK_QUARTZ_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_ACTIVATOR_RAIL",E_BLOCK_ACTIVATOR_RAIL); + tolua_constant(tolua_S,"E_BLOCK_DROPPER",E_BLOCK_DROPPER); + tolua_constant(tolua_S,"E_BLOCK_STAINED_CLAY",E_BLOCK_STAINED_CLAY); + tolua_constant(tolua_S,"E_BLOCK_STAINED_GLASS_PANE",E_BLOCK_STAINED_GLASS_PANE); + tolua_constant(tolua_S,"E_BLOCK_NEW_LEAVES",E_BLOCK_NEW_LEAVES); + tolua_constant(tolua_S,"E_BLOCK_NEW_LOG",E_BLOCK_NEW_LOG); + tolua_constant(tolua_S,"E_BLOCK_ACACIA_WOOD_STAIRS",E_BLOCK_ACACIA_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_DARK_OAK_WOOD_STAIRS",E_BLOCK_DARK_OAK_WOOD_STAIRS); + tolua_constant(tolua_S,"E_BLOCK_HAY_BALE",E_BLOCK_HAY_BALE); + tolua_constant(tolua_S,"E_BLOCK_CARPET",E_BLOCK_CARPET); + tolua_constant(tolua_S,"E_BLOCK_HARDENED_CLAY",E_BLOCK_HARDENED_CLAY); + tolua_constant(tolua_S,"E_BLOCK_BLOCK_OF_COAL",E_BLOCK_BLOCK_OF_COAL); + tolua_constant(tolua_S,"E_BLOCK_PACKED_ICE",E_BLOCK_PACKED_ICE); + tolua_constant(tolua_S,"E_BLOCK_BIG_FLOWER",E_BLOCK_BIG_FLOWER); + tolua_constant(tolua_S,"E_BLOCK_NUMBER_OF_TYPES",E_BLOCK_NUMBER_OF_TYPES); + tolua_constant(tolua_S,"E_BLOCK_MAX_TYPE_ID",E_BLOCK_MAX_TYPE_ID); + tolua_constant(tolua_S,"E_BLOCK_YELLOW_FLOWER",E_BLOCK_YELLOW_FLOWER); + tolua_constant(tolua_S,"E_BLOCK_RED_ROSE",E_BLOCK_RED_ROSE); + tolua_constant(tolua_S,"E_BLOCK_LOCKED_CHEST",E_BLOCK_LOCKED_CHEST); + tolua_constant(tolua_S,"E_ITEM_EMPTY",E_ITEM_EMPTY); + tolua_constant(tolua_S,"E_ITEM_FIRST",E_ITEM_FIRST); + tolua_constant(tolua_S,"E_ITEM_IRON_SHOVEL",E_ITEM_IRON_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_IRON_PICKAXE",E_ITEM_IRON_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_IRON_AXE",E_ITEM_IRON_AXE); + tolua_constant(tolua_S,"E_ITEM_FLINT_AND_STEEL",E_ITEM_FLINT_AND_STEEL); + tolua_constant(tolua_S,"E_ITEM_RED_APPLE",E_ITEM_RED_APPLE); + tolua_constant(tolua_S,"E_ITEM_BOW",E_ITEM_BOW); + tolua_constant(tolua_S,"E_ITEM_ARROW",E_ITEM_ARROW); + tolua_constant(tolua_S,"E_ITEM_COAL",E_ITEM_COAL); + tolua_constant(tolua_S,"E_ITEM_DIAMOND",E_ITEM_DIAMOND); + tolua_constant(tolua_S,"E_ITEM_IRON",E_ITEM_IRON); + tolua_constant(tolua_S,"E_ITEM_GOLD",E_ITEM_GOLD); + tolua_constant(tolua_S,"E_ITEM_IRON_SWORD",E_ITEM_IRON_SWORD); + tolua_constant(tolua_S,"E_ITEM_WOODEN_SWORD",E_ITEM_WOODEN_SWORD); + tolua_constant(tolua_S,"E_ITEM_WOODEN_SHOVEL",E_ITEM_WOODEN_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_WOODEN_PICKAXE",E_ITEM_WOODEN_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_WOODEN_AXE",E_ITEM_WOODEN_AXE); + tolua_constant(tolua_S,"E_ITEM_STONE_SWORD",E_ITEM_STONE_SWORD); + tolua_constant(tolua_S,"E_ITEM_STONE_SHOVEL",E_ITEM_STONE_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_STONE_PICKAXE",E_ITEM_STONE_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_STONE_AXE",E_ITEM_STONE_AXE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_SWORD",E_ITEM_DIAMOND_SWORD); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_SHOVEL",E_ITEM_DIAMOND_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_PICKAXE",E_ITEM_DIAMOND_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_AXE",E_ITEM_DIAMOND_AXE); + tolua_constant(tolua_S,"E_ITEM_STICK",E_ITEM_STICK); + tolua_constant(tolua_S,"E_ITEM_BOWL",E_ITEM_BOWL); + tolua_constant(tolua_S,"E_ITEM_MUSHROOM_SOUP",E_ITEM_MUSHROOM_SOUP); + tolua_constant(tolua_S,"E_ITEM_GOLD_SWORD",E_ITEM_GOLD_SWORD); + tolua_constant(tolua_S,"E_ITEM_GOLD_SHOVEL",E_ITEM_GOLD_SHOVEL); + tolua_constant(tolua_S,"E_ITEM_GOLD_PICKAXE",E_ITEM_GOLD_PICKAXE); + tolua_constant(tolua_S,"E_ITEM_GOLD_AXE",E_ITEM_GOLD_AXE); + tolua_constant(tolua_S,"E_ITEM_STRING",E_ITEM_STRING); + tolua_constant(tolua_S,"E_ITEM_FEATHER",E_ITEM_FEATHER); + tolua_constant(tolua_S,"E_ITEM_GUNPOWDER",E_ITEM_GUNPOWDER); + tolua_constant(tolua_S,"E_ITEM_WOODEN_HOE",E_ITEM_WOODEN_HOE); + tolua_constant(tolua_S,"E_ITEM_STONE_HOE",E_ITEM_STONE_HOE); + tolua_constant(tolua_S,"E_ITEM_IRON_HOE",E_ITEM_IRON_HOE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_HOE",E_ITEM_DIAMOND_HOE); + tolua_constant(tolua_S,"E_ITEM_GOLD_HOE",E_ITEM_GOLD_HOE); + tolua_constant(tolua_S,"E_ITEM_SEEDS",E_ITEM_SEEDS); + tolua_constant(tolua_S,"E_ITEM_WHEAT",E_ITEM_WHEAT); + tolua_constant(tolua_S,"E_ITEM_BREAD",E_ITEM_BREAD); + tolua_constant(tolua_S,"E_ITEM_LEATHER_CAP",E_ITEM_LEATHER_CAP); + tolua_constant(tolua_S,"E_ITEM_LEATHER_TUNIC",E_ITEM_LEATHER_TUNIC); + tolua_constant(tolua_S,"E_ITEM_LEATHER_PANTS",E_ITEM_LEATHER_PANTS); + tolua_constant(tolua_S,"E_ITEM_LEATHER_BOOTS",E_ITEM_LEATHER_BOOTS); + tolua_constant(tolua_S,"E_ITEM_CHAIN_HELMET",E_ITEM_CHAIN_HELMET); + tolua_constant(tolua_S,"E_ITEM_CHAIN_CHESTPLATE",E_ITEM_CHAIN_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_CHAIN_LEGGINGS",E_ITEM_CHAIN_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_CHAIN_BOOTS",E_ITEM_CHAIN_BOOTS); + tolua_constant(tolua_S,"E_ITEM_IRON_HELMET",E_ITEM_IRON_HELMET); + tolua_constant(tolua_S,"E_ITEM_IRON_CHESTPLATE",E_ITEM_IRON_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_IRON_LEGGINGS",E_ITEM_IRON_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_IRON_BOOTS",E_ITEM_IRON_BOOTS); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_HELMET",E_ITEM_DIAMOND_HELMET); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_CHESTPLATE",E_ITEM_DIAMOND_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_LEGGINGS",E_ITEM_DIAMOND_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_BOOTS",E_ITEM_DIAMOND_BOOTS); + tolua_constant(tolua_S,"E_ITEM_GOLD_HELMET",E_ITEM_GOLD_HELMET); + tolua_constant(tolua_S,"E_ITEM_GOLD_CHESTPLATE",E_ITEM_GOLD_CHESTPLATE); + tolua_constant(tolua_S,"E_ITEM_GOLD_LEGGINGS",E_ITEM_GOLD_LEGGINGS); + tolua_constant(tolua_S,"E_ITEM_GOLD_BOOTS",E_ITEM_GOLD_BOOTS); + tolua_constant(tolua_S,"E_ITEM_FLINT",E_ITEM_FLINT); + tolua_constant(tolua_S,"E_ITEM_RAW_PORKCHOP",E_ITEM_RAW_PORKCHOP); + tolua_constant(tolua_S,"E_ITEM_COOKED_PORKCHOP",E_ITEM_COOKED_PORKCHOP); + tolua_constant(tolua_S,"E_ITEM_PAINTINGS",E_ITEM_PAINTINGS); + tolua_constant(tolua_S,"E_ITEM_GOLDEN_APPLE",E_ITEM_GOLDEN_APPLE); + tolua_constant(tolua_S,"E_ITEM_SIGN",E_ITEM_SIGN); + tolua_constant(tolua_S,"E_ITEM_WOODEN_DOOR",E_ITEM_WOODEN_DOOR); + tolua_constant(tolua_S,"E_ITEM_BUCKET",E_ITEM_BUCKET); + tolua_constant(tolua_S,"E_ITEM_WATER_BUCKET",E_ITEM_WATER_BUCKET); + tolua_constant(tolua_S,"E_ITEM_LAVA_BUCKET",E_ITEM_LAVA_BUCKET); + tolua_constant(tolua_S,"E_ITEM_MINECART",E_ITEM_MINECART); + tolua_constant(tolua_S,"E_ITEM_SADDLE",E_ITEM_SADDLE); + tolua_constant(tolua_S,"E_ITEM_IRON_DOOR",E_ITEM_IRON_DOOR); + tolua_constant(tolua_S,"E_ITEM_REDSTONE_DUST",E_ITEM_REDSTONE_DUST); + tolua_constant(tolua_S,"E_ITEM_SNOWBALL",E_ITEM_SNOWBALL); + tolua_constant(tolua_S,"E_ITEM_BOAT",E_ITEM_BOAT); + tolua_constant(tolua_S,"E_ITEM_LEATHER",E_ITEM_LEATHER); + tolua_constant(tolua_S,"E_ITEM_MILK",E_ITEM_MILK); + tolua_constant(tolua_S,"E_ITEM_CLAY_BRICK",E_ITEM_CLAY_BRICK); + tolua_constant(tolua_S,"E_ITEM_CLAY",E_ITEM_CLAY); + tolua_constant(tolua_S,"E_ITEM_SUGARCANE",E_ITEM_SUGARCANE); + tolua_constant(tolua_S,"E_ITEM_SUGAR_CANE",E_ITEM_SUGAR_CANE); + tolua_constant(tolua_S,"E_ITEM_PAPER",E_ITEM_PAPER); + tolua_constant(tolua_S,"E_ITEM_BOOK",E_ITEM_BOOK); + tolua_constant(tolua_S,"E_ITEM_SLIMEBALL",E_ITEM_SLIMEBALL); + tolua_constant(tolua_S,"E_ITEM_CHEST_MINECART",E_ITEM_CHEST_MINECART); + tolua_constant(tolua_S,"E_ITEM_FURNACE_MINECART",E_ITEM_FURNACE_MINECART); + tolua_constant(tolua_S,"E_ITEM_EGG",E_ITEM_EGG); + tolua_constant(tolua_S,"E_ITEM_COMPASS",E_ITEM_COMPASS); + tolua_constant(tolua_S,"E_ITEM_FISHING_ROD",E_ITEM_FISHING_ROD); + tolua_constant(tolua_S,"E_ITEM_CLOCK",E_ITEM_CLOCK); + tolua_constant(tolua_S,"E_ITEM_GLOWSTONE_DUST",E_ITEM_GLOWSTONE_DUST); + tolua_constant(tolua_S,"E_ITEM_RAW_FISH",E_ITEM_RAW_FISH); + tolua_constant(tolua_S,"E_ITEM_COOKED_FISH",E_ITEM_COOKED_FISH); + tolua_constant(tolua_S,"E_ITEM_DYE",E_ITEM_DYE); + tolua_constant(tolua_S,"E_ITEM_BONE",E_ITEM_BONE); + tolua_constant(tolua_S,"E_ITEM_SUGAR",E_ITEM_SUGAR); + tolua_constant(tolua_S,"E_ITEM_CAKE",E_ITEM_CAKE); + tolua_constant(tolua_S,"E_ITEM_BED",E_ITEM_BED); + tolua_constant(tolua_S,"E_ITEM_REDSTONE_REPEATER",E_ITEM_REDSTONE_REPEATER); + tolua_constant(tolua_S,"E_ITEM_COOKIE",E_ITEM_COOKIE); + tolua_constant(tolua_S,"E_ITEM_MAP",E_ITEM_MAP); + tolua_constant(tolua_S,"E_ITEM_SHEARS",E_ITEM_SHEARS); + tolua_constant(tolua_S,"E_ITEM_MELON_SLICE",E_ITEM_MELON_SLICE); + tolua_constant(tolua_S,"E_ITEM_PUMPKIN_SEEDS",E_ITEM_PUMPKIN_SEEDS); + tolua_constant(tolua_S,"E_ITEM_MELON_SEEDS",E_ITEM_MELON_SEEDS); + tolua_constant(tolua_S,"E_ITEM_RAW_BEEF",E_ITEM_RAW_BEEF); + tolua_constant(tolua_S,"E_ITEM_STEAK",E_ITEM_STEAK); + tolua_constant(tolua_S,"E_ITEM_RAW_CHICKEN",E_ITEM_RAW_CHICKEN); + tolua_constant(tolua_S,"E_ITEM_COOKED_CHICKEN",E_ITEM_COOKED_CHICKEN); + tolua_constant(tolua_S,"E_ITEM_ROTTEN_FLESH",E_ITEM_ROTTEN_FLESH); + tolua_constant(tolua_S,"E_ITEM_ENDER_PEARL",E_ITEM_ENDER_PEARL); + tolua_constant(tolua_S,"E_ITEM_BLAZE_ROD",E_ITEM_BLAZE_ROD); + tolua_constant(tolua_S,"E_ITEM_GHAST_TEAR",E_ITEM_GHAST_TEAR); + tolua_constant(tolua_S,"E_ITEM_GOLD_NUGGET",E_ITEM_GOLD_NUGGET); + tolua_constant(tolua_S,"E_ITEM_NETHER_WART",E_ITEM_NETHER_WART); + tolua_constant(tolua_S,"E_ITEM_POTIONS",E_ITEM_POTIONS); + tolua_constant(tolua_S,"E_ITEM_GLASS_BOTTLE",E_ITEM_GLASS_BOTTLE); + tolua_constant(tolua_S,"E_ITEM_SPIDER_EYE",E_ITEM_SPIDER_EYE); + tolua_constant(tolua_S,"E_ITEM_FERMENTED_SPIDER_EYE",E_ITEM_FERMENTED_SPIDER_EYE); + tolua_constant(tolua_S,"E_ITEM_BLAZE_POWDER",E_ITEM_BLAZE_POWDER); + tolua_constant(tolua_S,"E_ITEM_MAGMA_CREAM",E_ITEM_MAGMA_CREAM); + tolua_constant(tolua_S,"E_ITEM_BREWING_STAND",E_ITEM_BREWING_STAND); + tolua_constant(tolua_S,"E_ITEM_CAULDRON",E_ITEM_CAULDRON); + tolua_constant(tolua_S,"E_ITEM_EYE_OF_ENDER",E_ITEM_EYE_OF_ENDER); + tolua_constant(tolua_S,"E_ITEM_GLISTERING_MELON",E_ITEM_GLISTERING_MELON); + tolua_constant(tolua_S,"E_ITEM_SPAWN_EGG",E_ITEM_SPAWN_EGG); + tolua_constant(tolua_S,"E_ITEM_BOTTLE_O_ENCHANTING",E_ITEM_BOTTLE_O_ENCHANTING); + tolua_constant(tolua_S,"E_ITEM_FIRE_CHARGE",E_ITEM_FIRE_CHARGE); + 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); + tolua_constant(tolua_S,"E_ITEM_ENCHANTED_BOOK",E_ITEM_ENCHANTED_BOOK); + tolua_constant(tolua_S,"E_ITEM_COMPARATOR",E_ITEM_COMPARATOR); + tolua_constant(tolua_S,"E_ITEM_NETHER_BRICK",E_ITEM_NETHER_BRICK); + tolua_constant(tolua_S,"E_ITEM_NETHER_QUARTZ",E_ITEM_NETHER_QUARTZ); + tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_TNT",E_ITEM_MINECART_WITH_TNT); + tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_HOPPER",E_ITEM_MINECART_WITH_HOPPER); + tolua_constant(tolua_S,"E_ITEM_IRON_HORSE_ARMOR",E_ITEM_IRON_HORSE_ARMOR); + tolua_constant(tolua_S,"E_ITEM_GOLD_HORSE_ARMOR",E_ITEM_GOLD_HORSE_ARMOR); + tolua_constant(tolua_S,"E_ITEM_DIAMOND_HORSE_ARMOR",E_ITEM_DIAMOND_HORSE_ARMOR); + tolua_constant(tolua_S,"E_ITEM_LEAD",E_ITEM_LEAD); + tolua_constant(tolua_S,"E_ITEM_NAME_TAG",E_ITEM_NAME_TAG); + tolua_constant(tolua_S,"E_ITEM_MINECART_WITH_COMMAND_BLOCK",E_ITEM_MINECART_WITH_COMMAND_BLOCK); + tolua_constant(tolua_S,"E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES",E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES); + tolua_constant(tolua_S,"E_ITEM_MAX_CONSECUTIVE_TYPE_ID",E_ITEM_MAX_CONSECUTIVE_TYPE_ID); + tolua_constant(tolua_S,"E_ITEM_FIRST_DISC",E_ITEM_FIRST_DISC); + tolua_constant(tolua_S,"E_ITEM_13_DISC",E_ITEM_13_DISC); + tolua_constant(tolua_S,"E_ITEM_CAT_DISC",E_ITEM_CAT_DISC); + tolua_constant(tolua_S,"E_ITEM_BLOCKS_DISC",E_ITEM_BLOCKS_DISC); + tolua_constant(tolua_S,"E_ITEM_CHIRP_DISC",E_ITEM_CHIRP_DISC); + tolua_constant(tolua_S,"E_ITEM_FAR_DISC",E_ITEM_FAR_DISC); + tolua_constant(tolua_S,"E_ITEM_MALL_DISC",E_ITEM_MALL_DISC); + tolua_constant(tolua_S,"E_ITEM_MELLOHI_DISC",E_ITEM_MELLOHI_DISC); + tolua_constant(tolua_S,"E_ITEM_STAL_DISC",E_ITEM_STAL_DISC); + tolua_constant(tolua_S,"E_ITEM_STRAD_DISC",E_ITEM_STRAD_DISC); + tolua_constant(tolua_S,"E_ITEM_WARD_DISC",E_ITEM_WARD_DISC); + tolua_constant(tolua_S,"E_ITEM_11_DISC",E_ITEM_11_DISC); + tolua_constant(tolua_S,"E_ITEM_WAIT_DISC",E_ITEM_WAIT_DISC); + tolua_constant(tolua_S,"E_ITEM_LAST_DISC_PLUS_ONE",E_ITEM_LAST_DISC_PLUS_ONE); + tolua_constant(tolua_S,"E_ITEM_LAST_DISC",E_ITEM_LAST_DISC); + tolua_constant(tolua_S,"E_ITEM_LAST",E_ITEM_LAST); + tolua_constant(tolua_S,"E_META_CHEST_FACING_ZM",E_META_CHEST_FACING_ZM); + tolua_constant(tolua_S,"E_META_CHEST_FACING_ZP",E_META_CHEST_FACING_ZP); + tolua_constant(tolua_S,"E_META_CHEST_FACING_XM",E_META_CHEST_FACING_XM); + tolua_constant(tolua_S,"E_META_CHEST_FACING_XP",E_META_CHEST_FACING_XP); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YM",E_META_DROPSPENSER_FACING_YM); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_YP",E_META_DROPSPENSER_FACING_YP); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZM",E_META_DROPSPENSER_FACING_ZM); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_ZP",E_META_DROPSPENSER_FACING_ZP); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XM",E_META_DROPSPENSER_FACING_XM); + tolua_constant(tolua_S,"E_META_DROPSPENSER_FACING_XP",E_META_DROPSPENSER_FACING_XP); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE",E_META_DOUBLE_STONE_SLAB_STONE); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_SANDSTONE",E_META_DOUBLE_STONE_SLAB_SANDSTONE); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_WOODEN",E_META_DOUBLE_STONE_SLAB_WOODEN); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_COBBLESTONE",E_META_DOUBLE_STONE_SLAB_COBBLESTONE); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_BRICK",E_META_DOUBLE_STONE_SLAB_BRICK); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_BRICK",E_META_DOUBLE_STONE_SLAB_STONE_BRICK); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_NETHER_BRICK",E_META_DOUBLE_STONE_SLAB_NETHER_BRICK); + tolua_constant(tolua_S,"E_META_DOUBLE_STONE_SLAB_STONE_SECRET",E_META_DOUBLE_STONE_SLAB_STONE_SECRET); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_YM",E_META_HOPPER_FACING_YM); + tolua_constant(tolua_S,"E_META_HOPPER_UNATTACHED",E_META_HOPPER_UNATTACHED); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZM",E_META_HOPPER_FACING_ZM); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_ZP",E_META_HOPPER_FACING_ZP); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_XM",E_META_HOPPER_FACING_XM); + tolua_constant(tolua_S,"E_META_HOPPER_FACING_XP",E_META_HOPPER_FACING_XP); + tolua_constant(tolua_S,"E_META_LEAVES_APPLE",E_META_LEAVES_APPLE); + tolua_constant(tolua_S,"E_META_LEAVES_CONIFER",E_META_LEAVES_CONIFER); + tolua_constant(tolua_S,"E_META_LEAVES_BIRCH",E_META_LEAVES_BIRCH); + tolua_constant(tolua_S,"E_META_LEAVES_JUNGLE",E_META_LEAVES_JUNGLE); + tolua_constant(tolua_S,"E_META_LOG_APPLE",E_META_LOG_APPLE); + tolua_constant(tolua_S,"E_META_LOG_CONIFER",E_META_LOG_CONIFER); + tolua_constant(tolua_S,"E_META_LOG_BIRCH",E_META_LOG_BIRCH); + tolua_constant(tolua_S,"E_META_LOG_JUNGLE",E_META_LOG_JUNGLE); + tolua_constant(tolua_S,"E_META_PLANKS_APPLE",E_META_PLANKS_APPLE); + tolua_constant(tolua_S,"E_META_PLANKS_CONIFER",E_META_PLANKS_CONIFER); + tolua_constant(tolua_S,"E_META_PLANKS_BIRCH",E_META_PLANKS_BIRCH); + tolua_constant(tolua_S,"E_META_PLANKS_JUNGLE",E_META_PLANKS_JUNGLE); + tolua_constant(tolua_S,"E_META_SANDSTONE_NORMAL",E_META_SANDSTONE_NORMAL); + tolua_constant(tolua_S,"E_META_SANDSTONE_ORNAMENT",E_META_SANDSTONE_ORNAMENT); + tolua_constant(tolua_S,"E_META_SANDSTONE_SMOOTH",E_META_SANDSTONE_SMOOTH); + tolua_constant(tolua_S,"E_META_SAPLING_APPLE",E_META_SAPLING_APPLE); + tolua_constant(tolua_S,"E_META_SAPLING_CONIFER",E_META_SAPLING_CONIFER); + tolua_constant(tolua_S,"E_META_SAPLING_BIRCH",E_META_SAPLING_BIRCH); + tolua_constant(tolua_S,"E_META_SAPLING_JUNGLE",E_META_SAPLING_JUNGLE); + tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE",E_META_SILVERFISH_EGG_STONE); + tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_COBBLESTONE",E_META_SILVERFISH_EGG_COBBLESTONE); + tolua_constant(tolua_S,"E_META_SILVERFISH_EGG_STONE_BRICK",E_META_SILVERFISH_EGG_STONE_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE",E_META_STONE_SLAB_STONE); + tolua_constant(tolua_S,"E_META_STONE_SLAB_SANDSTONE",E_META_STONE_SLAB_SANDSTONE); + tolua_constant(tolua_S,"E_META_STONE_SLAB_PLANKS",E_META_STONE_SLAB_PLANKS); + tolua_constant(tolua_S,"E_META_STONE_SLAB_COBBLESTONE",E_META_STONE_SLAB_COBBLESTONE); + tolua_constant(tolua_S,"E_META_STONE_SLAB_BRICK",E_META_STONE_SLAB_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_BRICK",E_META_STONE_SLAB_STONE_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_NETHER_BRICK",E_META_STONE_SLAB_NETHER_BRICK); + tolua_constant(tolua_S,"E_META_STONE_SLAB_STONE_SECRET",E_META_STONE_SLAB_STONE_SECRET); + tolua_constant(tolua_S,"E_META_STONE_BRICK_NORMAL",E_META_STONE_BRICK_NORMAL); + tolua_constant(tolua_S,"E_META_STONE_BRICK_MOSSY",E_META_STONE_BRICK_MOSSY); + tolua_constant(tolua_S,"E_META_STONE_BRICK_CRACKED",E_META_STONE_BRICK_CRACKED); + tolua_constant(tolua_S,"E_META_STONE_BRICK_ORNAMENT",E_META_STONE_BRICK_ORNAMENT); + tolua_constant(tolua_S,"E_META_TALL_GRASS_DEAD_SHRUB",E_META_TALL_GRASS_DEAD_SHRUB); + tolua_constant(tolua_S,"E_META_TALL_GRASS_GRASS",E_META_TALL_GRASS_GRASS); + tolua_constant(tolua_S,"E_META_TALL_GRASS_FERN",E_META_TALL_GRASS_FERN); + tolua_constant(tolua_S,"E_META_TORCH_EAST",E_META_TORCH_EAST); + tolua_constant(tolua_S,"E_META_TORCH_WEST",E_META_TORCH_WEST); + tolua_constant(tolua_S,"E_META_TORCH_SOUTH",E_META_TORCH_SOUTH); + tolua_constant(tolua_S,"E_META_TORCH_NORTH",E_META_TORCH_NORTH); + tolua_constant(tolua_S,"E_META_TORCH_FLOOR",E_META_TORCH_FLOOR); + tolua_constant(tolua_S,"E_META_TORCH_XM",E_META_TORCH_XM); + tolua_constant(tolua_S,"E_META_TORCH_XP",E_META_TORCH_XP); + tolua_constant(tolua_S,"E_META_TORCH_ZM",E_META_TORCH_ZM); + tolua_constant(tolua_S,"E_META_TORCH_ZP",E_META_TORCH_ZP); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_APPLE",E_META_WOODEN_DOUBLE_SLAB_APPLE); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_CONIFER",E_META_WOODEN_DOUBLE_SLAB_CONIFER); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_BIRCH",E_META_WOODEN_DOUBLE_SLAB_BIRCH); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_JUNGLE",E_META_WOODEN_DOUBLE_SLAB_JUNGLE); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_ACACIA",E_META_WOODEN_DOUBLE_SLAB_ACACIA); + tolua_constant(tolua_S,"E_META_WOODEN_DOUBLE_SLAB_DARK_OAK",E_META_WOODEN_DOUBLE_SLAB_DARK_OAK); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_APPLE",E_META_WOODEN_SLAB_APPLE); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_CONIFER",E_META_WOODEN_SLAB_CONIFER); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_BIRCH",E_META_WOODEN_SLAB_BIRCH); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_JUNGLE",E_META_WOODEN_SLAB_JUNGLE); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_ACACIA",E_META_WOODEN_SLAB_ACACIA); + tolua_constant(tolua_S,"E_META_WOODEN_SLAB_DARK_OAK",E_META_WOODEN_SLAB_DARK_OAK); + tolua_constant(tolua_S,"E_META_WOOL_WHITE",E_META_WOOL_WHITE); + tolua_constant(tolua_S,"E_META_WOOL_ORANGE",E_META_WOOL_ORANGE); + tolua_constant(tolua_S,"E_META_WOOL_MAGENTA",E_META_WOOL_MAGENTA); + tolua_constant(tolua_S,"E_META_WOOL_LIGHTBLUE",E_META_WOOL_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_WOOL_YELLOW",E_META_WOOL_YELLOW); + tolua_constant(tolua_S,"E_META_WOOL_LIGHTGREEN",E_META_WOOL_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_WOOL_PINK",E_META_WOOL_PINK); + tolua_constant(tolua_S,"E_META_WOOL_GRAY",E_META_WOOL_GRAY); + tolua_constant(tolua_S,"E_META_WOOL_LIGHTGRAY",E_META_WOOL_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_WOOL_CYAN",E_META_WOOL_CYAN); + tolua_constant(tolua_S,"E_META_WOOL_PURPLE",E_META_WOOL_PURPLE); + tolua_constant(tolua_S,"E_META_WOOL_BLUE",E_META_WOOL_BLUE); + tolua_constant(tolua_S,"E_META_WOOL_BROWN",E_META_WOOL_BROWN); + tolua_constant(tolua_S,"E_META_WOOL_GREEN",E_META_WOOL_GREEN); + tolua_constant(tolua_S,"E_META_WOOL_RED",E_META_WOOL_RED); + tolua_constant(tolua_S,"E_META_WOOL_BLACK",E_META_WOOL_BLACK); + tolua_constant(tolua_S,"E_META_CARPET_WHITE",E_META_CARPET_WHITE); + tolua_constant(tolua_S,"E_META_CARPET_ORANGE",E_META_CARPET_ORANGE); + tolua_constant(tolua_S,"E_META_CARPET_MAGENTA",E_META_CARPET_MAGENTA); + tolua_constant(tolua_S,"E_META_CARPET_LIGHTBLUE",E_META_CARPET_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_CARPET_YELLOW",E_META_CARPET_YELLOW); + tolua_constant(tolua_S,"E_META_CARPET_LIGHTGREEN",E_META_CARPET_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_CARPET_PINK",E_META_CARPET_PINK); + tolua_constant(tolua_S,"E_META_CARPET_GRAY",E_META_CARPET_GRAY); + tolua_constant(tolua_S,"E_META_CARPET_LIGHTGRAY",E_META_CARPET_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_CARPET_CYAN",E_META_CARPET_CYAN); + tolua_constant(tolua_S,"E_META_CARPET_PURPLE",E_META_CARPET_PURPLE); + tolua_constant(tolua_S,"E_META_CARPET_BLUE",E_META_CARPET_BLUE); + tolua_constant(tolua_S,"E_META_CARPET_BROWN",E_META_CARPET_BROWN); + tolua_constant(tolua_S,"E_META_CARPET_GREEN",E_META_CARPET_GREEN); + tolua_constant(tolua_S,"E_META_CARPET_RED",E_META_CARPET_RED); + tolua_constant(tolua_S,"E_META_CARPET_BLACK",E_META_CARPET_BLACK); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_WHITE",E_META_STAINED_CLAY_WHITE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_ORANGE",E_META_STAINED_CLAY_ORANGE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_MAGENTA",E_META_STAINED_CLAY_MAGENTA); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTBLUE",E_META_STAINED_CLAY_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_YELLOW",E_META_STAINED_CLAY_YELLOW); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGREEN",E_META_STAINED_CLAY_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_PINK",E_META_STAINED_CLAY_PINK); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_GRAY",E_META_STAINED_CLAY_GRAY); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_LIGHTGRAY",E_META_STAINED_CLAY_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_CYAN",E_META_STAINED_CLAY_CYAN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_PURPLE",E_META_STAINED_CLAY_PURPLE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLUE",E_META_STAINED_CLAY_BLUE); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_BROWN",E_META_STAINED_CLAY_BROWN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_GREEN",E_META_STAINED_CLAY_GREEN); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_RED",E_META_STAINED_CLAY_RED); + tolua_constant(tolua_S,"E_META_STAINED_CLAY_BLACK",E_META_STAINED_CLAY_BLACK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_WHITE",E_META_STAINED_GLASS_WHITE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_ORANGE",E_META_STAINED_GLASS_ORANGE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_MAGENTA",E_META_STAINED_GLASS_MAGENTA); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTBLUE",E_META_STAINED_GLASS_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_YELLOW",E_META_STAINED_GLASS_YELLOW); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGREEN",E_META_STAINED_GLASS_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PINK",E_META_STAINED_GLASS_PINK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_GRAY",E_META_STAINED_GLASS_GRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_LIGHTGRAY",E_META_STAINED_GLASS_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_CYAN",E_META_STAINED_GLASS_CYAN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PURPLE",E_META_STAINED_GLASS_PURPLE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLUE",E_META_STAINED_GLASS_BLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_BROWN",E_META_STAINED_GLASS_BROWN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_GREEN",E_META_STAINED_GLASS_GREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_RED",E_META_STAINED_GLASS_RED); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_BLACK",E_META_STAINED_GLASS_BLACK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_WHITE",E_META_STAINED_GLASS_PANE_WHITE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_ORANGE",E_META_STAINED_GLASS_PANE_ORANGE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_MAGENTA",E_META_STAINED_GLASS_PANE_MAGENTA); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTBLUE",E_META_STAINED_GLASS_PANE_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_YELLOW",E_META_STAINED_GLASS_PANE_YELLOW); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGREEN",E_META_STAINED_GLASS_PANE_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PINK",E_META_STAINED_GLASS_PANE_PINK); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GRAY",E_META_STAINED_GLASS_PANE_GRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_LIGHTGRAY",E_META_STAINED_GLASS_PANE_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_CYAN",E_META_STAINED_GLASS_PANE_CYAN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_PURPLE",E_META_STAINED_GLASS_PANE_PURPLE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLUE",E_META_STAINED_GLASS_PANE_BLUE); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BROWN",E_META_STAINED_GLASS_PANE_BROWN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_GREEN",E_META_STAINED_GLASS_PANE_GREEN); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_RED",E_META_STAINED_GLASS_PANE_RED); + tolua_constant(tolua_S,"E_META_STAINED_GLASS_PANE_BLACK",E_META_STAINED_GLASS_PANE_BLACK); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_ONE",E_META_SNOW_LAYER_ONE); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_TWO",E_META_SNOW_LAYER_TWO); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_THREE",E_META_SNOW_LAYER_THREE); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_FOUR",E_META_SNOW_LAYER_FOUR); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_FIVE",E_META_SNOW_LAYER_FIVE); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_SIX",E_META_SNOW_LAYER_SIX); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_SEVEN",E_META_SNOW_LAYER_SEVEN); + tolua_constant(tolua_S,"E_META_SNOW_LAYER_EIGHT",E_META_SNOW_LAYER_EIGHT); + tolua_constant(tolua_S,"E_META_RAIL_ZM_ZP",E_META_RAIL_ZM_ZP); + tolua_constant(tolua_S,"E_META_RAIL_XM_XP",E_META_RAIL_XM_XP); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XP",E_META_RAIL_ASCEND_XP); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_XM",E_META_RAIL_ASCEND_XM); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZM",E_META_RAIL_ASCEND_ZM); + tolua_constant(tolua_S,"E_META_RAIL_ASCEND_ZP",E_META_RAIL_ASCEND_ZP); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XP",E_META_RAIL_CURVED_ZP_XP); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZP_XM",E_META_RAIL_CURVED_ZP_XM); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XM",E_META_RAIL_CURVED_ZM_XM); + tolua_constant(tolua_S,"E_META_RAIL_CURVED_ZM_XP",E_META_RAIL_CURVED_ZM_XP); + tolua_constant(tolua_S,"E_META_NEW_LEAVES_ACACIA_WOOD",E_META_NEW_LEAVES_ACACIA_WOOD); + tolua_constant(tolua_S,"E_META_NEW_LEAVES_DARK_OAK_WOOD",E_META_NEW_LEAVES_DARK_OAK_WOOD); + tolua_constant(tolua_S,"E_META_NEW_LOG_ACACIA_WOOD",E_META_NEW_LOG_ACACIA_WOOD); + tolua_constant(tolua_S,"E_META_NEW_LOG_DARK_OAK_WOOD",E_META_NEW_LOG_DARK_OAK_WOOD); + tolua_constant(tolua_S,"E_META_FLOWER_POPPY",E_META_FLOWER_POPPY); + tolua_constant(tolua_S,"E_META_FLOWER_BLUE_ORCHID",E_META_FLOWER_BLUE_ORCHID); + tolua_constant(tolua_S,"E_META_FLOWER_ALLIUM",E_META_FLOWER_ALLIUM); + tolua_constant(tolua_S,"E_META_FLOWER_RED_TULIP",E_META_FLOWER_RED_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_ORANGE_TULIP",E_META_FLOWER_ORANGE_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_WHITE_TULIP",E_META_FLOWER_WHITE_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_PINK_TULIP",E_META_FLOWER_PINK_TULIP); + tolua_constant(tolua_S,"E_META_FLOWER_OXEYE_DAISY",E_META_FLOWER_OXEYE_DAISY); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_SUNFLOWER",E_META_BIG_FLOWER_SUNFLOWER); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_LILAC",E_META_BIG_FLOWER_LILAC); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_DOUBLE_TALL_GRASS",E_META_BIG_FLOWER_DOUBLE_TALL_GRASS); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_LARGE_FERN",E_META_BIG_FLOWER_LARGE_FERN); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_ROSE_BUSH",E_META_BIG_FLOWER_ROSE_BUSH); + tolua_constant(tolua_S,"E_META_BIG_FLOWER_PEONY",E_META_BIG_FLOWER_PEONY); + tolua_constant(tolua_S,"E_META_COAL_NORMAL",E_META_COAL_NORMAL); + tolua_constant(tolua_S,"E_META_COAL_CHARCOAL",E_META_COAL_CHARCOAL); + tolua_constant(tolua_S,"E_META_DYE_BLACK",E_META_DYE_BLACK); + tolua_constant(tolua_S,"E_META_DYE_RED",E_META_DYE_RED); + tolua_constant(tolua_S,"E_META_DYE_GREEN",E_META_DYE_GREEN); + tolua_constant(tolua_S,"E_META_DYE_BROWN",E_META_DYE_BROWN); + tolua_constant(tolua_S,"E_META_DYE_BLUE",E_META_DYE_BLUE); + tolua_constant(tolua_S,"E_META_DYE_PURPLE",E_META_DYE_PURPLE); + tolua_constant(tolua_S,"E_META_DYE_CYAN",E_META_DYE_CYAN); + tolua_constant(tolua_S,"E_META_DYE_LIGHTGRAY",E_META_DYE_LIGHTGRAY); + tolua_constant(tolua_S,"E_META_DYE_GRAY",E_META_DYE_GRAY); + tolua_constant(tolua_S,"E_META_DYE_PINK",E_META_DYE_PINK); + tolua_constant(tolua_S,"E_META_DYE_LIGHTGREEN",E_META_DYE_LIGHTGREEN); + tolua_constant(tolua_S,"E_META_DYE_YELLOW",E_META_DYE_YELLOW); + tolua_constant(tolua_S,"E_META_DYE_LIGHTBLUE",E_META_DYE_LIGHTBLUE); + tolua_constant(tolua_S,"E_META_DYE_MAGENTA",E_META_DYE_MAGENTA); + tolua_constant(tolua_S,"E_META_DYE_ORANGE",E_META_DYE_ORANGE); + tolua_constant(tolua_S,"E_META_DYE_WHITE",E_META_DYE_WHITE); + tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_NORMAL",E_META_GOLDEN_APPLE_NORMAL); + tolua_constant(tolua_S,"E_META_GOLDEN_APPLE_ENCHANTED",E_META_GOLDEN_APPLE_ENCHANTED); + tolua_constant(tolua_S,"E_META_RAW_FISH_FISH",E_META_RAW_FISH_FISH); + tolua_constant(tolua_S,"E_META_RAW_FISH_SALMON",E_META_RAW_FISH_SALMON); + tolua_constant(tolua_S,"E_META_RAW_FISH_CLOWNFISH",E_META_RAW_FISH_CLOWNFISH); + tolua_constant(tolua_S,"E_META_RAW_FISH_PUFFERFISH",E_META_RAW_FISH_PUFFERFISH); + tolua_constant(tolua_S,"E_META_COOKED_FISH_FISH",E_META_COOKED_FISH_FISH); + tolua_constant(tolua_S,"E_META_COOKED_FISH_SALMON",E_META_COOKED_FISH_SALMON); + tolua_constant(tolua_S,"E_META_COOKED_FISH_CLOWNFISH",E_META_COOKED_FISH_CLOWNFISH); + tolua_constant(tolua_S,"E_META_COOKED_FISH_PUFFERFISH",E_META_COOKED_FISH_PUFFERFISH); + 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_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); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDERMAN",E_META_SPAWN_EGG_ENDERMAN); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_CAVE_SPIDER",E_META_SPAWN_EGG_CAVE_SPIDER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SILVERFISH",E_META_SPAWN_EGG_SILVERFISH); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BLAZE",E_META_SPAWN_EGG_BLAZE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MAGMA_CUBE",E_META_SPAWN_EGG_MAGMA_CUBE); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_ENDER_DRAGON",E_META_SPAWN_EGG_ENDER_DRAGON); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITHER",E_META_SPAWN_EGG_WITHER); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_BAT",E_META_SPAWN_EGG_BAT); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WITCH",E_META_SPAWN_EGG_WITCH); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_PIG",E_META_SPAWN_EGG_PIG); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SHEEP",E_META_SPAWN_EGG_SHEEP); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_COW",E_META_SPAWN_EGG_COW); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_CHICKEN",E_META_SPAWN_EGG_CHICKEN); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SQUID",E_META_SPAWN_EGG_SQUID); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_WOLF",E_META_SPAWN_EGG_WOLF); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_MOOSHROOM",E_META_SPAWN_EGG_MOOSHROOM); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_SNOW_GOLEM",E_META_SPAWN_EGG_SNOW_GOLEM); + tolua_constant(tolua_S,"E_META_SPAWN_EGG_OCELOT",E_META_SPAWN_EGG_OCELOT); + 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); + tolua_constant(tolua_S,"dtAttack",dtAttack); + tolua_constant(tolua_S,"dtRangedAttack",dtRangedAttack); + tolua_constant(tolua_S,"dtLightning",dtLightning); + tolua_constant(tolua_S,"dtFalling",dtFalling); + tolua_constant(tolua_S,"dtDrowning",dtDrowning); + tolua_constant(tolua_S,"dtSuffocating",dtSuffocating); + tolua_constant(tolua_S,"dtStarving",dtStarving); + tolua_constant(tolua_S,"dtCactusContact",dtCactusContact); + tolua_constant(tolua_S,"dtLavaContact",dtLavaContact); + tolua_constant(tolua_S,"dtPoisoning",dtPoisoning); + tolua_constant(tolua_S,"dtOnFire",dtOnFire); + tolua_constant(tolua_S,"dtFireContact",dtFireContact); + tolua_constant(tolua_S,"dtInVoid",dtInVoid); + tolua_constant(tolua_S,"dtPotionOfHarming",dtPotionOfHarming); + tolua_constant(tolua_S,"dtEnderPearl",dtEnderPearl); + tolua_constant(tolua_S,"dtAdmin",dtAdmin); + tolua_constant(tolua_S,"dtPawnAttack",dtPawnAttack); + tolua_constant(tolua_S,"dtEntityAttack",dtEntityAttack); + tolua_constant(tolua_S,"dtMob",dtMob); + tolua_constant(tolua_S,"dtMobAttack",dtMobAttack); + tolua_constant(tolua_S,"dtArrowAttack",dtArrowAttack); + tolua_constant(tolua_S,"dtArrow",dtArrow); + tolua_constant(tolua_S,"dtProjectile",dtProjectile); + tolua_constant(tolua_S,"dtFall",dtFall); + tolua_constant(tolua_S,"dtDrown",dtDrown); + tolua_constant(tolua_S,"dtSuffocation",dtSuffocation); + tolua_constant(tolua_S,"dtStarvation",dtStarvation); + tolua_constant(tolua_S,"dtHunger",dtHunger); + tolua_constant(tolua_S,"dtCactus",dtCactus); + tolua_constant(tolua_S,"dtCactuses",dtCactuses); + tolua_constant(tolua_S,"dtCacti",dtCacti); + tolua_constant(tolua_S,"dtLava",dtLava); + tolua_constant(tolua_S,"dtPoison",dtPoison); + tolua_constant(tolua_S,"dtBurning",dtBurning); + tolua_constant(tolua_S,"dtInFire",dtInFire); + tolua_constant(tolua_S,"dtPlugin",dtPlugin); + tolua_constant(tolua_S,"esOther",esOther); + tolua_constant(tolua_S,"esPrimedTNT",esPrimedTNT); + tolua_constant(tolua_S,"esCreeper",esCreeper); + tolua_constant(tolua_S,"esBed",esBed); + tolua_constant(tolua_S,"esEnderCrystal",esEnderCrystal); + tolua_constant(tolua_S,"esGhastFireball",esGhastFireball); + tolua_constant(tolua_S,"esWitherSkullBlack",esWitherSkullBlack); + tolua_constant(tolua_S,"esWitherSkullBlue",esWitherSkullBlue); + tolua_constant(tolua_S,"esWitherBirth",esWitherBirth); + tolua_constant(tolua_S,"esPlugin",esPlugin); + tolua_function(tolua_S,"BlockStringToType",tolua_AllToLua_BlockStringToType00); + tolua_function(tolua_S,"StringToItem",tolua_AllToLua_StringToItem00); + tolua_function(tolua_S,"ItemToString",tolua_AllToLua_ItemToString00); + tolua_function(tolua_S,"ItemTypeToString",tolua_AllToLua_ItemTypeToString00); + tolua_function(tolua_S,"ItemToFullString",tolua_AllToLua_ItemToFullString00); + tolua_function(tolua_S,"StringToBiome",tolua_AllToLua_StringToBiome00); + tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_StringToMobType00); + tolua_function(tolua_S,"StringToDimension",tolua_AllToLua_StringToDimension00); + tolua_function(tolua_S,"DamageTypeToString",tolua_AllToLua_DamageTypeToString00); + tolua_function(tolua_S,"StringToDamageType",tolua_AllToLua_StringToDamageType00); + tolua_function(tolua_S,"GetIniItemSet",tolua_AllToLua_GetIniItemSet00); + tolua_function(tolua_S,"TrimString",tolua_AllToLua_TrimString00); + tolua_function(tolua_S,"NoCaseCompare",tolua_AllToLua_NoCaseCompare00); + tolua_function(tolua_S,"ReplaceString",tolua_AllToLua_ReplaceString00); + tolua_function(tolua_S,"EscapeString",tolua_AllToLua_EscapeString00); + tolua_function(tolua_S,"StripColorCodes",tolua_AllToLua_StripColorCodes00); + tolua_array(tolua_S,"g_BlockLightValue",tolua_get_AllToLua_g_BlockLightValue,tolua_set_AllToLua_g_BlockLightValue); + tolua_array(tolua_S,"g_BlockSpreadLightFalloff",tolua_get_AllToLua_g_BlockSpreadLightFalloff,tolua_set_AllToLua_g_BlockSpreadLightFalloff); + tolua_array(tolua_S,"g_BlockTransparent",tolua_get_AllToLua_g_BlockTransparent,tolua_set_AllToLua_g_BlockTransparent); + tolua_array(tolua_S,"g_BlockOneHitDig",tolua_get_AllToLua_g_BlockOneHitDig,tolua_set_AllToLua_g_BlockOneHitDig); + tolua_array(tolua_S,"g_BlockPistonBreakable",tolua_get_AllToLua_g_BlockPistonBreakable,tolua_set_AllToLua_g_BlockPistonBreakable); + tolua_array(tolua_S,"g_BlockIsSnowable",tolua_get_AllToLua_g_BlockIsSnowable,tolua_set_AllToLua_g_BlockIsSnowable); + tolua_array(tolua_S,"g_BlockRequiresSpecialTool",tolua_get_AllToLua_g_BlockRequiresSpecialTool,tolua_set_AllToLua_g_BlockRequiresSpecialTool); + tolua_array(tolua_S,"g_BlockIsSolid",tolua_get_AllToLua_g_BlockIsSolid,tolua_set_AllToLua_g_BlockIsSolid); + tolua_array(tolua_S,"g_BlockIsTorchPlaceable",tolua_get_AllToLua_g_BlockIsTorchPlaceable,tolua_set_AllToLua_g_BlockIsTorchPlaceable); + tolua_constant(tolua_S,"MAX_EXPERIENCE_ORB_SIZE",MAX_EXPERIENCE_ORB_SIZE); + tolua_constant(tolua_S,"BLOCK_FACE_NONE",BLOCK_FACE_NONE); + tolua_constant(tolua_S,"BLOCK_FACE_XM",BLOCK_FACE_XM); + tolua_constant(tolua_S,"BLOCK_FACE_XP",BLOCK_FACE_XP); + tolua_constant(tolua_S,"BLOCK_FACE_YM",BLOCK_FACE_YM); + tolua_constant(tolua_S,"BLOCK_FACE_YP",BLOCK_FACE_YP); + tolua_constant(tolua_S,"BLOCK_FACE_ZM",BLOCK_FACE_ZM); + tolua_constant(tolua_S,"BLOCK_FACE_ZP",BLOCK_FACE_ZP); + tolua_constant(tolua_S,"BLOCK_FACE_BOTTOM",BLOCK_FACE_BOTTOM); + tolua_constant(tolua_S,"BLOCK_FACE_TOP",BLOCK_FACE_TOP); + tolua_constant(tolua_S,"BLOCK_FACE_NORTH",BLOCK_FACE_NORTH); + tolua_constant(tolua_S,"BLOCK_FACE_SOUTH",BLOCK_FACE_SOUTH); + tolua_constant(tolua_S,"BLOCK_FACE_WEST",BLOCK_FACE_WEST); + tolua_constant(tolua_S,"BLOCK_FACE_EAST",BLOCK_FACE_EAST); + tolua_constant(tolua_S,"DIG_STATUS_STARTED",DIG_STATUS_STARTED); + tolua_constant(tolua_S,"DIG_STATUS_CANCELLED",DIG_STATUS_CANCELLED); + tolua_constant(tolua_S,"DIG_STATUS_FINISHED",DIG_STATUS_FINISHED); + tolua_constant(tolua_S,"DIG_STATUS_DROP_HELD",DIG_STATUS_DROP_HELD); + tolua_constant(tolua_S,"DIG_STATUS_SHOOT_EAT",DIG_STATUS_SHOOT_EAT); + tolua_constant(tolua_S,"caLeftClick",caLeftClick); + tolua_constant(tolua_S,"caRightClick",caRightClick); + tolua_constant(tolua_S,"caShiftLeftClick",caShiftLeftClick); + tolua_constant(tolua_S,"caShiftRightClick",caShiftRightClick); + tolua_constant(tolua_S,"caNumber1",caNumber1); + tolua_constant(tolua_S,"caNumber2",caNumber2); + tolua_constant(tolua_S,"caNumber3",caNumber3); + tolua_constant(tolua_S,"caNumber4",caNumber4); + tolua_constant(tolua_S,"caNumber5",caNumber5); + tolua_constant(tolua_S,"caNumber6",caNumber6); + tolua_constant(tolua_S,"caNumber7",caNumber7); + tolua_constant(tolua_S,"caNumber8",caNumber8); + tolua_constant(tolua_S,"caNumber9",caNumber9); + tolua_constant(tolua_S,"caMiddleClick",caMiddleClick); + tolua_constant(tolua_S,"caDropKey",caDropKey); + tolua_constant(tolua_S,"caCtrlDropKey",caCtrlDropKey); + tolua_constant(tolua_S,"caLeftClickOutside",caLeftClickOutside); + tolua_constant(tolua_S,"caRightClickOutside",caRightClickOutside); + tolua_constant(tolua_S,"caLeftClickOutsideHoldNothing",caLeftClickOutsideHoldNothing); + tolua_constant(tolua_S,"caRightClickOutsideHoldNothing",caRightClickOutsideHoldNothing); + tolua_constant(tolua_S,"caLeftPaintBegin",caLeftPaintBegin); + tolua_constant(tolua_S,"caRightPaintBegin",caRightPaintBegin); + tolua_constant(tolua_S,"caLeftPaintProgress",caLeftPaintProgress); + tolua_constant(tolua_S,"caRightPaintProgress",caRightPaintProgress); + tolua_constant(tolua_S,"caLeftPaintEnd",caLeftPaintEnd); + tolua_constant(tolua_S,"caRightPaintEnd",caRightPaintEnd); + tolua_constant(tolua_S,"caDblClick",caDblClick); + tolua_constant(tolua_S,"caUnknown",caUnknown); + tolua_constant(tolua_S,"eGameMode_NotSet",eGameMode_NotSet); + tolua_constant(tolua_S,"eGameMode_Survival",eGameMode_Survival); + tolua_constant(tolua_S,"eGameMode_Creative",eGameMode_Creative); + tolua_constant(tolua_S,"eGameMode_Adventure",eGameMode_Adventure); + tolua_constant(tolua_S,"gmNotSet",gmNotSet); + tolua_constant(tolua_S,"gmSurvival",gmSurvival); + tolua_constant(tolua_S,"gmCreative",gmCreative); + tolua_constant(tolua_S,"gmAdventure",gmAdventure); + tolua_constant(tolua_S,"gmMax",gmMax); + tolua_constant(tolua_S,"gmMin",gmMin); + tolua_constant(tolua_S,"eWeather_Sunny",eWeather_Sunny); + tolua_constant(tolua_S,"eWeather_Rain",eWeather_Rain); + tolua_constant(tolua_S,"eWeather_ThunderStorm",eWeather_ThunderStorm); + tolua_constant(tolua_S,"wSunny",wSunny); + tolua_constant(tolua_S,"wRain",wRain); + tolua_constant(tolua_S,"wThunderstorm",wThunderstorm); + tolua_constant(tolua_S,"wStorm",wStorm); + tolua_function(tolua_S,"ClickActionToString",tolua_AllToLua_ClickActionToString00); + tolua_function(tolua_S,"IsValidBlock",tolua_AllToLua_IsValidBlock00); + tolua_function(tolua_S,"IsValidItem",tolua_AllToLua_IsValidItem00); + tolua_function(tolua_S,"AddFaceDirection",tolua_AllToLua_AddFaceDirection00); + tolua_module(tolua_S,"ItemCategory",0); + tolua_beginmodule(tolua_S,"ItemCategory"); + tolua_function(tolua_S,"IsPickaxe",tolua_AllToLua_ItemCategory_IsPickaxe00); + tolua_function(tolua_S,"IsAxe",tolua_AllToLua_ItemCategory_IsAxe00); + tolua_function(tolua_S,"IsSword",tolua_AllToLua_ItemCategory_IsSword00); + tolua_function(tolua_S,"IsHoe",tolua_AllToLua_ItemCategory_IsHoe00); + tolua_function(tolua_S,"IsShovel",tolua_AllToLua_ItemCategory_IsShovel00); + tolua_function(tolua_S,"IsTool",tolua_AllToLua_ItemCategory_IsTool00); + tolua_function(tolua_S,"IsHelmet",tolua_AllToLua_ItemCategory_IsHelmet00); + tolua_function(tolua_S,"IsChestPlate",tolua_AllToLua_ItemCategory_IsChestPlate00); + tolua_function(tolua_S,"IsLeggings",tolua_AllToLua_ItemCategory_IsLeggings00); + tolua_function(tolua_S,"IsBoots",tolua_AllToLua_ItemCategory_IsBoots00); + tolua_function(tolua_S,"IsArmor",tolua_AllToLua_ItemCategory_IsArmor00); + tolua_endmodule(tolua_S); + tolua_function(tolua_S,"GetTime",tolua_AllToLua_GetTime00); + tolua_function(tolua_S,"GetChar",tolua_AllToLua_GetChar00); + tolua_cclass(tolua_S,"cChatColor","cChatColor","",NULL); + tolua_beginmodule(tolua_S,"cChatColor"); + tolua_variable(tolua_S,"Color",tolua_get_cChatColor_Color,NULL); + tolua_variable(tolua_S,"Delimiter",tolua_get_cChatColor_Delimiter,NULL); + tolua_variable(tolua_S,"Black",tolua_get_cChatColor_Black,NULL); + tolua_variable(tolua_S,"Navy",tolua_get_cChatColor_Navy,NULL); + tolua_variable(tolua_S,"Green",tolua_get_cChatColor_Green,NULL); + tolua_variable(tolua_S,"Blue",tolua_get_cChatColor_Blue,NULL); + tolua_variable(tolua_S,"Red",tolua_get_cChatColor_Red,NULL); + tolua_variable(tolua_S,"Purple",tolua_get_cChatColor_Purple,NULL); + tolua_variable(tolua_S,"Gold",tolua_get_cChatColor_Gold,NULL); + tolua_variable(tolua_S,"LightGray",tolua_get_cChatColor_LightGray,NULL); + tolua_variable(tolua_S,"Gray",tolua_get_cChatColor_Gray,NULL); + tolua_variable(tolua_S,"DarkPurple",tolua_get_cChatColor_DarkPurple,NULL); + tolua_variable(tolua_S,"LightGreen",tolua_get_cChatColor_LightGreen,NULL); + tolua_variable(tolua_S,"LightBlue",tolua_get_cChatColor_LightBlue,NULL); + tolua_variable(tolua_S,"Rose",tolua_get_cChatColor_Rose,NULL); + tolua_variable(tolua_S,"LightPurple",tolua_get_cChatColor_LightPurple,NULL); + tolua_variable(tolua_S,"Yellow",tolua_get_cChatColor_Yellow,NULL); + tolua_variable(tolua_S,"White",tolua_get_cChatColor_White,NULL); + tolua_variable(tolua_S,"Random",tolua_get_cChatColor_Random,NULL); + tolua_variable(tolua_S,"Bold",tolua_get_cChatColor_Bold,NULL); + tolua_variable(tolua_S,"Strikethrough",tolua_get_cChatColor_Strikethrough,NULL); + tolua_variable(tolua_S,"Underlined",tolua_get_cChatColor_Underlined,NULL); + tolua_variable(tolua_S,"Italic",tolua_get_cChatColor_Italic,NULL); + tolua_variable(tolua_S,"Plain",tolua_get_cChatColor_Plain,NULL); + tolua_function(tolua_S,"MakeColor",tolua_AllToLua_cChatColor_MakeColor00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cClientHandle","cClientHandle","",NULL); + tolua_beginmodule(tolua_S,"cClientHandle"); + tolua_function(tolua_S,"GetPlayer",tolua_AllToLua_cClientHandle_GetPlayer00); + tolua_function(tolua_S,"Kick",tolua_AllToLua_cClientHandle_Kick00); + tolua_function(tolua_S,"SendBlockChange",tolua_AllToLua_cClientHandle_SendBlockChange00); + tolua_function(tolua_S,"GetUsername",tolua_AllToLua_cClientHandle_GetUsername00); + tolua_function(tolua_S,"SetUsername",tolua_AllToLua_cClientHandle_SetUsername00); + tolua_function(tolua_S,"GetPing",tolua_AllToLua_cClientHandle_GetPing00); + tolua_function(tolua_S,"SetViewDistance",tolua_AllToLua_cClientHandle_SetViewDistance00); + tolua_function(tolua_S,"GetViewDistance",tolua_AllToLua_cClientHandle_GetViewDistance00); + tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cClientHandle_GetUniqueID00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"TakeDamageInfo","TakeDamageInfo","",NULL); + tolua_beginmodule(tolua_S,"TakeDamageInfo"); + tolua_variable(tolua_S,"DamageType",tolua_get_TakeDamageInfo_DamageType,tolua_set_TakeDamageInfo_DamageType); + tolua_variable(tolua_S,"Attacker",tolua_get_TakeDamageInfo_Attacker_ptr,tolua_set_TakeDamageInfo_Attacker_ptr); + tolua_variable(tolua_S,"RawDamage",tolua_get_TakeDamageInfo_RawDamage,tolua_set_TakeDamageInfo_RawDamage); + tolua_variable(tolua_S,"FinalDamage",tolua_get_TakeDamageInfo_FinalDamage,tolua_set_TakeDamageInfo_FinalDamage); + tolua_variable(tolua_S,"Knockback",tolua_get_TakeDamageInfo_Knockback,tolua_set_TakeDamageInfo_Knockback); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cEntity","cEntity","",NULL); + tolua_beginmodule(tolua_S,"cEntity"); + tolua_constant(tolua_S,"etEntity",cEntity::etEntity); + tolua_constant(tolua_S,"etPlayer",cEntity::etPlayer); + tolua_constant(tolua_S,"etPickup",cEntity::etPickup); + tolua_constant(tolua_S,"etMonster",cEntity::etMonster); + tolua_constant(tolua_S,"etFallingBlock",cEntity::etFallingBlock); + tolua_constant(tolua_S,"etMinecart",cEntity::etMinecart); + tolua_constant(tolua_S,"etBoat",cEntity::etBoat); + tolua_constant(tolua_S,"etTNT",cEntity::etTNT); + tolua_constant(tolua_S,"etProjectile",cEntity::etProjectile); + tolua_constant(tolua_S,"etExpOrb",cEntity::etExpOrb); + tolua_constant(tolua_S,"etMob",cEntity::etMob); + tolua_function(tolua_S,"GetEntityType",tolua_AllToLua_cEntity_GetEntityType00); + tolua_function(tolua_S,"IsPlayer",tolua_AllToLua_cEntity_IsPlayer00); + tolua_function(tolua_S,"IsPickup",tolua_AllToLua_cEntity_IsPickup00); + tolua_function(tolua_S,"IsMob",tolua_AllToLua_cEntity_IsMob00); + tolua_function(tolua_S,"IsFallingBlock",tolua_AllToLua_cEntity_IsFallingBlock00); + tolua_function(tolua_S,"IsMinecart",tolua_AllToLua_cEntity_IsMinecart00); + tolua_function(tolua_S,"IsBoat",tolua_AllToLua_cEntity_IsBoat00); + tolua_function(tolua_S,"IsTNT",tolua_AllToLua_cEntity_IsTNT00); + tolua_function(tolua_S,"IsProjectile",tolua_AllToLua_cEntity_IsProjectile00); + tolua_function(tolua_S,"IsA",tolua_AllToLua_cEntity_IsA00); + tolua_function(tolua_S,"GetClass",tolua_AllToLua_cEntity_GetClass00); + tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cEntity_GetClassStatic00); + tolua_function(tolua_S,"GetParentClass",tolua_AllToLua_cEntity_GetParentClass00); + tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cEntity_GetWorld00); + tolua_function(tolua_S,"GetHeadYaw",tolua_AllToLua_cEntity_GetHeadYaw00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cEntity_GetHeight00); + tolua_function(tolua_S,"GetMass",tolua_AllToLua_cEntity_GetMass00); + tolua_function(tolua_S,"GetPosition",tolua_AllToLua_cEntity_GetPosition00); + tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cEntity_GetPosX00); + tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cEntity_GetPosY00); + tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cEntity_GetPosZ00); + tolua_function(tolua_S,"GetRot",tolua_AllToLua_cEntity_GetRot00); + tolua_function(tolua_S,"GetRotation",tolua_AllToLua_cEntity_GetRotation00); + tolua_function(tolua_S,"GetYaw",tolua_AllToLua_cEntity_GetYaw00); + tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cEntity_GetPitch00); + tolua_function(tolua_S,"GetRoll",tolua_AllToLua_cEntity_GetRoll00); + tolua_function(tolua_S,"GetLookVector",tolua_AllToLua_cEntity_GetLookVector00); + tolua_function(tolua_S,"GetSpeed",tolua_AllToLua_cEntity_GetSpeed00); + tolua_function(tolua_S,"GetSpeedX",tolua_AllToLua_cEntity_GetSpeedX00); + tolua_function(tolua_S,"GetSpeedY",tolua_AllToLua_cEntity_GetSpeedY00); + tolua_function(tolua_S,"GetSpeedZ",tolua_AllToLua_cEntity_GetSpeedZ00); + tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cEntity_GetWidth00); + tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cEntity_GetChunkX00); + tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cEntity_GetChunkZ00); + tolua_function(tolua_S,"SetHeadYaw",tolua_AllToLua_cEntity_SetHeadYaw00); + tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cEntity_SetHeight00); + tolua_function(tolua_S,"SetMass",tolua_AllToLua_cEntity_SetMass00); + tolua_function(tolua_S,"SetPosX",tolua_AllToLua_cEntity_SetPosX00); + tolua_function(tolua_S,"SetPosY",tolua_AllToLua_cEntity_SetPosY00); + tolua_function(tolua_S,"SetPosZ",tolua_AllToLua_cEntity_SetPosZ00); + tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition00); + tolua_function(tolua_S,"SetPosition",tolua_AllToLua_cEntity_SetPosition01); + tolua_function(tolua_S,"SetRot",tolua_AllToLua_cEntity_SetRot00); + tolua_function(tolua_S,"SetRotation",tolua_AllToLua_cEntity_SetRotation00); + tolua_function(tolua_S,"SetYaw",tolua_AllToLua_cEntity_SetYaw00); + tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cEntity_SetPitch00); + tolua_function(tolua_S,"SetRoll",tolua_AllToLua_cEntity_SetRoll00); + tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed00); + tolua_function(tolua_S,"SetSpeed",tolua_AllToLua_cEntity_SetSpeed01); + tolua_function(tolua_S,"SetSpeedX",tolua_AllToLua_cEntity_SetSpeedX00); + tolua_function(tolua_S,"SetSpeedY",tolua_AllToLua_cEntity_SetSpeedY00); + tolua_function(tolua_S,"SetSpeedZ",tolua_AllToLua_cEntity_SetSpeedZ00); + tolua_function(tolua_S,"SetWidth",tolua_AllToLua_cEntity_SetWidth00); + tolua_function(tolua_S,"AddPosX",tolua_AllToLua_cEntity_AddPosX00); + tolua_function(tolua_S,"AddPosY",tolua_AllToLua_cEntity_AddPosY00); + tolua_function(tolua_S,"AddPosZ",tolua_AllToLua_cEntity_AddPosZ00); + tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition00); + tolua_function(tolua_S,"AddPosition",tolua_AllToLua_cEntity_AddPosition01); + tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed00); + tolua_function(tolua_S,"AddSpeed",tolua_AllToLua_cEntity_AddSpeed01); + tolua_function(tolua_S,"AddSpeedX",tolua_AllToLua_cEntity_AddSpeedX00); + tolua_function(tolua_S,"AddSpeedY",tolua_AllToLua_cEntity_AddSpeedY00); + tolua_function(tolua_S,"AddSpeedZ",tolua_AllToLua_cEntity_AddSpeedZ00); + tolua_function(tolua_S,"SteerVehicle",tolua_AllToLua_cEntity_SteerVehicle00); + tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cEntity_GetUniqueID00); + tolua_function(tolua_S,"IsDestroyed",tolua_AllToLua_cEntity_IsDestroyed00); + tolua_function(tolua_S,"Destroy",tolua_AllToLua_cEntity_Destroy00); + tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage00); + tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage01); + tolua_function(tolua_S,"TakeDamage",tolua_AllToLua_cEntity_TakeDamage02); + tolua_function(tolua_S,"GetGravity",tolua_AllToLua_cEntity_GetGravity00); + tolua_function(tolua_S,"SetGravity",tolua_AllToLua_cEntity_SetGravity00); + tolua_function(tolua_S,"SetRotationFromSpeed",tolua_AllToLua_cEntity_SetRotationFromSpeed00); + tolua_function(tolua_S,"SetPitchFromSpeed",tolua_AllToLua_cEntity_SetPitchFromSpeed00); + tolua_function(tolua_S,"GetRawDamageAgainst",tolua_AllToLua_cEntity_GetRawDamageAgainst00); + tolua_function(tolua_S,"GetArmorCoverAgainst",tolua_AllToLua_cEntity_GetArmorCoverAgainst00); + tolua_function(tolua_S,"GetKnockbackAmountAgainst",tolua_AllToLua_cEntity_GetKnockbackAmountAgainst00); + tolua_function(tolua_S,"GetEquippedWeapon",tolua_AllToLua_cEntity_GetEquippedWeapon00); + tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cEntity_GetEquippedHelmet00); + tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cEntity_GetEquippedChestplate00); + tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cEntity_GetEquippedLeggings00); + tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cEntity_GetEquippedBoots00); + tolua_function(tolua_S,"KilledBy",tolua_AllToLua_cEntity_KilledBy00); + tolua_function(tolua_S,"Heal",tolua_AllToLua_cEntity_Heal00); + tolua_function(tolua_S,"GetHealth",tolua_AllToLua_cEntity_GetHealth00); + tolua_function(tolua_S,"SetHealth",tolua_AllToLua_cEntity_SetHealth00); + tolua_function(tolua_S,"SetMaxHealth",tolua_AllToLua_cEntity_SetMaxHealth00); + tolua_function(tolua_S,"GetMaxHealth",tolua_AllToLua_cEntity_GetMaxHealth00); + tolua_function(tolua_S,"StartBurning",tolua_AllToLua_cEntity_StartBurning00); + tolua_function(tolua_S,"StopBurning",tolua_AllToLua_cEntity_StopBurning00); + tolua_function(tolua_S,"TeleportToEntity",tolua_AllToLua_cEntity_TeleportToEntity00); + tolua_function(tolua_S,"TeleportToCoords",tolua_AllToLua_cEntity_TeleportToCoords00); + tolua_function(tolua_S,"IsOnFire",tolua_AllToLua_cEntity_IsOnFire00); + tolua_function(tolua_S,"IsCrouched",tolua_AllToLua_cEntity_IsCrouched00); + tolua_function(tolua_S,"IsRiding",tolua_AllToLua_cEntity_IsRiding00); + tolua_function(tolua_S,"IsSprinting",tolua_AllToLua_cEntity_IsSprinting00); + tolua_function(tolua_S,"IsRclking",tolua_AllToLua_cEntity_IsRclking00); + tolua_function(tolua_S,"IsInvisible",tolua_AllToLua_cEntity_IsInvisible00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPawn","cPawn","cEntity",NULL); + tolua_beginmodule(tolua_S,"cPawn"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPlayer","cPlayer","cPawn",NULL); + tolua_beginmodule(tolua_S,"cPlayer"); + tolua_constant(tolua_S,"MAX_HEALTH",cPlayer::MAX_HEALTH); + tolua_constant(tolua_S,"MAX_FOOD_LEVEL",cPlayer::MAX_FOOD_LEVEL); + tolua_constant(tolua_S,"EATING_TICKS",cPlayer::EATING_TICKS); + tolua_constant(tolua_S,"MAX_AIR_LEVEL",cPlayer::MAX_AIR_LEVEL); + tolua_constant(tolua_S,"DROWNING_TICKS",cPlayer::DROWNING_TICKS); + tolua_function(tolua_S,"SetCurrentExperience",tolua_AllToLua_cPlayer_SetCurrentExperience00); + tolua_function(tolua_S,"DeltaExperience",tolua_AllToLua_cPlayer_DeltaExperience00); + tolua_function(tolua_S,"GetXpLifetimeTotal",tolua_AllToLua_cPlayer_GetXpLifetimeTotal00); + tolua_function(tolua_S,"GetCurrentXp",tolua_AllToLua_cPlayer_GetCurrentXp00); + tolua_function(tolua_S,"GetXpLevel",tolua_AllToLua_cPlayer_GetXpLevel00); + tolua_function(tolua_S,"GetXpPercentage",tolua_AllToLua_cPlayer_GetXpPercentage00); + tolua_function(tolua_S,"XpForLevel",tolua_AllToLua_cPlayer_XpForLevel00); + tolua_function(tolua_S,"CalcLevelFromXp",tolua_AllToLua_cPlayer_CalcLevelFromXp00); + tolua_function(tolua_S,"GetEyeHeight",tolua_AllToLua_cPlayer_GetEyeHeight00); + tolua_function(tolua_S,"GetEyePosition",tolua_AllToLua_cPlayer_GetEyePosition00); + tolua_function(tolua_S,"IsOnGround",tolua_AllToLua_cPlayer_IsOnGround00); + tolua_function(tolua_S,"GetStance",tolua_AllToLua_cPlayer_GetStance00); + tolua_function(tolua_S,"GetInventory",tolua_AllToLua_cPlayer_GetInventory00); + tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cPlayer_GetEquippedItem00); + tolua_function(tolua_S,"GetThrowStartPos",tolua_AllToLua_cPlayer_GetThrowStartPos00); + tolua_function(tolua_S,"GetThrowSpeed",tolua_AllToLua_cPlayer_GetThrowSpeed00); + tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cPlayer_GetGameMode00); + tolua_function(tolua_S,"GetEffectiveGameMode",tolua_AllToLua_cPlayer_GetEffectiveGameMode00); + tolua_function(tolua_S,"SetGameMode",tolua_AllToLua_cPlayer_SetGameMode00); + tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cPlayer_IsGameModeCreative00); + tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cPlayer_IsGameModeSurvival00); + tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cPlayer_IsGameModeAdventure00); + tolua_function(tolua_S,"GetIP",tolua_AllToLua_cPlayer_GetIP00); + tolua_function(tolua_S,"MoveTo",tolua_AllToLua_cPlayer_MoveTo00); + tolua_function(tolua_S,"GetWindow",tolua_AllToLua_cPlayer_GetWindow00); + tolua_function(tolua_S,"CloseWindow",tolua_AllToLua_cPlayer_CloseWindow00); + tolua_function(tolua_S,"CloseWindowIfID",tolua_AllToLua_cPlayer_CloseWindowIfID00); + tolua_function(tolua_S,"GetClientHandle",tolua_AllToLua_cPlayer_GetClientHandle00); + tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cPlayer_SendMessage00); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlayer_GetName00); + tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlayer_SetName00); + tolua_function(tolua_S,"AddToGroup",tolua_AllToLua_cPlayer_AddToGroup00); + tolua_function(tolua_S,"RemoveFromGroup",tolua_AllToLua_cPlayer_RemoveFromGroup00); + tolua_function(tolua_S,"CanUseCommand",tolua_AllToLua_cPlayer_CanUseCommand00); + tolua_function(tolua_S,"HasPermission",tolua_AllToLua_cPlayer_HasPermission00); + tolua_function(tolua_S,"IsInGroup",tolua_AllToLua_cPlayer_IsInGroup00); + tolua_function(tolua_S,"GetColor",tolua_AllToLua_cPlayer_GetColor00); + tolua_function(tolua_S,"TossItem",tolua_AllToLua_cPlayer_TossItem00); + tolua_function(tolua_S,"Heal",tolua_AllToLua_cPlayer_Heal00); + tolua_function(tolua_S,"GetFoodLevel",tolua_AllToLua_cPlayer_GetFoodLevel00); + tolua_function(tolua_S,"GetFoodSaturationLevel",tolua_AllToLua_cPlayer_GetFoodSaturationLevel00); + tolua_function(tolua_S,"GetFoodTickTimer",tolua_AllToLua_cPlayer_GetFoodTickTimer00); + tolua_function(tolua_S,"GetFoodExhaustionLevel",tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00); + tolua_function(tolua_S,"GetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00); + tolua_function(tolua_S,"GetAirLevel",tolua_AllToLua_cPlayer_GetAirLevel00); + tolua_function(tolua_S,"IsSatiated",tolua_AllToLua_cPlayer_IsSatiated00); + tolua_function(tolua_S,"SetFoodLevel",tolua_AllToLua_cPlayer_SetFoodLevel00); + tolua_function(tolua_S,"SetFoodSaturationLevel",tolua_AllToLua_cPlayer_SetFoodSaturationLevel00); + tolua_function(tolua_S,"SetFoodTickTimer",tolua_AllToLua_cPlayer_SetFoodTickTimer00); + tolua_function(tolua_S,"SetFoodExhaustionLevel",tolua_AllToLua_cPlayer_SetFoodExhaustionLevel00); + tolua_function(tolua_S,"SetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_SetFoodPoisonedTicksRemaining00); + tolua_function(tolua_S,"Feed",tolua_AllToLua_cPlayer_Feed00); + tolua_function(tolua_S,"AddFoodExhaustion",tolua_AllToLua_cPlayer_AddFoodExhaustion00); + tolua_function(tolua_S,"FoodPoison",tolua_AllToLua_cPlayer_FoodPoison00); + tolua_function(tolua_S,"IsEating",tolua_AllToLua_cPlayer_IsEating00); + tolua_function(tolua_S,"Respawn",tolua_AllToLua_cPlayer_Respawn00); + tolua_function(tolua_S,"SetVisible",tolua_AllToLua_cPlayer_SetVisible00); + tolua_function(tolua_S,"IsVisible",tolua_AllToLua_cPlayer_IsVisible00); + tolua_function(tolua_S,"MoveToWorld",tolua_AllToLua_cPlayer_MoveToWorld00); + tolua_function(tolua_S,"LoadPermissionsFromDisk",tolua_AllToLua_cPlayer_LoadPermissionsFromDisk00); + tolua_function(tolua_S,"GetMaxSpeed",tolua_AllToLua_cPlayer_GetMaxSpeed00); + tolua_function(tolua_S,"GetNormalMaxSpeed",tolua_AllToLua_cPlayer_GetNormalMaxSpeed00); + tolua_function(tolua_S,"GetSprintingMaxSpeed",tolua_AllToLua_cPlayer_GetSprintingMaxSpeed00); + tolua_function(tolua_S,"SetNormalMaxSpeed",tolua_AllToLua_cPlayer_SetNormalMaxSpeed00); + tolua_function(tolua_S,"SetSprintingMaxSpeed",tolua_AllToLua_cPlayer_SetSprintingMaxSpeed00); + tolua_function(tolua_S,"SetCrouch",tolua_AllToLua_cPlayer_SetCrouch00); + tolua_function(tolua_S,"SetSprint",tolua_AllToLua_cPlayer_SetSprint00); + tolua_function(tolua_S,"IsSwimming",tolua_AllToLua_cPlayer_IsSwimming00); + tolua_function(tolua_S,"IsSubmerged",tolua_AllToLua_cPlayer_IsSubmerged00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",tolua_collect_cPickup); + #else + tolua_cclass(tolua_S,"cPickup","cPickup","cEntity",NULL); + #endif + tolua_beginmodule(tolua_S,"cPickup"); + tolua_function(tolua_S,"new",tolua_AllToLua_cPickup_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cPickup_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cPickup_new00_local); + tolua_function(tolua_S,"GetItem",tolua_AllToLua_cPickup_GetItem00); + tolua_function(tolua_S,"CollectedBy",tolua_AllToLua_cPickup_CollectedBy00); + tolua_function(tolua_S,"GetAge",tolua_AllToLua_cPickup_GetAge00); + tolua_function(tolua_S,"IsCollected",tolua_AllToLua_cPickup_IsCollected00); + tolua_function(tolua_S,"IsPlayerCreated",tolua_AllToLua_cPickup_IsPlayerCreated00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cProjectileEntity","cProjectileEntity","cEntity",NULL); + tolua_beginmodule(tolua_S,"cProjectileEntity"); + tolua_constant(tolua_S,"pkArrow",cProjectileEntity::pkArrow); + tolua_constant(tolua_S,"pkSnowball",cProjectileEntity::pkSnowball); + tolua_constant(tolua_S,"pkEgg",cProjectileEntity::pkEgg); + tolua_constant(tolua_S,"pkGhastFireball",cProjectileEntity::pkGhastFireball); + tolua_constant(tolua_S,"pkFireCharge",cProjectileEntity::pkFireCharge); + tolua_constant(tolua_S,"pkEnderPearl",cProjectileEntity::pkEnderPearl); + tolua_constant(tolua_S,"pkExpBottle",cProjectileEntity::pkExpBottle); + tolua_constant(tolua_S,"pkSplashPotion",cProjectileEntity::pkSplashPotion); + tolua_constant(tolua_S,"pkFirework",cProjectileEntity::pkFirework); + tolua_constant(tolua_S,"pkWitherSkull",cProjectileEntity::pkWitherSkull); + tolua_constant(tolua_S,"pkFishingFloat",cProjectileEntity::pkFishingFloat); + tolua_function(tolua_S,"GetProjectileKind",tolua_AllToLua_cProjectileEntity_GetProjectileKind00); + tolua_function(tolua_S,"GetCreator",tolua_AllToLua_cProjectileEntity_GetCreator00); + tolua_function(tolua_S,"GetMCAClassName",tolua_AllToLua_cProjectileEntity_GetMCAClassName00); + tolua_function(tolua_S,"IsInGround",tolua_AllToLua_cProjectileEntity_IsInGround00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cArrowEntity","cArrowEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cArrowEntity"); + tolua_constant(tolua_S,"psNoPickup",cArrowEntity::psNoPickup); + tolua_constant(tolua_S,"psInSurvivalOrCreative",cArrowEntity::psInSurvivalOrCreative); + tolua_constant(tolua_S,"psInCreative",cArrowEntity::psInCreative); + tolua_function(tolua_S,"GetPickupState",tolua_AllToLua_cArrowEntity_GetPickupState00); + tolua_function(tolua_S,"SetPickupState",tolua_AllToLua_cArrowEntity_SetPickupState00); + tolua_function(tolua_S,"GetDamageCoeff",tolua_AllToLua_cArrowEntity_GetDamageCoeff00); + tolua_function(tolua_S,"SetDamageCoeff",tolua_AllToLua_cArrowEntity_SetDamageCoeff00); + tolua_function(tolua_S,"CanPickup",tolua_AllToLua_cArrowEntity_CanPickup00); + tolua_function(tolua_S,"IsCritical",tolua_AllToLua_cArrowEntity_IsCritical00); + tolua_function(tolua_S,"SetIsCritical",tolua_AllToLua_cArrowEntity_SetIsCritical00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cThrownEggEntity","cThrownEggEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cThrownEggEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cThrownEnderPearlEntity","cThrownEnderPearlEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cThrownEnderPearlEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cThrownSnowballEntity","cThrownSnowballEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cThrownSnowballEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cExpBottleEntity","cExpBottleEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cExpBottleEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFireworkEntity","cFireworkEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cFireworkEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cGhastFireballEntity","cGhastFireballEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cGhastFireballEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFireChargeEntity","cFireChargeEntity","cProjectileEntity",NULL); + tolua_beginmodule(tolua_S,"cFireChargeEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPluginManager","cPluginManager","",NULL); + tolua_beginmodule(tolua_S,"cPluginManager"); + tolua_constant(tolua_S,"HOOK_BLOCK_TO_PICKUPS",cPluginManager::HOOK_BLOCK_TO_PICKUPS); + tolua_constant(tolua_S,"HOOK_CHAT",cPluginManager::HOOK_CHAT); + tolua_constant(tolua_S,"HOOK_CHUNK_AVAILABLE",cPluginManager::HOOK_CHUNK_AVAILABLE); + tolua_constant(tolua_S,"HOOK_CHUNK_GENERATED",cPluginManager::HOOK_CHUNK_GENERATED); + tolua_constant(tolua_S,"HOOK_CHUNK_GENERATING",cPluginManager::HOOK_CHUNK_GENERATING); + tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADED",cPluginManager::HOOK_CHUNK_UNLOADED); + tolua_constant(tolua_S,"HOOK_CHUNK_UNLOADING",cPluginManager::HOOK_CHUNK_UNLOADING); + tolua_constant(tolua_S,"HOOK_COLLECTING_PICKUP",cPluginManager::HOOK_COLLECTING_PICKUP); + tolua_constant(tolua_S,"HOOK_CRAFTING_NO_RECIPE",cPluginManager::HOOK_CRAFTING_NO_RECIPE); + tolua_constant(tolua_S,"HOOK_DISCONNECT",cPluginManager::HOOK_DISCONNECT); + tolua_constant(tolua_S,"HOOK_EXECUTE_COMMAND",cPluginManager::HOOK_EXECUTE_COMMAND); + tolua_constant(tolua_S,"HOOK_EXPLODED",cPluginManager::HOOK_EXPLODED); + tolua_constant(tolua_S,"HOOK_EXPLODING",cPluginManager::HOOK_EXPLODING); + tolua_constant(tolua_S,"HOOK_HANDSHAKE",cPluginManager::HOOK_HANDSHAKE); + tolua_constant(tolua_S,"HOOK_HOPPER_PULLING_ITEM",cPluginManager::HOOK_HOPPER_PULLING_ITEM); + tolua_constant(tolua_S,"HOOK_HOPPER_PUSHING_ITEM",cPluginManager::HOOK_HOPPER_PUSHING_ITEM); + tolua_constant(tolua_S,"HOOK_KILLING",cPluginManager::HOOK_KILLING); + tolua_constant(tolua_S,"HOOK_LOGIN",cPluginManager::HOOK_LOGIN); + tolua_constant(tolua_S,"HOOK_PLAYER_ANIMATION",cPluginManager::HOOK_PLAYER_ANIMATION); + tolua_constant(tolua_S,"HOOK_PLAYER_BREAKING_BLOCK",cPluginManager::HOOK_PLAYER_BREAKING_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_BROKEN_BLOCK",cPluginManager::HOOK_PLAYER_BROKEN_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_EATING",cPluginManager::HOOK_PLAYER_EATING); + tolua_constant(tolua_S,"HOOK_PLAYER_JOINED",cPluginManager::HOOK_PLAYER_JOINED); + tolua_constant(tolua_S,"HOOK_PLAYER_LEFT_CLICK",cPluginManager::HOOK_PLAYER_LEFT_CLICK); + tolua_constant(tolua_S,"HOOK_PLAYER_MOVING",cPluginManager::HOOK_PLAYER_MOVING); + tolua_constant(tolua_S,"HOOK_PLAYER_PLACED_BLOCK",cPluginManager::HOOK_PLAYER_PLACED_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_PLACING_BLOCK",cPluginManager::HOOK_PLAYER_PLACING_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICK",cPluginManager::HOOK_PLAYER_RIGHT_CLICK); + tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICKING_ENTITY",cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY); + tolua_constant(tolua_S,"HOOK_PLAYER_SHOOTING",cPluginManager::HOOK_PLAYER_SHOOTING); + tolua_constant(tolua_S,"HOOK_PLAYER_SPAWNED",cPluginManager::HOOK_PLAYER_SPAWNED); + tolua_constant(tolua_S,"HOOK_PLAYER_TOSSING_ITEM",cPluginManager::HOOK_PLAYER_TOSSING_ITEM); + tolua_constant(tolua_S,"HOOK_PLAYER_USED_BLOCK",cPluginManager::HOOK_PLAYER_USED_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_USED_ITEM",cPluginManager::HOOK_PLAYER_USED_ITEM); + tolua_constant(tolua_S,"HOOK_PLAYER_USING_BLOCK",cPluginManager::HOOK_PLAYER_USING_BLOCK); + tolua_constant(tolua_S,"HOOK_PLAYER_USING_ITEM",cPluginManager::HOOK_PLAYER_USING_ITEM); + tolua_constant(tolua_S,"HOOK_POST_CRAFTING",cPluginManager::HOOK_POST_CRAFTING); + tolua_constant(tolua_S,"HOOK_PRE_CRAFTING",cPluginManager::HOOK_PRE_CRAFTING); + tolua_constant(tolua_S,"HOOK_SPAWNED_ENTITY",cPluginManager::HOOK_SPAWNED_ENTITY); + tolua_constant(tolua_S,"HOOK_SPAWNED_MONSTER",cPluginManager::HOOK_SPAWNED_MONSTER); + tolua_constant(tolua_S,"HOOK_SPAWNING_ENTITY",cPluginManager::HOOK_SPAWNING_ENTITY); + tolua_constant(tolua_S,"HOOK_SPAWNING_MONSTER",cPluginManager::HOOK_SPAWNING_MONSTER); + tolua_constant(tolua_S,"HOOK_TAKE_DAMAGE",cPluginManager::HOOK_TAKE_DAMAGE); + tolua_constant(tolua_S,"HOOK_TICK",cPluginManager::HOOK_TICK); + tolua_constant(tolua_S,"HOOK_UPDATED_SIGN",cPluginManager::HOOK_UPDATED_SIGN); + tolua_constant(tolua_S,"HOOK_UPDATING_SIGN",cPluginManager::HOOK_UPDATING_SIGN); + tolua_constant(tolua_S,"HOOK_WEATHER_CHANGED",cPluginManager::HOOK_WEATHER_CHANGED); + tolua_constant(tolua_S,"HOOK_WEATHER_CHANGING",cPluginManager::HOOK_WEATHER_CHANGING); + tolua_constant(tolua_S,"HOOK_WORLD_TICK",cPluginManager::HOOK_WORLD_TICK); + tolua_constant(tolua_S,"HOOK_NUM_HOOKS",cPluginManager::HOOK_NUM_HOOKS); + tolua_constant(tolua_S,"HOOK_MAX",cPluginManager::HOOK_MAX); + tolua_function(tolua_S,"Get",tolua_AllToLua_cPluginManager_Get00); + tolua_function(tolua_S,"GetPlugin",tolua_AllToLua_cPluginManager_GetPlugin00); + tolua_function(tolua_S,"FindPlugins",tolua_AllToLua_cPluginManager_FindPlugins00); + tolua_function(tolua_S,"ReloadPlugins",tolua_AllToLua_cPluginManager_ReloadPlugins00); + tolua_function(tolua_S,"GetNumPlugins",tolua_AllToLua_cPluginManager_GetNumPlugins00); + tolua_function(tolua_S,"DisablePlugin",tolua_AllToLua_cPluginManager_DisablePlugin00); + tolua_function(tolua_S,"LoadPlugin",tolua_AllToLua_cPluginManager_LoadPlugin00); + tolua_function(tolua_S,"IsCommandBound",tolua_AllToLua_cPluginManager_IsCommandBound00); + tolua_function(tolua_S,"GetCommandPermission",tolua_AllToLua_cPluginManager_GetCommandPermission00); + tolua_function(tolua_S,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00); + tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00); + tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL); + tolua_beginmodule(tolua_S,"cPlugin"); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cPlugin_GetName00); + tolua_function(tolua_S,"SetName",tolua_AllToLua_cPlugin_SetName00); + tolua_function(tolua_S,"GetVersion",tolua_AllToLua_cPlugin_GetVersion00); + 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"); + tolua_variable(tolua_S,"__cWebPlugin__",tolua_get_cPluginLua___cWebPlugin__,NULL); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cServer","cServer","",NULL); + tolua_beginmodule(tolua_S,"cServer"); + tolua_function(tolua_S,"GetDescription",tolua_AllToLua_cServer_GetDescription00); + tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cServer_GetMaxPlayers00); + tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cServer_GetNumPlayers00); + tolua_function(tolua_S,"SetMaxPlayers",tolua_AllToLua_cServer_SetMaxPlayers00); + tolua_function(tolua_S,"IsHardcore",tolua_AllToLua_cServer_IsHardcore00); + tolua_function(tolua_S,"GetServerID",tolua_AllToLua_cServer_GetServerID00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWorld","cWorld","",NULL); + tolua_beginmodule(tolua_S,"cWorld"); + tolua_function(tolua_S,"GetTicksUntilWeatherChange",tolua_AllToLua_cWorld_GetTicksUntilWeatherChange00); + tolua_function(tolua_S,"GetWorldAge",tolua_AllToLua_cWorld_GetWorldAge00); + tolua_function(tolua_S,"GetTimeOfDay",tolua_AllToLua_cWorld_GetTimeOfDay00); + tolua_function(tolua_S,"SetTicksUntilWeatherChange",tolua_AllToLua_cWorld_SetTicksUntilWeatherChange00); + tolua_function(tolua_S,"SetTimeOfDay",tolua_AllToLua_cWorld_SetTimeOfDay00); + tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00); + tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cWorld_IsGameModeCreative00); + tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cWorld_IsGameModeSurvival00); + tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cWorld_IsGameModeAdventure00); + tolua_function(tolua_S,"IsPVPEnabled",tolua_AllToLua_cWorld_IsPVPEnabled00); + tolua_function(tolua_S,"IsDeepSnowEnabled",tolua_AllToLua_cWorld_IsDeepSnowEnabled00); + tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cWorld_GetHeight00); + tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cWorld_BroadcastChat00); + tolua_function(tolua_S,"BroadcastSoundEffect",tolua_AllToLua_cWorld_BroadcastSoundEffect00); + tolua_function(tolua_S,"BroadcastSoundParticleEffect",tolua_AllToLua_cWorld_BroadcastSoundParticleEffect00); + tolua_function(tolua_S,"UnloadUnusedChunks",tolua_AllToLua_cWorld_UnloadUnusedChunks00); + tolua_function(tolua_S,"RegenerateChunk",tolua_AllToLua_cWorld_RegenerateChunk00); + tolua_function(tolua_S,"GenerateChunk",tolua_AllToLua_cWorld_GenerateChunk00); + tolua_function(tolua_S,"SetBlock",tolua_AllToLua_cWorld_SetBlock00); + tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock00); + tolua_function(tolua_S,"QueueSetBlock",tolua_AllToLua_cWorld_QueueSetBlock00); + tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock00); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta00); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta00); + tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cWorld_GetBlockSkyLight00); + tolua_function(tolua_S,"GetBlockBlockLight",tolua_AllToLua_cWorld_GetBlockBlockLight00); + tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock01); + tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock01); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cWorld_GetBlockMeta01); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cWorld_SetBlockMeta01); + tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups00); + tolua_function(tolua_S,"SpawnItemPickups",tolua_AllToLua_cWorld_SpawnItemPickups01); + tolua_function(tolua_S,"SpawnExperienceOrb",tolua_AllToLua_cWorld_SpawnExperienceOrb00); + tolua_function(tolua_S,"SpawnPrimedTNT",tolua_AllToLua_cWorld_SpawnPrimedTNT00); + tolua_function(tolua_S,"DigBlock",tolua_AllToLua_cWorld_DigBlock00); + tolua_function(tolua_S,"SendBlockTo",tolua_AllToLua_cWorld_SendBlockTo00); + tolua_function(tolua_S,"GetSpawnX",tolua_AllToLua_cWorld_GetSpawnX00); + tolua_function(tolua_S,"GetSpawnY",tolua_AllToLua_cWorld_GetSpawnY00); + tolua_function(tolua_S,"GetSpawnZ",tolua_AllToLua_cWorld_GetSpawnZ00); + tolua_function(tolua_S,"WakeUpSimulators",tolua_AllToLua_cWorld_WakeUpSimulators00); + tolua_function(tolua_S,"WakeUpSimulatorsInArea",tolua_AllToLua_cWorld_WakeUpSimulatorsInArea00); + tolua_function(tolua_S,"DoExplosionAt",tolua_AllToLua_cWorld_DoExplosionAt00); + tolua_function(tolua_S,"UseBlockEntity",tolua_AllToLua_cWorld_UseBlockEntity00); + tolua_function(tolua_S,"GrowTree",tolua_AllToLua_cWorld_GrowTree00); + tolua_function(tolua_S,"GrowTreeFromSapling",tolua_AllToLua_cWorld_GrowTreeFromSapling00); + tolua_function(tolua_S,"GrowTreeByBiome",tolua_AllToLua_cWorld_GrowTreeByBiome00); + tolua_function(tolua_S,"GrowRipePlant",tolua_AllToLua_cWorld_GrowRipePlant00); + tolua_function(tolua_S,"GrowCactus",tolua_AllToLua_cWorld_GrowCactus00); + tolua_function(tolua_S,"GrowMelonPumpkin",tolua_AllToLua_cWorld_GrowMelonPumpkin00); + tolua_function(tolua_S,"GrowSugarcane",tolua_AllToLua_cWorld_GrowSugarcane00); + tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00); + tolua_function(tolua_S,"GetIniFileName",tolua_AllToLua_cWorld_GetIniFileName00); + tolua_function(tolua_S,"QueueSaveAllChunks",tolua_AllToLua_cWorld_QueueSaveAllChunks00); + tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00); + tolua_function(tolua_S,"GetGeneratorQueueLength",tolua_AllToLua_cWorld_GetGeneratorQueueLength00); + tolua_function(tolua_S,"GetLightingQueueLength",tolua_AllToLua_cWorld_GetLightingQueueLength00); + tolua_function(tolua_S,"GetStorageLoadQueueLength",tolua_AllToLua_cWorld_GetStorageLoadQueueLength00); + tolua_function(tolua_S,"GetStorageSaveQueueLength",tolua_AllToLua_cWorld_GetStorageSaveQueueLength00); + tolua_function(tolua_S,"QueueBlockForTick",tolua_AllToLua_cWorld_QueueBlockForTick00); + tolua_function(tolua_S,"CastThunderbolt",tolua_AllToLua_cWorld_CastThunderbolt00); + 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); + tolua_function(tolua_S,"IsBlockDirectlyWatered",tolua_AllToLua_cWorld_IsBlockDirectlyWatered00); + tolua_function(tolua_S,"SpawnMob",tolua_AllToLua_cWorld_SpawnMob00); + tolua_function(tolua_S,"CreateProjectile",tolua_AllToLua_cWorld_CreateProjectile00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cInventory","cInventory","cItemGrid::cListener",NULL); + tolua_beginmodule(tolua_S,"cInventory"); + tolua_constant(tolua_S,"invArmorCount",cInventory::invArmorCount); + tolua_constant(tolua_S,"invInventoryCount",cInventory::invInventoryCount); + tolua_constant(tolua_S,"invHotbarCount",cInventory::invHotbarCount); + tolua_constant(tolua_S,"invArmorOffset",cInventory::invArmorOffset); + tolua_constant(tolua_S,"invInventoryOffset",cInventory::invInventoryOffset); + tolua_constant(tolua_S,"invHotbarOffset",cInventory::invHotbarOffset); + tolua_constant(tolua_S,"invNumSlots",cInventory::invNumSlots); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cInventory_Clear00); + tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit00); + tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cInventory_HowManyCanFit01); + tolua_function(tolua_S,"AddItem",tolua_AllToLua_cInventory_AddItem00); + tolua_function(tolua_S,"AddItems",tolua_AllToLua_cInventory_AddItems00); + tolua_function(tolua_S,"RemoveOneEquippedItem",tolua_AllToLua_cInventory_RemoveOneEquippedItem00); + tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cInventory_HowManyItems00); + tolua_function(tolua_S,"HasItems",tolua_AllToLua_cInventory_HasItems00); + tolua_function(tolua_S,"GetArmorGrid",tolua_AllToLua_cInventory_GetArmorGrid00); + tolua_function(tolua_S,"GetInventoryGrid",tolua_AllToLua_cInventory_GetInventoryGrid00); + tolua_function(tolua_S,"GetHotbarGrid",tolua_AllToLua_cInventory_GetHotbarGrid00); + tolua_function(tolua_S,"GetOwner",tolua_AllToLua_cInventory_GetOwner00); + tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cInventory_CopyToItems00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cInventory_GetSlot00); + tolua_function(tolua_S,"GetArmorSlot",tolua_AllToLua_cInventory_GetArmorSlot00); + tolua_function(tolua_S,"GetInventorySlot",tolua_AllToLua_cInventory_GetInventorySlot00); + tolua_function(tolua_S,"GetHotbarSlot",tolua_AllToLua_cInventory_GetHotbarSlot00); + tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cInventory_GetEquippedItem00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cInventory_SetSlot00); + tolua_function(tolua_S,"SetArmorSlot",tolua_AllToLua_cInventory_SetArmorSlot00); + tolua_function(tolua_S,"SetInventorySlot",tolua_AllToLua_cInventory_SetInventorySlot00); + tolua_function(tolua_S,"SetHotbarSlot",tolua_AllToLua_cInventory_SetHotbarSlot00); + tolua_function(tolua_S,"SetEquippedSlotNum",tolua_AllToLua_cInventory_SetEquippedSlotNum00); + tolua_function(tolua_S,"GetEquippedSlotNum",tolua_AllToLua_cInventory_GetEquippedSlotNum00); + tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cInventory_ChangeSlotCount00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cInventory_DamageItem00); + tolua_function(tolua_S,"DamageEquippedItem",tolua_AllToLua_cInventory_DamageEquippedItem00); + tolua_function(tolua_S,"GetEquippedHelmet",tolua_AllToLua_cInventory_GetEquippedHelmet00); + tolua_function(tolua_S,"GetEquippedChestplate",tolua_AllToLua_cInventory_GetEquippedChestplate00); + tolua_function(tolua_S,"GetEquippedLeggings",tolua_AllToLua_cInventory_GetEquippedLeggings00); + tolua_function(tolua_S,"GetEquippedBoots",tolua_AllToLua_cInventory_GetEquippedBoots00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",tolua_collect_cEnchantments); + #else + tolua_cclass(tolua_S,"cEnchantments","cEnchantments","",NULL); + #endif + tolua_beginmodule(tolua_S,"cEnchantments"); + tolua_constant(tolua_S,"enchProtection",cEnchantments::enchProtection); + tolua_constant(tolua_S,"enchFireProtection",cEnchantments::enchFireProtection); + tolua_constant(tolua_S,"enchFeatherFalling",cEnchantments::enchFeatherFalling); + tolua_constant(tolua_S,"enchBlastProtection",cEnchantments::enchBlastProtection); + tolua_constant(tolua_S,"enchProjectileProtection",cEnchantments::enchProjectileProtection); + tolua_constant(tolua_S,"enchRespiration",cEnchantments::enchRespiration); + tolua_constant(tolua_S,"enchAquaAffinity",cEnchantments::enchAquaAffinity); + tolua_constant(tolua_S,"enchThorns",cEnchantments::enchThorns); + tolua_constant(tolua_S,"enchSharpness",cEnchantments::enchSharpness); + tolua_constant(tolua_S,"enchSmite",cEnchantments::enchSmite); + tolua_constant(tolua_S,"enchBaneOfArthropods",cEnchantments::enchBaneOfArthropods); + tolua_constant(tolua_S,"enchKnockback",cEnchantments::enchKnockback); + tolua_constant(tolua_S,"enchFireAspect",cEnchantments::enchFireAspect); + tolua_constant(tolua_S,"enchLooting",cEnchantments::enchLooting); + tolua_constant(tolua_S,"enchEfficiency",cEnchantments::enchEfficiency); + tolua_constant(tolua_S,"enchSilkTouch",cEnchantments::enchSilkTouch); + tolua_constant(tolua_S,"enchUnbreaking",cEnchantments::enchUnbreaking); + tolua_constant(tolua_S,"enchFortune",cEnchantments::enchFortune); + tolua_constant(tolua_S,"enchPower",cEnchantments::enchPower); + tolua_constant(tolua_S,"enchPunch",cEnchantments::enchPunch); + tolua_constant(tolua_S,"enchFlame",cEnchantments::enchFlame); + tolua_constant(tolua_S,"enchInfinity",cEnchantments::enchInfinity); + tolua_constant(tolua_S,"enchLuckOfTheSea",cEnchantments::enchLuckOfTheSea); + tolua_constant(tolua_S,"enchLure",cEnchantments::enchLure); + tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cEnchantments_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cEnchantments_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cEnchantments_new01_local); + tolua_function(tolua_S,"AddFromString",tolua_AllToLua_cEnchantments_AddFromString00); + tolua_function(tolua_S,"ToString",tolua_AllToLua_cEnchantments_ToString00); + tolua_function(tolua_S,"GetLevel",tolua_AllToLua_cEnchantments_GetLevel00); + tolua_function(tolua_S,"SetLevel",tolua_AllToLua_cEnchantments_SetLevel00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cEnchantments_Clear00); + tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cEnchantments_IsEmpty00); + tolua_function(tolua_S,"StringToEnchantmentID",tolua_AllToLua_cEnchantments_StringToEnchantmentID00); + tolua_function(tolua_S,".eq",tolua_AllToLua_cEnchantments__eq00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cItem","cItem","",tolua_collect_cItem); + #else + tolua_cclass(tolua_S,"cItem","cItem","",NULL); + #endif + tolua_beginmodule(tolua_S,"cItem"); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cItem_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItem_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItem_new03_local); + tolua_function(tolua_S,"Empty",tolua_AllToLua_cItem_Empty00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cItem_Clear00); + tolua_function(tolua_S,"IsEmpty",tolua_AllToLua_cItem_IsEmpty00); + tolua_function(tolua_S,"IsEqual",tolua_AllToLua_cItem_IsEqual00); + tolua_function(tolua_S,"IsSameType",tolua_AllToLua_cItem_IsSameType00); + tolua_function(tolua_S,"CopyOne",tolua_AllToLua_cItem_CopyOne00); + tolua_function(tolua_S,"AddCount",tolua_AllToLua_cItem_AddCount00); + tolua_function(tolua_S,"GetMaxDamage",tolua_AllToLua_cItem_GetMaxDamage00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItem_DamageItem00); + tolua_function(tolua_S,"IsDamageable",tolua_AllToLua_cItem_IsDamageable00); + tolua_function(tolua_S,"IsStackableWith",tolua_AllToLua_cItem_IsStackableWith00); + tolua_function(tolua_S,"IsFullStack",tolua_AllToLua_cItem_IsFullStack00); + tolua_function(tolua_S,"GetMaxStackSize",tolua_AllToLua_cItem_GetMaxStackSize00); + tolua_variable(tolua_S,"m_ItemType",tolua_get_cItem_m_ItemType,tolua_set_cItem_m_ItemType); + tolua_variable(tolua_S,"m_ItemCount",tolua_get_cItem_m_ItemCount,tolua_set_cItem_m_ItemCount); + tolua_variable(tolua_S,"m_ItemDamage",tolua_get_cItem_m_ItemDamage,tolua_set_cItem_m_ItemDamage); + tolua_variable(tolua_S,"m_Enchantments",tolua_get_cItem_m_Enchantments,tolua_set_cItem_m_Enchantments); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cItems","cItems","",tolua_collect_cItems); + #else + tolua_cclass(tolua_S,"cItems","cItems","",NULL); + #endif + tolua_beginmodule(tolua_S,"cItems"); + tolua_function(tolua_S,"new",tolua_AllToLua_cItems_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cItems_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cItems_new00_local); + tolua_function(tolua_S,"Get",tolua_AllToLua_cItems_Get00); + tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set00); + tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add00); + tolua_function(tolua_S,"Delete",tolua_AllToLua_cItems_Delete00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cItems_Clear00); + tolua_function(tolua_S,"Size",tolua_AllToLua_cItems_Size00); + tolua_function(tolua_S,"Set",tolua_AllToLua_cItems_Set01); + tolua_function(tolua_S,"Add",tolua_AllToLua_cItems_Add01); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cItemGrid","cItemGrid","",NULL); + tolua_beginmodule(tolua_S,"cItemGrid"); + tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cItemGrid_GetWidth00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cItemGrid_GetHeight00); + tolua_function(tolua_S,"GetNumSlots",tolua_AllToLua_cItemGrid_GetNumSlots00); + tolua_function(tolua_S,"GetSlotNum",tolua_AllToLua_cItemGrid_GetSlotNum00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cItemGrid_GetSlot01); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot01); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot02); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cItemGrid_SetSlot03); + tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot00); + tolua_function(tolua_S,"EmptySlot",tolua_AllToLua_cItemGrid_EmptySlot01); + tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty00); + tolua_function(tolua_S,"IsSlotEmpty",tolua_AllToLua_cItemGrid_IsSlotEmpty01); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cItemGrid_Clear00); + tolua_function(tolua_S,"HowManyCanFit",tolua_AllToLua_cItemGrid_HowManyCanFit00); + tolua_function(tolua_S,"AddItem",tolua_AllToLua_cItemGrid_AddItem00); + tolua_function(tolua_S,"AddItems",tolua_AllToLua_cItemGrid_AddItems00); + tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount00); + tolua_function(tolua_S,"ChangeSlotCount",tolua_AllToLua_cItemGrid_ChangeSlotCount01); + tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem00); + tolua_function(tolua_S,"RemoveOneItem",tolua_AllToLua_cItemGrid_RemoveOneItem01); + tolua_function(tolua_S,"HowManyItems",tolua_AllToLua_cItemGrid_HowManyItems00); + tolua_function(tolua_S,"HasItems",tolua_AllToLua_cItemGrid_HasItems00); + tolua_function(tolua_S,"GetFirstEmptySlot",tolua_AllToLua_cItemGrid_GetFirstEmptySlot00); + tolua_function(tolua_S,"GetFirstUsedSlot",tolua_AllToLua_cItemGrid_GetFirstUsedSlot00); + tolua_function(tolua_S,"GetLastEmptySlot",tolua_AllToLua_cItemGrid_GetLastEmptySlot00); + tolua_function(tolua_S,"GetLastUsedSlot",tolua_AllToLua_cItemGrid_GetLastUsedSlot00); + tolua_function(tolua_S,"GetNextEmptySlot",tolua_AllToLua_cItemGrid_GetNextEmptySlot00); + tolua_function(tolua_S,"GetNextUsedSlot",tolua_AllToLua_cItemGrid_GetNextUsedSlot00); + tolua_function(tolua_S,"CopyToItems",tolua_AllToLua_cItemGrid_CopyToItems00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem00); + tolua_function(tolua_S,"DamageItem",tolua_AllToLua_cItemGrid_DamageItem01); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",tolua_collect_cBlockEntity); + #else + tolua_cclass(tolua_S,"cBlockEntity","cBlockEntity","",NULL); + #endif + tolua_beginmodule(tolua_S,"cBlockEntity"); + tolua_function(tolua_S,"GetPosX",tolua_AllToLua_cBlockEntity_GetPosX00); + tolua_function(tolua_S,"GetPosY",tolua_AllToLua_cBlockEntity_GetPosY00); + tolua_function(tolua_S,"GetPosZ",tolua_AllToLua_cBlockEntity_GetPosZ00); + tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockEntity_GetBlockType00); + tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cBlockEntity_GetWorld00); + tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cBlockEntity_GetChunkX00); + tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cBlockEntity_GetChunkZ00); + tolua_function(tolua_S,"GetRelX",tolua_AllToLua_cBlockEntity_GetRelX00); + tolua_function(tolua_S,"GetRelZ",tolua_AllToLua_cBlockEntity_GetRelZ00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cBlockEntityWithItems","cBlockEntityWithItems","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cBlockEntityWithItems"); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cBlockEntityWithItems_GetSlot01); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cBlockEntityWithItems_SetSlot01); + tolua_function(tolua_S,"GetContents",tolua_AllToLua_cBlockEntityWithItems_GetContents00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cChestEntity","cChestEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cChestEntity"); + tolua_constant(tolua_S,"ContentsHeight",cChestEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cChestEntity::ContentsWidth); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cDropSpenserEntity","cDropSpenserEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cDropSpenserEntity"); + tolua_constant(tolua_S,"ContentsHeight",cDropSpenserEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cDropSpenserEntity::ContentsWidth); + tolua_function(tolua_S,"AddDropSpenserDir",tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00); + tolua_function(tolua_S,"Activate",tolua_AllToLua_cDropSpenserEntity_Activate00); + tolua_function(tolua_S,"SetRedstonePower",tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cDispenserEntity","cDispenserEntity","cDropSpenserEntity",NULL); + tolua_beginmodule(tolua_S,"cDispenserEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cDropperEntity","cDropperEntity","cDropSpenserEntity",NULL); + tolua_beginmodule(tolua_S,"cDropperEntity"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cFurnaceEntity","cFurnaceEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cFurnaceEntity"); + tolua_constant(tolua_S,"fsInput",cFurnaceEntity::fsInput); + tolua_constant(tolua_S,"fsFuel",cFurnaceEntity::fsFuel); + tolua_constant(tolua_S,"fsOutput",cFurnaceEntity::fsOutput); + tolua_constant(tolua_S,"ContentsWidth",cFurnaceEntity::ContentsWidth); + tolua_constant(tolua_S,"ContentsHeight",cFurnaceEntity::ContentsHeight); + tolua_function(tolua_S,"GetInputSlot",tolua_AllToLua_cFurnaceEntity_GetInputSlot00); + tolua_function(tolua_S,"GetFuelSlot",tolua_AllToLua_cFurnaceEntity_GetFuelSlot00); + tolua_function(tolua_S,"GetOutputSlot",tolua_AllToLua_cFurnaceEntity_GetOutputSlot00); + tolua_function(tolua_S,"SetInputSlot",tolua_AllToLua_cFurnaceEntity_SetInputSlot00); + tolua_function(tolua_S,"SetFuelSlot",tolua_AllToLua_cFurnaceEntity_SetFuelSlot00); + tolua_function(tolua_S,"SetOutputSlot",tolua_AllToLua_cFurnaceEntity_SetOutputSlot00); + tolua_function(tolua_S,"GetTimeCooked",tolua_AllToLua_cFurnaceEntity_GetTimeCooked00); + tolua_function(tolua_S,"GetCookTimeLeft",tolua_AllToLua_cFurnaceEntity_GetCookTimeLeft00); + tolua_function(tolua_S,"GetFuelBurnTimeLeft",tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00); + tolua_function(tolua_S,"HasFuelTimeLeft",tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",NULL); + tolua_beginmodule(tolua_S,"cHopperEntity"); + tolua_constant(tolua_S,"ContentsHeight",cHopperEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cHopperEntity::ContentsWidth); + tolua_constant(tolua_S,"TICKS_PER_TRANSFER",cHopperEntity::TICKS_PER_TRANSFER); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cJukeboxEntity","cJukeboxEntity","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cJukeboxEntity"); + tolua_function(tolua_S,"GetRecord",tolua_AllToLua_cJukeboxEntity_GetRecord00); + tolua_function(tolua_S,"SetRecord",tolua_AllToLua_cJukeboxEntity_SetRecord00); + tolua_function(tolua_S,"PlayRecord",tolua_AllToLua_cJukeboxEntity_PlayRecord00); + tolua_function(tolua_S,"EjectRecord",tolua_AllToLua_cJukeboxEntity_EjectRecord00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cNoteEntity","cNoteEntity","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cNoteEntity"); + tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cNoteEntity_GetPitch00); + tolua_function(tolua_S,"SetPitch",tolua_AllToLua_cNoteEntity_SetPitch00); + tolua_function(tolua_S,"IncrementPitch",tolua_AllToLua_cNoteEntity_IncrementPitch00); + tolua_function(tolua_S,"MakeSound",tolua_AllToLua_cNoteEntity_MakeSound00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cSignEntity","cSignEntity","cBlockEntity",NULL); + tolua_beginmodule(tolua_S,"cSignEntity"); + tolua_function(tolua_S,"SetLines",tolua_AllToLua_cSignEntity_SetLines00); + tolua_function(tolua_S,"SetLine",tolua_AllToLua_cSignEntity_SetLine00); + tolua_function(tolua_S,"GetLine",tolua_AllToLua_cSignEntity_GetLine00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"HTTPFormData","HTTPFormData","",NULL); + tolua_beginmodule(tolua_S,"HTTPFormData"); + tolua_variable(tolua_S,"Name",tolua_get_HTTPFormData_Name,tolua_set_HTTPFormData_Name); + tolua_variable(tolua_S,"Value",tolua_get_HTTPFormData_Value,tolua_set_HTTPFormData_Value); + tolua_variable(tolua_S,"Type",tolua_get_HTTPFormData_Type,tolua_set_HTTPFormData_Type); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL); + tolua_beginmodule(tolua_S,"HTTPRequest"); + tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); + tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); + tolua_variable(tolua_S,"Username",tolua_get_HTTPRequest_Username,tolua_set_HTTPRequest_Username); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"HTTPTemplateRequest","HTTPTemplateRequest","",NULL); + tolua_beginmodule(tolua_S,"HTTPTemplateRequest"); + tolua_variable(tolua_S,"Request",tolua_get_HTTPTemplateRequest_Request,tolua_set_HTTPTemplateRequest_Request); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",tolua_collect_sWebAdminPage); + #else + tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",NULL); + #endif + tolua_beginmodule(tolua_S,"sWebAdminPage"); + tolua_variable(tolua_S,"Content",tolua_get_sWebAdminPage_Content,tolua_set_sWebAdminPage_Content); + 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","cHTTPServer::cCallbacks",NULL); + tolua_beginmodule(tolua_S,"cWebAdmin"); + 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_function(tolua_S,"GetHTMLEscapedString",tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL); + tolua_beginmodule(tolua_S,"cWebPlugin"); + tolua_function(tolua_S,"GetWebTitle",tolua_AllToLua_cWebPlugin_GetWebTitle00); + tolua_function(tolua_S,"HandleWebRequest",tolua_AllToLua_cWebPlugin_HandleWebRequest00); + tolua_function(tolua_S,"SafeString",tolua_AllToLua_cWebPlugin_SafeString00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cRoot","cRoot","",NULL); + tolua_beginmodule(tolua_S,"cRoot"); + tolua_function(tolua_S,"Get",tolua_AllToLua_cRoot_Get00); + tolua_function(tolua_S,"GetServer",tolua_AllToLua_cRoot_GetServer00); + tolua_function(tolua_S,"GetDefaultWorld",tolua_AllToLua_cRoot_GetDefaultWorld00); + tolua_function(tolua_S,"GetWorld",tolua_AllToLua_cRoot_GetWorld00); + tolua_function(tolua_S,"GetPrimaryServerVersion",tolua_AllToLua_cRoot_GetPrimaryServerVersion00); + tolua_function(tolua_S,"SetPrimaryServerVersion",tolua_AllToLua_cRoot_SetPrimaryServerVersion00); + tolua_function(tolua_S,"GetGroupManager",tolua_AllToLua_cRoot_GetGroupManager00); + tolua_function(tolua_S,"GetCraftingRecipes",tolua_AllToLua_cRoot_GetCraftingRecipes00); + tolua_function(tolua_S,"GetFurnaceFuelBurnTime",tolua_AllToLua_cRoot_GetFurnaceFuelBurnTime00); + tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00); + tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00); + tolua_function(tolua_S,"QueueExecuteConsoleCommand",tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00); + tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00); + 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); + #else + tolua_cclass(tolua_S,"Vector3f","Vector3f","",NULL); + #endif + tolua_beginmodule(tolua_S,"Vector3f"); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new03_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new04); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new04_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new04_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3f_new05); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3f_new05_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3f_new05_local); + tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3f_Set00); + tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3f_Normalize00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3f_NormalizeCopy01); + tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3f_Length00); + tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3f_SqrLength00); + tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3f_Dot00); + tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3f_Cross00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3f_Equals00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3f__add01); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub00); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3f__sub01); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul00); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3f__mul01); + tolua_variable(tolua_S,"x",tolua_get_Vector3f_x,tolua_set_Vector3f_x); + tolua_variable(tolua_S,"y",tolua_get_Vector3f_y,tolua_set_Vector3f_y); + tolua_variable(tolua_S,"z",tolua_get_Vector3f_z,tolua_set_Vector3f_z); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Vector3d","Vector3d","",tolua_collect_Vector3d); + #else + tolua_cclass(tolua_S,"Vector3d","Vector3d","",NULL); + #endif + tolua_beginmodule(tolua_S,"Vector3d"); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3d_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3d_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3d_new03_local); + tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3d_Set00); + tolua_function(tolua_S,"Normalize",tolua_AllToLua_Vector3d_Normalize00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy00); + tolua_function(tolua_S,"NormalizeCopy",tolua_AllToLua_Vector3d_NormalizeCopy01); + tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3d_Length00); + tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3d_SqrLength00); + tolua_function(tolua_S,"Dot",tolua_AllToLua_Vector3d_Dot00); + tolua_function(tolua_S,"Cross",tolua_AllToLua_Vector3d_Cross00); + tolua_function(tolua_S,"LineCoeffToXYPlane",tolua_AllToLua_Vector3d_LineCoeffToXYPlane00); + tolua_function(tolua_S,"LineCoeffToXZPlane",tolua_AllToLua_Vector3d_LineCoeffToXZPlane00); + tolua_function(tolua_S,"LineCoeffToYZPlane",tolua_AllToLua_Vector3d_LineCoeffToYZPlane00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3d_Equals00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add00); + tolua_function(tolua_S,".add",tolua_AllToLua_Vector3d__add01); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub00); + tolua_function(tolua_S,".sub",tolua_AllToLua_Vector3d__sub01); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul00); + tolua_function(tolua_S,".mul",tolua_AllToLua_Vector3d__mul01); + tolua_function(tolua_S,".div",tolua_AllToLua_Vector3d__div00); + tolua_variable(tolua_S,"x",tolua_get_Vector3d_x,tolua_set_Vector3d_x); + tolua_variable(tolua_S,"y",tolua_get_Vector3d_y,tolua_set_Vector3d_y); + tolua_variable(tolua_S,"z",tolua_get_Vector3d_z,tolua_set_Vector3d_z); + tolua_variable(tolua_S,"EPS",tolua_get_Vector3d_EPS,NULL); + tolua_variable(tolua_S,"NO_INTERSECTION",tolua_get_Vector3d_NO_INTERSECTION,NULL); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Vector3i","Vector3i","",tolua_collect_Vector3i); + #else + tolua_cclass(tolua_S,"Vector3i","Vector3i","",NULL); + #endif + tolua_beginmodule(tolua_S,"Vector3i"); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_Vector3i_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_Vector3i_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_Vector3i_new02_local); + tolua_function(tolua_S,"Set",tolua_AllToLua_Vector3i_Set00); + tolua_function(tolua_S,"Length",tolua_AllToLua_Vector3i_Length00); + tolua_function(tolua_S,"SqrLength",tolua_AllToLua_Vector3i_SqrLength00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals00); + tolua_function(tolua_S,"Equals",tolua_AllToLua_Vector3i_Equals01); + tolua_variable(tolua_S,"x",tolua_get_Vector3i_x,tolua_set_Vector3i_x); + tolua_variable(tolua_S,"y",tolua_get_Vector3i_y,tolua_set_Vector3i_y); + tolua_variable(tolua_S,"z",tolua_get_Vector3i_z,tolua_set_Vector3i_z); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cCuboid","cCuboid","",tolua_collect_cCuboid); + #else + tolua_cclass(tolua_S,"cCuboid","cCuboid","",NULL); + #endif + tolua_beginmodule(tolua_S,"cCuboid"); + tolua_variable(tolua_S,"p1",tolua_get_cCuboid_p1,tolua_set_cCuboid_p1); + tolua_variable(tolua_S,"p2",tolua_get_cCuboid_p2,tolua_set_cCuboid_p2); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new03_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cCuboid_new04); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCuboid_new04_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCuboid_new04_local); + tolua_function(tolua_S,"Assign",tolua_AllToLua_cCuboid_Assign00); + tolua_function(tolua_S,"Sort",tolua_AllToLua_cCuboid_Sort00); + tolua_function(tolua_S,"DifX",tolua_AllToLua_cCuboid_DifX00); + tolua_function(tolua_S,"DifY",tolua_AllToLua_cCuboid_DifY00); + tolua_function(tolua_S,"DifZ",tolua_AllToLua_cCuboid_DifZ00); + tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cCuboid_DoesIntersect00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside01); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cCuboid_IsInside02); + tolua_function(tolua_S,"IsCompletelyInside",tolua_AllToLua_cCuboid_IsCompletelyInside00); + tolua_function(tolua_S,"Move",tolua_AllToLua_cCuboid_Move00); + tolua_function(tolua_S,"IsSorted",tolua_AllToLua_cCuboid_IsSorted00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",tolua_collect_cBoundingBox); + #else + tolua_cclass(tolua_S,"cBoundingBox","cBoundingBox","",NULL); + #endif + tolua_beginmodule(tolua_S,"cBoundingBox"); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new00_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new01); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new01_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new01_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new02); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new02_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new02_local); + tolua_function(tolua_S,"new",tolua_AllToLua_cBoundingBox_new03); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBoundingBox_new03_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBoundingBox_new03_local); + tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move00); + tolua_function(tolua_S,"Move",tolua_AllToLua_cBoundingBox_Move01); + tolua_function(tolua_S,"Expand",tolua_AllToLua_cBoundingBox_Expand00); + tolua_function(tolua_S,"DoesIntersect",tolua_AllToLua_cBoundingBox_DoesIntersect00); + tolua_function(tolua_S,"Union",tolua_AllToLua_cBoundingBox_Union00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside00); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside01); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside02); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside03); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside04); + tolua_function(tolua_S,"IsInside",tolua_AllToLua_cBoundingBox_IsInside05); + tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection00); + tolua_function(tolua_S,"CalcLineIntersection",tolua_AllToLua_cBoundingBox_CalcLineIntersection01); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cTracer","cTracer","",tolua_collect_cTracer); + #else + tolua_cclass(tolua_S,"cTracer","cTracer","",NULL); + #endif + tolua_beginmodule(tolua_S,"cTracer"); + tolua_variable(tolua_S,"BlockHitPosition",tolua_get_cTracer_BlockHitPosition,tolua_set_cTracer_BlockHitPosition); + tolua_variable(tolua_S,"HitNormal",tolua_get_cTracer_HitNormal,tolua_set_cTracer_HitNormal); + tolua_variable(tolua_S,"RealHit",tolua_get_cTracer_RealHit,tolua_set_cTracer_RealHit); + tolua_function(tolua_S,"new",tolua_AllToLua_cTracer_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cTracer_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cTracer_new00_local); + tolua_function(tolua_S,"delete",tolua_AllToLua_cTracer_delete00); + tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace00); + tolua_function(tolua_S,"Trace",tolua_AllToLua_cTracer_Trace01); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cGroup","cGroup","",NULL); + tolua_beginmodule(tolua_S,"cGroup"); + tolua_function(tolua_S,"SetName",tolua_AllToLua_cGroup_SetName00); + tolua_function(tolua_S,"GetName",tolua_AllToLua_cGroup_GetName00); + tolua_function(tolua_S,"SetColor",tolua_AllToLua_cGroup_SetColor00); + tolua_function(tolua_S,"AddCommand",tolua_AllToLua_cGroup_AddCommand00); + tolua_function(tolua_S,"AddPermission",tolua_AllToLua_cGroup_AddPermission00); + tolua_function(tolua_S,"InheritFrom",tolua_AllToLua_cGroup_InheritFrom00); + tolua_function(tolua_S,"HasCommand",tolua_AllToLua_cGroup_HasCommand00); + tolua_function(tolua_S,"GetColor",tolua_AllToLua_cGroup_GetColor00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",tolua_collect_cBlockArea); + #else + tolua_cclass(tolua_S,"cBlockArea","cBlockArea","",NULL); + #endif + tolua_beginmodule(tolua_S,"cBlockArea"); + tolua_constant(tolua_S,"baTypes",cBlockArea::baTypes); + tolua_constant(tolua_S,"baMetas",cBlockArea::baMetas); + tolua_constant(tolua_S,"baLight",cBlockArea::baLight); + tolua_constant(tolua_S,"baSkyLight",cBlockArea::baSkyLight); + tolua_constant(tolua_S,"msOverwrite",cBlockArea::msOverwrite); + tolua_constant(tolua_S,"msFillAir",cBlockArea::msFillAir); + tolua_constant(tolua_S,"msImprint",cBlockArea::msImprint); + tolua_constant(tolua_S,"msLake",cBlockArea::msLake); + tolua_function(tolua_S,"new",tolua_AllToLua_cBlockArea_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cBlockArea_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cBlockArea_new00_local); + tolua_function(tolua_S,"delete",tolua_AllToLua_cBlockArea_delete00); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cBlockArea_Clear00); + tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create00); + tolua_function(tolua_S,"Create",tolua_AllToLua_cBlockArea_Create01); + tolua_function(tolua_S,"SetOrigin",tolua_AllToLua_cBlockArea_SetOrigin00); + tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read00); + tolua_function(tolua_S,"Read",tolua_AllToLua_cBlockArea_Read01); + tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write00); + tolua_function(tolua_S,"Write",tolua_AllToLua_cBlockArea_Write01); + tolua_function(tolua_S,"CopyTo",tolua_AllToLua_cBlockArea_CopyTo00); + tolua_function(tolua_S,"CopyFrom",tolua_AllToLua_cBlockArea_CopyFrom00); + tolua_function(tolua_S,"DumpToRawFile",tolua_AllToLua_cBlockArea_DumpToRawFile00); + tolua_function(tolua_S,"LoadFromSchematicFile",tolua_AllToLua_cBlockArea_LoadFromSchematicFile00); + tolua_function(tolua_S,"SaveToSchematicFile",tolua_AllToLua_cBlockArea_SaveToSchematicFile00); + tolua_function(tolua_S,"Crop",tolua_AllToLua_cBlockArea_Crop00); + tolua_function(tolua_S,"Expand",tolua_AllToLua_cBlockArea_Expand00); + tolua_function(tolua_S,"Merge",tolua_AllToLua_cBlockArea_Merge00); + tolua_function(tolua_S,"Fill",tolua_AllToLua_cBlockArea_Fill00); + tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cBlockArea_FillRelCuboid00); + tolua_function(tolua_S,"RelLine",tolua_AllToLua_cBlockArea_RelLine00); + tolua_function(tolua_S,"RotateCCW",tolua_AllToLua_cBlockArea_RotateCCW00); + tolua_function(tolua_S,"RotateCW",tolua_AllToLua_cBlockArea_RotateCW00); + tolua_function(tolua_S,"MirrorXY",tolua_AllToLua_cBlockArea_MirrorXY00); + tolua_function(tolua_S,"MirrorXZ",tolua_AllToLua_cBlockArea_MirrorXZ00); + tolua_function(tolua_S,"MirrorYZ",tolua_AllToLua_cBlockArea_MirrorYZ00); + tolua_function(tolua_S,"RotateCCWNoMeta",tolua_AllToLua_cBlockArea_RotateCCWNoMeta00); + tolua_function(tolua_S,"RotateCWNoMeta",tolua_AllToLua_cBlockArea_RotateCWNoMeta00); + tolua_function(tolua_S,"MirrorXYNoMeta",tolua_AllToLua_cBlockArea_MirrorXYNoMeta00); + tolua_function(tolua_S,"MirrorXZNoMeta",tolua_AllToLua_cBlockArea_MirrorXZNoMeta00); + tolua_function(tolua_S,"MirrorYZNoMeta",tolua_AllToLua_cBlockArea_MirrorYZNoMeta00); + tolua_function(tolua_S,"SetRelBlockType",tolua_AllToLua_cBlockArea_SetRelBlockType00); + tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cBlockArea_SetBlockType00); + tolua_function(tolua_S,"SetRelBlockMeta",tolua_AllToLua_cBlockArea_SetRelBlockMeta00); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cBlockArea_SetBlockMeta00); + tolua_function(tolua_S,"SetRelBlockLight",tolua_AllToLua_cBlockArea_SetRelBlockLight00); + tolua_function(tolua_S,"SetBlockLight",tolua_AllToLua_cBlockArea_SetBlockLight00); + tolua_function(tolua_S,"SetRelBlockSkyLight",tolua_AllToLua_cBlockArea_SetRelBlockSkyLight00); + tolua_function(tolua_S,"SetBlockSkyLight",tolua_AllToLua_cBlockArea_SetBlockSkyLight00); + tolua_function(tolua_S,"GetRelBlockType",tolua_AllToLua_cBlockArea_GetRelBlockType00); + tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cBlockArea_GetBlockType00); + tolua_function(tolua_S,"GetRelBlockMeta",tolua_AllToLua_cBlockArea_GetRelBlockMeta00); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cBlockArea_GetBlockMeta00); + tolua_function(tolua_S,"GetRelBlockLight",tolua_AllToLua_cBlockArea_GetRelBlockLight00); + tolua_function(tolua_S,"GetBlockLight",tolua_AllToLua_cBlockArea_GetBlockLight00); + tolua_function(tolua_S,"GetRelBlockSkyLight",tolua_AllToLua_cBlockArea_GetRelBlockSkyLight00); + tolua_function(tolua_S,"GetBlockSkyLight",tolua_AllToLua_cBlockArea_GetBlockSkyLight00); + tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cBlockArea_SetBlockTypeMeta00); + tolua_function(tolua_S,"SetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_SetRelBlockTypeMeta00); + tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cBlockArea_GetBlockTypeMeta00); + tolua_function(tolua_S,"GetRelBlockTypeMeta",tolua_AllToLua_cBlockArea_GetRelBlockTypeMeta00); + tolua_function(tolua_S,"GetSizeX",tolua_AllToLua_cBlockArea_GetSizeX00); + tolua_function(tolua_S,"GetSizeY",tolua_AllToLua_cBlockArea_GetSizeY00); + tolua_function(tolua_S,"GetSizeZ",tolua_AllToLua_cBlockArea_GetSizeZ00); + tolua_function(tolua_S,"GetOriginX",tolua_AllToLua_cBlockArea_GetOriginX00); + tolua_function(tolua_S,"GetOriginY",tolua_AllToLua_cBlockArea_GetOriginY00); + tolua_function(tolua_S,"GetOriginZ",tolua_AllToLua_cBlockArea_GetOriginZ00); + tolua_function(tolua_S,"GetDataTypes",tolua_AllToLua_cBlockArea_GetDataTypes00); + tolua_function(tolua_S,"HasBlockTypes",tolua_AllToLua_cBlockArea_HasBlockTypes00); + tolua_function(tolua_S,"HasBlockMetas",tolua_AllToLua_cBlockArea_HasBlockMetas00); + tolua_function(tolua_S,"HasBlockLights",tolua_AllToLua_cBlockArea_HasBlockLights00); + tolua_function(tolua_S,"HasBlockSkyLights",tolua_AllToLua_cBlockArea_HasBlockSkyLights00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cChunkDesc","cChunkDesc","",NULL); + 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,"FillBlocks",tolua_AllToLua_cChunkDesc_FillBlocks00); + tolua_function(tolua_S,"SetBlockTypeMeta",tolua_AllToLua_cChunkDesc_SetBlockTypeMeta00); + tolua_function(tolua_S,"GetBlockTypeMeta",tolua_AllToLua_cChunkDesc_GetBlockTypeMeta00); + tolua_function(tolua_S,"SetBlockType",tolua_AllToLua_cChunkDesc_SetBlockType00); + tolua_function(tolua_S,"GetBlockType",tolua_AllToLua_cChunkDesc_GetBlockType00); + tolua_function(tolua_S,"SetBlockMeta",tolua_AllToLua_cChunkDesc_SetBlockMeta00); + tolua_function(tolua_S,"GetBlockMeta",tolua_AllToLua_cChunkDesc_GetBlockMeta00); + tolua_function(tolua_S,"SetBiome",tolua_AllToLua_cChunkDesc_SetBiome00); + tolua_function(tolua_S,"GetBiome",tolua_AllToLua_cChunkDesc_GetBiome00); + tolua_function(tolua_S,"SetHeight",tolua_AllToLua_cChunkDesc_SetHeight00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cChunkDesc_GetHeight00); + tolua_function(tolua_S,"SetUseDefaultBiomes",tolua_AllToLua_cChunkDesc_SetUseDefaultBiomes00); + tolua_function(tolua_S,"IsUsingDefaultBiomes",tolua_AllToLua_cChunkDesc_IsUsingDefaultBiomes00); + tolua_function(tolua_S,"SetUseDefaultHeight",tolua_AllToLua_cChunkDesc_SetUseDefaultHeight00); + tolua_function(tolua_S,"IsUsingDefaultHeight",tolua_AllToLua_cChunkDesc_IsUsingDefaultHeight00); + tolua_function(tolua_S,"SetUseDefaultComposition",tolua_AllToLua_cChunkDesc_SetUseDefaultComposition00); + tolua_function(tolua_S,"IsUsingDefaultComposition",tolua_AllToLua_cChunkDesc_IsUsingDefaultComposition00); + tolua_function(tolua_S,"SetUseDefaultStructures",tolua_AllToLua_cChunkDesc_SetUseDefaultStructures00); + tolua_function(tolua_S,"IsUsingDefaultStructures",tolua_AllToLua_cChunkDesc_IsUsingDefaultStructures00); + tolua_function(tolua_S,"SetUseDefaultFinish",tolua_AllToLua_cChunkDesc_SetUseDefaultFinish00); + tolua_function(tolua_S,"IsUsingDefaultFinish",tolua_AllToLua_cChunkDesc_IsUsingDefaultFinish00); + tolua_function(tolua_S,"WriteBlockArea",tolua_AllToLua_cChunkDesc_WriteBlockArea00); + tolua_function(tolua_S,"ReadBlockArea",tolua_AllToLua_cChunkDesc_ReadBlockArea00); + tolua_function(tolua_S,"GetMaxHeight",tolua_AllToLua_cChunkDesc_GetMaxHeight00); + tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid00); + tolua_function(tolua_S,"FillRelCuboid",tolua_AllToLua_cChunkDesc_FillRelCuboid01); + tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid00); + tolua_function(tolua_S,"ReplaceRelCuboid",tolua_AllToLua_cChunkDesc_ReplaceRelCuboid01); + tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid00); + tolua_function(tolua_S,"FloorRelCuboid",tolua_AllToLua_cChunkDesc_FloorRelCuboid01); + tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid00); + tolua_function(tolua_S,"RandomFillRelCuboid",tolua_AllToLua_cChunkDesc_RandomFillRelCuboid01); + tolua_function(tolua_S,"GetBlockEntity",tolua_AllToLua_cChunkDesc_GetBlockEntity00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",tolua_collect_cCraftingGrid); + #else + tolua_cclass(tolua_S,"cCraftingGrid","cCraftingGrid","",NULL); + #endif + tolua_beginmodule(tolua_S,"cCraftingGrid"); + tolua_function(tolua_S,"new",tolua_AllToLua_cCraftingGrid_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cCraftingGrid_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cCraftingGrid_new00_local); + tolua_function(tolua_S,"GetWidth",tolua_AllToLua_cCraftingGrid_GetWidth00); + tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cCraftingGrid_GetHeight00); + tolua_function(tolua_S,"GetItem",tolua_AllToLua_cCraftingGrid_GetItem00); + tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem00); + tolua_function(tolua_S,"SetItem",tolua_AllToLua_cCraftingGrid_SetItem01); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingGrid_Clear00); + tolua_function(tolua_S,"ConsumeGrid",tolua_AllToLua_cCraftingGrid_ConsumeGrid00); + tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingGrid_Dump00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cCraftingRecipe","cCraftingRecipe","",NULL); + tolua_beginmodule(tolua_S,"cCraftingRecipe"); + tolua_function(tolua_S,"Clear",tolua_AllToLua_cCraftingRecipe_Clear00); + tolua_function(tolua_S,"GetIngredientsWidth",tolua_AllToLua_cCraftingRecipe_GetIngredientsWidth00); + tolua_function(tolua_S,"GetIngredientsHeight",tolua_AllToLua_cCraftingRecipe_GetIngredientsHeight00); + tolua_function(tolua_S,"GetIngredient",tolua_AllToLua_cCraftingRecipe_GetIngredient00); + tolua_function(tolua_S,"GetResult",tolua_AllToLua_cCraftingRecipe_GetResult00); + tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult00); + tolua_function(tolua_S,"SetResult",tolua_AllToLua_cCraftingRecipe_SetResult01); + tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient00); + tolua_function(tolua_S,"SetIngredient",tolua_AllToLua_cCraftingRecipe_SetIngredient01); + tolua_function(tolua_S,"ConsumeIngredients",tolua_AllToLua_cCraftingRecipe_ConsumeIngredients00); + tolua_function(tolua_S,"Dump",tolua_AllToLua_cCraftingRecipe_Dump00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cWindow","cWindow","",NULL); + tolua_beginmodule(tolua_S,"cWindow"); + tolua_constant(tolua_S,"wtInventory",cWindow::wtInventory); + tolua_constant(tolua_S,"wtChest",cWindow::wtChest); + tolua_constant(tolua_S,"wtWorkbench",cWindow::wtWorkbench); + tolua_constant(tolua_S,"wtFurnace",cWindow::wtFurnace); + tolua_constant(tolua_S,"wtDropSpenser",cWindow::wtDropSpenser); + tolua_constant(tolua_S,"wtEnchantment",cWindow::wtEnchantment); + tolua_constant(tolua_S,"wtBrewery",cWindow::wtBrewery); + tolua_constant(tolua_S,"wtNPCTrade",cWindow::wtNPCTrade); + tolua_constant(tolua_S,"wtBeacon",cWindow::wtBeacon); + tolua_constant(tolua_S,"wtAnvil",cWindow::wtAnvil); + tolua_constant(tolua_S,"wtHopper",cWindow::wtHopper); + tolua_constant(tolua_S,"wtAnimalChest",cWindow::wtAnimalChest); + tolua_function(tolua_S,"GetWindowID",tolua_AllToLua_cWindow_GetWindowID00); + tolua_function(tolua_S,"GetWindowType",tolua_AllToLua_cWindow_GetWindowType00); + tolua_function(tolua_S,"GetSlot",tolua_AllToLua_cWindow_GetSlot00); + tolua_function(tolua_S,"SetSlot",tolua_AllToLua_cWindow_SetSlot00); + tolua_function(tolua_S,"IsSlotInPlayerMainInventory",tolua_AllToLua_cWindow_IsSlotInPlayerMainInventory00); + tolua_function(tolua_S,"IsSlotInPlayerHotbar",tolua_AllToLua_cWindow_IsSlotInPlayerHotbar00); + tolua_function(tolua_S,"IsSlotInPlayerInventory",tolua_AllToLua_cWindow_IsSlotInPlayerInventory00); + tolua_function(tolua_S,"GetWindowTitle",tolua_AllToLua_cWindow_GetWindowTitle00); + tolua_function(tolua_S,"SetWindowTitle",tolua_AllToLua_cWindow_SetWindowTitle00); + tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty00); + tolua_function(tolua_S,"SetProperty",tolua_AllToLua_cWindow_SetProperty01); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",tolua_collect_cLuaWindow); + #else + tolua_cclass(tolua_S,"cLuaWindow","cLuaWindow","cWindow",NULL); + #endif + tolua_beginmodule(tolua_S,"cLuaWindow"); + tolua_function(tolua_S,"new",tolua_AllToLua_cLuaWindow_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cLuaWindow_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cLuaWindow_new00_local); + tolua_function(tolua_S,"delete",tolua_AllToLua_cLuaWindow_delete00); + tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL); + tolua_beginmodule(tolua_S,"cMonster"); + tolua_constant(tolua_S,"mtInvalidType",cMonster::mtInvalidType); + 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,"mtEnderDragon",cMonster::mtEnderDragon); + tolua_constant(tolua_S,"mtEnderman",cMonster::mtEnderman); + 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,"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,"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,"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_constant(tolua_S,"mfHostile",cMonster::mfHostile); + tolua_constant(tolua_S,"mfPassive",cMonster::mfPassive); + tolua_constant(tolua_S,"mfAmbient",cMonster::mfAmbient); + tolua_constant(tolua_S,"mfWater",cMonster::mfWater); + tolua_constant(tolua_S,"mfMaxplusone",cMonster::mfMaxplusone); + tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00); + tolua_function(tolua_S,"GetMobFamily",tolua_AllToLua_cMonster_GetMobFamily00); + tolua_function(tolua_S,"MobTypeToString",tolua_AllToLua_cMonster_MobTypeToString00); + tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_cMonster_StringToMobType00); + tolua_function(tolua_S,"FamilyFromType",tolua_AllToLua_cMonster_FamilyFromType00); + tolua_function(tolua_S,"GetSpawnDelay",tolua_AllToLua_cMonster_GetSpawnDelay00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL); + tolua_beginmodule(tolua_S,"cLineBlockTracer"); + tolua_endmodule(tolua_S); + tolua_endmodule(tolua_S); + return 1; +} + + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501 + TOLUA_API int luaopen_AllToLua (lua_State* tolua_S) { + return tolua_AllToLua_open(tolua_S); +}; +#endif + diff --git a/src/Bindings.h b/src/Bindings.h new file mode 100644 index 000000000..240ceec30 --- /dev/null +++ b/src/Bindings.h @@ -0,0 +1,8 @@ +/* +** Lua binding: AllToLua +** Generated automatically by tolua++-1.0.92 on 11/26/13 15:29:20. +*/ + +/* Exported function */ +TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); + diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp new file mode 100644 index 000000000..5c15adfef --- /dev/null +++ b/src/BlockArea.cpp @@ -0,0 +1,2124 @@ + +// BlockArea.cpp + +// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries +// The object also supports writing the blockdata back into cWorld, even into other coords + +#include "Globals.h" +#include "BlockArea.h" +#include "World.h" +#include "OSSupport/GZipFile.h" +#include "WorldStorage/FastNBT.h" +#include "Blocks/BlockHandler.h" + + + + + +// This wild construct allows us to pass a function argument and still have it inlined by the compiler :) +/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function +template<typename Combinator> void InternalMergeBlocks( + BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes, + NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas, + int a_SizeX, int a_SizeY, int a_SizeZ, + int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ, + int a_DstOffX, int a_DstOffY, int a_DstOffZ, + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ, + Combinator a_Combinator +) +{ + for (int y = 0; y < a_SizeY; y++) + { + int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ; + int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ; + for (int z = 0; z < a_SizeZ; z++) + { + int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX; + int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX; + int SrcIdx = SrcBaseZ + a_SrcOffX; + int DstIdx = DstBaseZ + a_DstOffX; + for (int x = 0; x < a_SizeX; x++) + { + a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]); + ++DstIdx; + ++SrcIdx; + } // for x + } // for z + } // for y +} + + + + + +/// Combinator used for cBlockArea::msOverwrite merging +static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; +} + + + + + +/// Combinator used for cBlockArea::msFillAir merging +static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + if (a_DstType == E_BLOCK_AIR) + { + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; + } + // "else" is the default, already in place +} + + + + + +/// Combinator used for cBlockArea::msImprint merging +static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + if (a_SrcType != E_BLOCK_AIR) + { + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; + } + // "else" is the default, already in place +} + + + + + +/// Combinator used for cBlockArea::msLake merging +static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + // Sponge is the NOP block + if (a_SrcType == E_BLOCK_SPONGE) + { + return; + } + + // Air is always hollowed out + if (a_SrcType == E_BLOCK_AIR) + { + a_DstType = E_BLOCK_AIR; + a_DstMeta = 0; + return; + } + + // Water and lava are never overwritten + switch (a_DstType) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + return; + } + } + + // Water and lava always overwrite + switch (a_SrcType) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + a_DstType = a_SrcType; + a_DstMeta = a_DstMeta; + return; + } + } + + if (a_SrcType == E_BLOCK_STONE) + { + switch (a_DstType) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_MYCELIUM: + { + a_DstType = E_BLOCK_STONE; + a_DstMeta = 0; + return; + } + } + } + // Everything else is left as it is +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBlockArea: + +cBlockArea::cBlockArea(void) : + m_SizeX(0), + m_SizeY(0), + m_SizeZ(0), + m_BlockTypes(NULL), + m_BlockMetas(NULL), + m_BlockLight(NULL), + m_BlockSkyLight(NULL) +{ +} + + + + + +cBlockArea::~cBlockArea() +{ + Clear(); +} + + + + + +void cBlockArea::Clear(void) +{ + delete[] m_BlockTypes; m_BlockTypes = NULL; + delete[] m_BlockMetas; m_BlockMetas = NULL; + delete[] m_BlockLight; m_BlockLight = NULL; + delete[] m_BlockSkyLight; m_BlockSkyLight = NULL; + m_SizeX = 0; + m_SizeY = 0; + m_SizeZ = 0; +} + + + + + +void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes) +{ + Clear(); + int BlockCount = a_SizeX * a_SizeY * a_SizeZ; + if ((a_DataTypes & baTypes) != 0) + { + m_BlockTypes = new BLOCKTYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockTypes[i] = E_BLOCK_AIR; + } + } + if ((a_DataTypes & baMetas) != 0) + { + m_BlockMetas = new NIBBLETYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockMetas[i] = 0; + } + } + if ((a_DataTypes & baLight) != 0) + { + m_BlockLight = new NIBBLETYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockLight[i] = 0; + } + } + if ((a_DataTypes & baSkyLight) != 0) + { + m_BlockSkyLight = new NIBBLETYPE[BlockCount]; + for (int i = 0; i < BlockCount; i++) + { + m_BlockSkyLight[i] = 0x0f; + } + } + m_SizeX = a_SizeX; + m_SizeY = a_SizeY; + m_SizeZ = a_SizeZ; + m_OriginX = 0; + m_OriginY = 0; + m_OriginZ = 0; +} + + + + + +void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ) +{ + m_OriginX = a_OriginX; + m_OriginY = a_OriginY; + m_OriginZ = a_OriginZ; +} + + + + + +bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes) +{ + // Normalize the coords: + if (a_MinBlockX > a_MaxBlockX) + { + std::swap(a_MinBlockX, a_MaxBlockX); + } + if (a_MinBlockY > a_MaxBlockY) + { + std::swap(a_MinBlockY, a_MaxBlockY); + } + if (a_MinBlockZ > a_MaxBlockZ) + { + std::swap(a_MinBlockZ, a_MaxBlockZ); + } + + // Include the Max coords: + a_MaxBlockX += 1; + a_MaxBlockY += 1; + a_MaxBlockZ += 1; + + // Check coords validity: + if (a_MinBlockY < 0) + { + LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); + a_MinBlockY = 0; + } + else if (a_MinBlockY >= cChunkDef::Height) + { + LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinBlockY = cChunkDef::Height - 1; + } + if (a_MaxBlockY < 0) + { + LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__); + a_MaxBlockY = 0; + } + else if (a_MaxBlockY >= cChunkDef::Height) + { + LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MaxBlockY = cChunkDef::Height - 1; + } + + // Allocate the needed memory: + Clear(); + if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes)) + { + return false; + } + m_OriginX = a_MinBlockX; + m_OriginY = a_MinBlockY; + m_OriginZ = a_MinBlockZ; + cChunkReader Reader(*this); + + // Convert block coords to chunks coords: + int MinChunkX, MaxChunkX; + int MinChunkZ, MaxChunkZ; + cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ); + cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ); + + // Query block data: + if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader)) + { + Clear(); + return false; + } + + return true; +} + + + + + +bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have? + a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have + + // Check coords validity: + if (a_MinBlockY < 0) + { + LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__); + a_MinBlockY = 0; + } + else if (a_MinBlockY >= cChunkDef::Height - m_SizeY) + { + LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinBlockY = cChunkDef::Height - m_SizeY - 1; + } + + return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); +} + + + + + +void cBlockArea::CopyTo(cBlockArea & a_Into) const +{ + if (&a_Into == this) + { + LOGWARNING("Trying to copy a cBlockArea into self, ignoring."); + return; + } + + a_Into.Clear(); + a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes()); + a_Into.m_OriginX = m_OriginX; + a_Into.m_OriginY = m_OriginY; + a_Into.m_OriginZ = m_OriginZ; + int BlockCount = GetBlockCount(); + if (HasBlockTypes()) + { + memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE)); + } + if (HasBlockMetas()) + { + memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE)); + } + if (HasBlockLights()) + { + memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE)); + } + if (HasBlockSkyLights()) + { + memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE)); + } +} + + + + + +void cBlockArea::CopyFrom(const cBlockArea & a_From) +{ + a_From.CopyTo(*this); +} + + + + + +void cBlockArea::DumpToRawFile(const AString & a_FileName) +{ + cFile f; + if (!f.Open(a_FileName, cFile::fmWrite)) + { + LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str()); + return; + } + UInt32 SizeX = ntohl(m_SizeX); + UInt32 SizeY = ntohl(m_SizeY); + UInt32 SizeZ = ntohl(m_SizeZ); + f.Write(&SizeX, 4); + f.Write(&SizeY, 4); + f.Write(&SizeZ, 4); + unsigned char DataTypes = GetDataTypes(); + f.Write(&DataTypes, 1); + int NumBlocks = GetBlockCount(); + if (HasBlockTypes()) + { + f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE)); + } + if (HasBlockMetas()) + { + f.Write(m_BlockMetas, NumBlocks); + } + if (HasBlockLights()) + { + f.Write(m_BlockLight, NumBlocks); + } + if (HasBlockSkyLights()) + { + f.Write(m_BlockSkyLight, NumBlocks); + } +} + + + + + +bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName) +{ + // Un-GZip the contents: + AString Contents; + cGZipFile File; + if (!File.Open(a_FileName, cGZipFile::fmRead)) + { + LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str()); + return false; + } + int NumBytesRead = File.ReadRestOfFile(Contents); + if (NumBytesRead < 0) + { + LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead); + return false; + } + File.Close(); + + // Parse the NBT: + cParsedNBT NBT(Contents.data(), Contents.size()); + if (!NBT.IsValid()) + { + LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str()); + return false; + } + + return LoadFromSchematicNBT(NBT); +} + + + + + +bool cBlockArea::SaveToSchematicFile(const AString & a_FileName) +{ + cFastNBTWriter Writer("Schematic"); + Writer.AddShort("Width", m_SizeX); + Writer.AddShort("Height", m_SizeY); + Writer.AddShort("Length", m_SizeZ); + Writer.AddString("Materials", "Alpha"); + if (HasBlockTypes()) + { + Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount()); + } + else + { + AString Dummy(GetBlockCount(), 0); + Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size()); + } + if (HasBlockMetas()) + { + Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount()); + } + else + { + AString Dummy(GetBlockCount(), 0); + Writer.AddByteArray("Data", Dummy.data(), Dummy.size()); + } + // TODO: Save entities and block entities + Writer.BeginList("Entities", TAG_Compound); + Writer.EndList(); + Writer.BeginList("TileEntities", TAG_Compound); + Writer.EndList(); + Writer.Finish(); + + // Save to file + cGZipFile File; + if (!File.Open(a_FileName, cGZipFile::fmWrite)) + { + LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str()); + return false; + } + if (!File.Write(Writer.GetResult())) + { + LOG("Cannot write data to file \"%s\".", a_FileName.c_str()); + return false; + } + return true; +} + + + + + +void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) +{ + if ( + (a_AddMinX + a_SubMaxX >= m_SizeX) || + (a_AddMinY + a_SubMaxY >= m_SizeY) || + (a_AddMinZ + a_SubMaxZ >= m_SizeZ) + ) + { + LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d", + m_SizeX, m_SizeY, m_SizeZ, + a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ + ); + return; + } + + if (HasBlockTypes()) + { + CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + if (HasBlockMetas()) + { + CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + if (HasBlockLights()) + { + CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + if (HasBlockSkyLights()) + { + CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ); + } + m_OriginX += a_AddMinX; + m_OriginY += a_AddMinY; + m_OriginZ += a_AddMinZ; + m_SizeX -= a_AddMinX + a_SubMaxX; + m_SizeY -= a_AddMinY + a_SubMaxY; + m_SizeZ -= a_AddMinZ + a_SubMaxZ; +} + + + + + +void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + if (HasBlockTypes()) + { + ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + if (HasBlockMetas()) + { + ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + if (HasBlockLights()) + { + ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + if (HasBlockSkyLights()) + { + ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ); + } + m_OriginX -= a_SubMinX; + m_OriginY -= a_SubMinY; + m_OriginZ -= a_SubMinZ; + m_SizeX += a_SubMinX + a_AddMaxX; + m_SizeY += a_SubMinY + a_AddMaxY; + m_SizeZ += a_SubMinZ + a_AddMaxZ; +} + + + + + +void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy) +{ + // Block types are compulsory, block metas are voluntary + if (!HasBlockTypes() || !a_Src.HasBlockTypes()) + { + LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__); + return; + } + + // Dst is *this, Src is a_Src + int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading + int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing + int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy + + int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading + int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing + int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy + + int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading + int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing + int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy + + const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas(); + NIBBLETYPE * DstMetas = m_BlockMetas; + bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL)); + + if (IsDummyMetas) + { + SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()]; + DstMetas = new NIBBLETYPE[GetBlockCount()]; + } + + switch (a_Strategy) + { + case msOverwrite: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorOverwrite + ); + break; + } // case msOverwrite + + case msFillAir: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorFillAir + ); + break; + } // case msFillAir + + case msImprint: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorImprint + ); + break; + } // case msImprint + + case msLake: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_SizeX, m_SizeY, m_SizeZ, + MergeCombinatorLake + ); + break; + } // case msLake + + default: + { + LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); + ASSERT(!"Unknown block area merge strategy"); + break; + } + } // switch (a_Strategy) + + if (IsDummyMetas) + { + delete[] SrcMetas; + delete[] DstMetas; + } +} + + + + + +void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight) +{ + if ((a_DataTypes & GetDataTypes()) != a_DataTypes) + { + LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)", + __FUNCTION__, a_DataTypes, GetDataTypes() + ); + a_DataTypes = a_DataTypes & GetDataTypes(); + } + + int BlockCount = GetBlockCount(); + if ((a_DataTypes & baTypes) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockTypes[i] = a_BlockType; + } + } + if ((a_DataTypes & baMetas) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockMetas[i] = a_BlockMeta; + } + } + if ((a_DataTypes & baLight) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockLight[i] = a_BlockLight; + } + } + if ((a_DataTypes & baSkyLight) != 0) + { + for (int i = 0; i < BlockCount; i++) + { + m_BlockSkyLight[i] = a_BlockSkyLight; + } + } +} + + + + + +void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + if ((a_DataTypes & GetDataTypes()) != a_DataTypes) + { + LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)", + __FUNCTION__, a_DataTypes, GetDataTypes() + ); + a_DataTypes = a_DataTypes & GetDataTypes(); + } + + if ((a_DataTypes & baTypes) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType; + } // for x, z, y + } + if ((a_DataTypes & baMetas) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta; + } // for x, z, y + } + if ((a_DataTypes & baLight) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight; + } // for x, z, y + } + if ((a_DataTypes & baSkyLight) != 0) + { + for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight; + } // for x, z, y + } +} + + + + + +void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + // Bresenham-3D algorithm for drawing lines: + int dx = abs(a_RelX2 - a_RelX1); + int dy = abs(a_RelY2 - a_RelY1); + int dz = abs(a_RelZ2 - a_RelZ1); + int sx = (a_RelX1 < a_RelX2) ? 1 : -1; + int sy = (a_RelY1 < a_RelY2) ? 1 : -1; + int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1; + int err = dx - dz; + + if (dx >= std::max(dy, dz)) // x dominant + { + int yd = dy - dx / 2; + int zd = dz - dx / 2; + + while (true) + { + RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); + + if (a_RelX1 == a_RelX2) + { + break; + } + + if (yd >= 0) // move along y + { + a_RelY1 += sy; + yd -= dx; + } + + if (zd >= 0) // move along z + { + a_RelZ1 += sz; + zd -= dx; + } + + // move along x + a_RelX1 += sx; + yd += dy; + zd += dz; + } + } + else if (dy >= std::max(dx, dz)) // y dominant + { + int xd = dx - dy / 2; + int zd = dz - dy / 2; + + while (true) + { + RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); + + if (a_RelY1 == a_RelY2) + { + break; + } + + if (xd >= 0) // move along x + { + a_RelX1 += sx; + xd -= dy; + } + + if (zd >= 0) // move along z + { + a_RelZ1 += sz; + zd -= dy; + } + + // move along y + a_RelY1 += sy; + xd += dx; + zd += dz; + } + } + else + { + // z dominant + ASSERT(dz >= std::max(dx, dy)); + int xd = dx - dz / 2; + int yd = dy - dz / 2; + + while (true) + { + RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight); + + if (a_RelZ1 == a_RelZ2) + { + break; + } + + if (xd >= 0) // move along x + { + a_RelX1 += sx; + xd -= dz; + } + + if (yd >= 0) // move along y + { + a_RelY1 += sy; + yd -= dz; + } + + // move along z + a_RelZ1 += sz; + xd += dx; + yd += dy; + } + } // if (which dimension is dominant) +} + + + + + +void cBlockArea::RotateCCW(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to rotate, just use the NoMeta function + RotateCCWNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = m_SizeX - x - 1; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = z; + for (int y = 0; y < m_SizeY; y++) + { + int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; + int OldIdx = MakeIndex(x, y, z); + NewTypes[NewIdx] = m_BlockTypes[OldIdx]; + NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]); + } // for y + } // for z + } // for x + std::swap(m_BlockTypes, NewTypes); + std::swap(m_BlockMetas, NewMetas); + delete[] NewTypes; + delete[] NewMetas; + + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::RotateCW(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to rotate, just use the NoMeta function + RotateCWNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = x; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = m_SizeZ - z - 1; + for (int y = 0; y < m_SizeY; y++) + { + int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; + int OldIdx = MakeIndex(x, y, z); + NewTypes[NewIdx] = m_BlockTypes[OldIdx]; + NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]); + } // for y + } // for z + } // for x + std::swap(m_BlockTypes, NewTypes); + std::swap(m_BlockMetas, NewMetas); + delete[] NewTypes; + delete[] NewMetas; + + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::MirrorXY(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to mirror, just use the NoMeta function + MirrorXYNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: + int HalfZ = m_SizeZ / 2; + int MaxZ = m_SizeZ - 1; + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < HalfZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + int Idx1 = MakeIndex(x, y, z); + int Idx2 = MakeIndex(x, y, MaxZ - z); + std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); + NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]); + NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]); + m_BlockMetas[Idx1] = Meta2; + m_BlockMetas[Idx2] = Meta1; + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::MirrorXZ(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to mirror, just use the NoMeta function + MirrorXZNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: + int HalfY = m_SizeY / 2; + int MaxY = m_SizeY - 1; + for (int y = 0; y < HalfY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + int Idx1 = MakeIndex(x, y, z); + int Idx2 = MakeIndex(x, MaxY - y, z); + std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); + NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]); + NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]); + m_BlockMetas[Idx1] = Meta2; + m_BlockMetas[Idx2] = Meta1; + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::MirrorYZ(void) +{ + if (!HasBlockTypes()) + { + LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); + return; + } + + if (!HasBlockMetas()) + { + // There are no blockmetas to mirror, just use the NoMeta function + MirrorYZNoMeta(); + return; + } + + // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: + int HalfX = m_SizeX / 2; + int MaxX = m_SizeX - 1; + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < HalfX; x++) + { + int Idx1 = MakeIndex(x, y, z); + int Idx2 = MakeIndex(MaxX - x, y, z); + std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); + NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]); + NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]); + m_BlockMetas[Idx1] = Meta2; + m_BlockMetas[Idx2] = Meta1; + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::RotateCCWNoMeta(void) +{ + if (HasBlockTypes()) + { + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = m_SizeX - x - 1; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = z; + for (int y = 0; y < m_SizeY; y++) + { + NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; + } // for y + } // for z + } // for x + std::swap(m_BlockTypes, NewTypes); + delete[] NewTypes; + } + if (HasBlockMetas()) + { + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = m_SizeX - x - 1; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = z; + for (int y = 0; y < m_SizeY; y++) + { + NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; + } // for y + } // for z + } // for x + std::swap(m_BlockMetas, NewMetas); + delete[] NewMetas; + } + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::RotateCWNoMeta(void) +{ + if (HasBlockTypes()) + { + BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = m_SizeZ - z - 1; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = x; + for (int y = 0; y < m_SizeY; y++) + { + NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)]; + } // for y + } // for x + } // for z + std::swap(m_BlockTypes, NewTypes); + delete[] NewTypes; + } + if (HasBlockMetas()) + { + NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; + for (int z = 0; z < m_SizeZ; z++) + { + int NewX = m_SizeZ - z - 1; + for (int x = 0; x < m_SizeX; x++) + { + int NewZ = x; + for (int y = 0; y < m_SizeY; y++) + { + NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)]; + } // for y + } // for x + } // for z + std::swap(m_BlockMetas, NewMetas); + delete[] NewMetas; + } + std::swap(m_SizeX, m_SizeZ); +} + + + + + +void cBlockArea::MirrorXYNoMeta(void) +{ + int HalfZ = m_SizeZ / 2; + int MaxZ = m_SizeZ - 1; + if (HasBlockTypes()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < HalfZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]); + } // for x + } // for z + } // for y + } // if (HasBlockTypes) + + if (HasBlockMetas()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < HalfZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]); + } // for x + } // for z + } // for y + } // if (HasBlockMetas) +} + + + + + +void cBlockArea::MirrorXZNoMeta(void) +{ + int HalfY = m_SizeY / 2; + int MaxY = m_SizeY - 1; + if (HasBlockTypes()) + { + for (int y = 0; y < HalfY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockTypes) + + if (HasBlockMetas()) + { + for (int y = 0; y < HalfY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < m_SizeX; x++) + { + std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockMetas) +} + + + + + +void cBlockArea::MirrorYZNoMeta(void) +{ + int HalfX = m_SizeX / 2; + int MaxX = m_SizeX - 1; + if (HasBlockTypes()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < HalfX; x++) + { + std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockTypes) + + if (HasBlockMetas()) + { + for (int y = 0; y < m_SizeY; y++) + { + for (int z = 0; z < m_SizeZ; z++) + { + for (int x = 0; x < HalfX; x++) + { + std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]); + } // for x + } // for z + } // for y + } // if (HasBlockMetas) +} + + + + + +void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) +{ + if (m_BlockTypes == NULL) + { + LOGWARNING("cBlockArea: BlockTypes have not been read!"); + return; + } + m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType; +} + + + + + +void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +{ + SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType); +} + + + + + +void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) +{ + SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas); +} + + + + + +void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta) +{ + SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas); +} + + + + + +void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight) +{ + SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight); +} + + + + + +void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight) +{ + SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight); +} + + + + + +void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight) +{ + SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight); +} + + + + + +void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight) +{ + SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight); +} + + + + + +BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const +{ + if (m_BlockTypes == NULL) + { + LOGWARNING("cBlockArea: BlockTypes have not been read!"); + return E_BLOCK_AIR; + } + return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)]; +} + + + + + +BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ); +} + + + + + +NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const +{ + return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas); +} + + + + + +NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas); +} + + + + + +NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const +{ + return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight); +} + + + + + +NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight); +} + + + + + +NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const +{ + return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight); +} + + + + + +NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const +{ + return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight); +} + + + + + +void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta); +} + + + + + +void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + int idx = MakeIndex(a_RelX, a_RelY, a_RelZ); + if (m_BlockTypes == NULL) + { + LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__); + } + else + { + m_BlockTypes[idx] = a_BlockType; + } + if (m_BlockMetas == NULL) + { + LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__); + } + else + { + m_BlockMetas[idx] = a_BlockMeta; + } +} + + + + + +void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const +{ + return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta); +} + + + + + +void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const +{ + int idx = MakeIndex(a_RelX, a_RelY, a_RelZ); + if (m_BlockTypes == NULL) + { + LOGWARNING("cBlockArea: BlockTypes have not been read!"); + a_BlockType = E_BLOCK_AIR; + } + else + { + a_BlockType = m_BlockTypes[idx]; + } + + if (m_BlockMetas == NULL) + { + LOGWARNING("cBlockArea: BlockMetas have not been read!"); + a_BlockMeta = 0; + } + else + { + a_BlockMeta = m_BlockMetas[idx]; + } +} + + + + + +int cBlockArea::GetDataTypes(void) const +{ + int res = 0; + if (m_BlockTypes != NULL) + { + res |= baTypes; + } + if (m_BlockMetas != NULL) + { + res |= baMetas; + } + if (m_BlockLight != NULL) + { + res |= baLight; + } + if (m_BlockSkyLight != NULL) + { + res |= baSkyLight; + } + return res; +} + + + + + +bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes) +{ + ASSERT(m_BlockTypes == NULL); // Has been cleared + + if (a_DataTypes & baTypes) + { + m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockTypes == NULL) + { + return false; + } + } + if (a_DataTypes & baMetas) + { + m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockMetas == NULL) + { + delete[] m_BlockTypes; + return false; + } + } + if (a_DataTypes & baLight) + { + m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockLight == NULL) + { + delete[] m_BlockMetas; + delete[] m_BlockTypes; + return false; + } + } + if (a_DataTypes & baSkyLight) + { + m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ]; + if (m_BlockSkyLight == NULL) + { + delete[] m_BlockLight; + delete[] m_BlockMetas; + delete[] m_BlockTypes; + return false; + } + } + m_SizeX = a_SizeX; + m_SizeY = a_SizeY; + m_SizeZ = a_SizeZ; + return true; +} + + + + + +int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const +{ + ASSERT(a_RelX >= 0); + ASSERT(a_RelX < m_SizeX); + ASSERT(a_RelY >= 0); + ASSERT(a_RelY < m_SizeY); + ASSERT(a_RelZ >= 0); + ASSERT(a_RelZ < m_SizeZ); + + return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ; +} + + + + + +void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array) +{ + if (a_Array == NULL) + { + LOGWARNING("cBlockArea: datatype has not been read!"); + return; + } + a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value; +} + + + + + +void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array) +{ + SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array); +} + + + + + +NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const +{ + if (a_Array == NULL) + { + LOGWARNING("cBlockArea: datatype has not been read!"); + return 16; + } + return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)]; +} + + + + + +NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const +{ + return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBlockArea::cChunkReader: + +cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) : + m_Area(a_Area), + m_OriginX(a_Area.m_OriginX), + m_OriginY(a_Area.m_OriginY), + m_OriginZ(a_Area.m_OriginZ) +{ +} + + + + + +void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc) +{ + int SizeY = m_Area.m_SizeY; + int MinY = m_OriginY; + + // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union) + // OffX, OffZ are the offsets of the current chunk data from the area origin + // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders + int SizeX = cChunkDef::Width; + int SizeZ = cChunkDef::Width; + int OffX, OffZ; + int BaseX, BaseZ; + OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX; + if (OffX < 0) + { + BaseX = -OffX; + SizeX += OffX; // SizeX is decreased, OffX is negative + OffX = 0; + } + else + { + BaseX = 0; + } + OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ; + if (OffZ < 0) + { + BaseZ = -OffZ; + SizeZ += OffZ; // SizeZ is decreased, OffZ is negative + OffZ = 0; + } + else + { + BaseZ = 0; + } + // If the chunk extends beyond the area in the X or Z axis, cut off the Size: + if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX) + { + SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX); + } + if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ) + { + SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ); + } + + for (int y = 0; y < SizeY; y++) + { + int ChunkY = MinY + y; + int AreaY = y; + for (int z = 0; z < SizeZ; z++) + { + int ChunkZ = BaseZ + z; + int AreaZ = OffZ + z; + for (int x = 0; x < SizeX; x++) + { + int ChunkX = BaseX + x; + int AreaX = OffX + x; + a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ); + } // for x + } // for z + } // for y +} + + + + + +bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ) +{ + m_CurrentChunkX = a_ChunkX; + m_CurrentChunkZ = a_ChunkZ; + return true; +} + + + + + +void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes) +{ + if (m_Area.m_BlockTypes == NULL) + { + // Don't want BlockTypes + return; + } + + int SizeY = m_Area.m_SizeY; + int MinY = m_OriginY; + + // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union) + // OffX, OffZ are the offsets of the current chunk data from the area origin + // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders + int SizeX = cChunkDef::Width; + int SizeZ = cChunkDef::Width; + int OffX, OffZ; + int BaseX, BaseZ; + OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX; + if (OffX < 0) + { + BaseX = -OffX; + SizeX += OffX; // SizeX is decreased, OffX is negative + OffX = 0; + } + else + { + BaseX = 0; + } + OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ; + if (OffZ < 0) + { + BaseZ = -OffZ; + SizeZ += OffZ; // SizeZ is decreased, OffZ is negative + OffZ = 0; + } + else + { + BaseZ = 0; + } + // If the chunk extends beyond the area in the X or Z axis, cut off the Size: + if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX) + { + SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX); + } + if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ) + { + SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ); + } + + for (int y = 0; y < SizeY; y++) + { + int ChunkY = MinY + y; + int AreaY = y; + for (int z = 0; z < SizeZ; z++) + { + int ChunkZ = BaseZ + z; + int AreaZ = OffZ + z; + for (int x = 0; x < SizeX; x++) + { + int ChunkX = BaseX + x; + int AreaX = OffX + x; + m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ); + } // for x + } // for z + } // for y +} + + + + + +void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas) +{ + if (m_Area.m_BlockMetas == NULL) + { + // Don't want metas + return; + } + CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas); +} + + + + + +void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight) +{ + if (m_Area.m_BlockLight == NULL) + { + // Don't want light + return; + } + CopyNibbles(m_Area.m_BlockLight, a_BlockLight); +} + + + + + +void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) +{ + if (m_Area.m_BlockSkyLight == NULL) + { + // Don't want skylight + return; + } + CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight); +} + + + + + +void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) +{ + int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX; + int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY; + int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ; + BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ]; + int idx = 0; + for (int y = 0; y < NewSizeY; y++) + { + for (int z = 0; z < NewSizeZ; z++) + { + for (int x = 0; x < NewSizeX; x++) + { + int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ); + NewBlockTypes[idx++] = m_BlockTypes[OldIndex]; + } // for x + } // for z + } // for y + delete m_BlockTypes; + m_BlockTypes = NewBlockTypes; +} + + + + + +void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ) +{ + int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX; + int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY; + int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ; + NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ]; + int idx = 0; + for (int y = 0; y < NewSizeY; y++) + { + for (int z = 0; z < NewSizeZ; z++) + { + for (int x = 0; x < NewSizeX; x++) + { + NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)]; + } // for x + } // for z + } // for y + delete a_Array; + a_Array = NewNibbles; +} + + + + + +void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX; + int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY; + int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ; + int BlockCount = NewSizeX * NewSizeY * NewSizeZ; + BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount]; + memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE)); + int OldIndex = 0; + for (int y = 0; y < m_SizeY; y++) + { + int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ; + for (int z = 0; z < m_SizeZ; z++) + { + int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX; + int idx = IndexBaseZ + a_SubMinX; + for (int x = 0; x < m_SizeX; x++) + { + NewBlockTypes[idx++] = m_BlockTypes[OldIndex++]; + } // for x + } // for z + } // for y + delete m_BlockTypes; + m_BlockTypes = NewBlockTypes; +} + + + + + +void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ) +{ + int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX; + int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY; + int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ; + int BlockCount = NewSizeX * NewSizeY * NewSizeZ; + NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount]; + memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE)); + int OldIndex = 0; + for (int y = 0; y < m_SizeY; y++) + { + int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ; + for (int z = 0; z < m_SizeZ; z++) + { + int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX; + int idx = IndexBaseZ + a_SubMinX; + for (int x = 0; x < m_SizeX; x++) + { + NewNibbles[idx++] = a_Array[OldIndex++]; + } // for x + } // for z + } // for y + delete a_Array; + a_Array = NewNibbles; +} + + + + + +bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT) +{ + int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials"); + if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String)) + { + AString Materials = a_NBT.GetString(TMaterials); + if (Materials.compare("Alpha") != 0) + { + LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str()); + return false; + } + } + int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width"); + int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height"); + int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length"); + if ( + (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) || + (a_NBT.GetType(TSizeX) != TAG_Short) || + (a_NBT.GetType(TSizeY) != TAG_Short) || + (a_NBT.GetType(TSizeZ) != TAG_Short) + ) + { + LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)", + TSizeX, TSizeY, TSizeZ, + a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ) + ); + return false; + } + + int SizeX = a_NBT.GetShort(TSizeX); + int SizeY = a_NBT.GetShort(TSizeY); + int SizeZ = a_NBT.GetShort(TSizeZ); + if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1)) + { + LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ); + return false; + } + + int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks"); + int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data"); + if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray)) + { + LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes); + return false; + } + bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray); + + Clear(); + SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes); + + // Copy the block types and metas: + int NumBytes = m_SizeX * m_SizeY * m_SizeZ; + if (a_NBT.GetDataLength(TBlockTypes) < NumBytes) + { + LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.", + NumBytes, a_NBT.GetDataLength(TBlockTypes) + ); + NumBytes = a_NBT.GetDataLength(TBlockTypes); + } + memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes); + + if (AreMetasPresent) + { + int NumBytes = m_SizeX * m_SizeY * m_SizeZ; + if (a_NBT.GetDataLength(TBlockMetas) < NumBytes) + { + LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.", + NumBytes, a_NBT.GetDataLength(TBlockMetas) + ); + NumBytes = a_NBT.GetDataLength(TBlockMetas); + } + memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes); + } + + return true; +} + + + + +void cBlockArea::RelSetData( + int a_RelX, int a_RelY, int a_RelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight +) +{ + int Index = MakeIndex(a_RelX, a_RelY, a_RelZ); + if ((a_DataTypes & baTypes) != 0) + { + m_BlockTypes[Index] = a_BlockType; + } + if ((a_DataTypes & baMetas) != 0) + { + m_BlockMetas[Index] = a_BlockMeta; + } + if ((a_DataTypes & baLight) != 0) + { + m_BlockLight[Index] = a_BlockLight; + } + if ((a_DataTypes & baSkyLight) != 0) + { + m_BlockSkyLight[Index] = a_BlockSkyLight; + } +} + + + + diff --git a/src/BlockArea.h b/src/BlockArea.h new file mode 100644 index 000000000..075cc99ec --- /dev/null +++ b/src/BlockArea.h @@ -0,0 +1,310 @@ + +// BlockArea.h + +// Interfaces to the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries +// The object also supports writing the blockdata back into cWorld, even into other coords + +// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting! + + + + + +#pragma once + + + + + +// fwd: World.h +class cWorld; + +// fwd: FastNBT.h +class cParsedNBT; + + + + + +// tolua_begin +class cBlockArea +{ + // tolua_end + DISALLOW_COPY_AND_ASSIGN(cBlockArea); + // tolua_begin + +public: + + /// What data is to be queried (bit-mask) + enum + { + baTypes = 1, + baMetas = 2, + baLight = 4, + baSkyLight = 8, + } ; + + enum eMergeStrategy + { + msOverwrite, + msFillAir, + msImprint, + msLake, + } ; + + cBlockArea(void); + ~cBlockArea(); + + /// Clears the data stored to reclaim memory + void Clear(void); + + /** Creates a new area of the specified size and contents. + Origin is set to all zeroes. + BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. + */ + void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas); + + /// Resets the origin. No other changes are made, contents are untouched. + void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ); + + /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive. + bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas); + + // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write + // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again + + /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all + bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas); + + /// Copies this object's contents into the specified BlockArea. + void CopyTo(cBlockArea & a_Into) const; + + /// Copies the contents from the specified BlockArea into this object. + void CopyFrom(const cBlockArea & a_From); + + /// For testing purposes only, dumps the area into a file. + void DumpToRawFile(const AString & a_FileName); + + /// Loads an area from a .schematic file. Returns true if successful + bool LoadFromSchematicFile(const AString & a_FileName); + + /// Saves the area into a .schematic file. Returns true if successful + bool SaveToSchematicFile(const AString & a_FileName); + + /// Crops the internal contents by the specified amount of blocks from each border. + void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + + /// Expands the internal contents by the specified amount of blocks from each border + void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /** Merges another block area into this one, using the specified block combinating strategy + This function combines another BlockArea into the current object. + The strategy parameter specifies how individual blocks are combined together, using the table below. + + | area block | result | + | this | Src | msOverwrite | msFillAir | msImprint | + +------+-----+-------------+-----------+-----------+ + | air | air | air | air | air | + | A | air | air | A | A | + | air | B | B | B | B | + | A | B | B | A | B | + + So to sum up: + - msOverwrite completely overwrites all blocks with the Src's blocks + - msFillAir overwrites only those blocks that were air + - msImprint overwrites with only those blocks that are non-air + + Special strategies: + msLake (evaluate top-down, first match wins): + | area block | | + | this | Src | result | + +----------+--------+--------+ + | A | sponge | A | Sponge is the NOP block + | * | air | air | Air always gets hollowed out, even under the oceans + | water | * | water | Water is never overwritten + | lava | * | lava | Lava is never overwritten + | * | water | water | Water always overwrites anything + | * | lava | lava | Lava always overwrites anything + | dirt | stone | stone | Stone overwrites dirt + | grass | stone | stone | ... and grass + | mycelium | stone | stone | ... and mycelium + | A | stone | A | ... but nothing else + | A | * | A | Everything else is left as it is + + */ + void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); + + /// Fills the entire block area with the specified data + void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f); + + /// Fills a cuboid inside the block area with the specified data + void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /// Draws a line from between two points with the specified data + void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /// Rotates the entire area counter-clockwise around the Y axis + void RotateCCW(void); + + /// Rotates the entire area clockwise around the Y axis + void RotateCW(void); + + /// Mirrors the entire area around the XY plane + void MirrorXY(void); + + /// Mirrors the entire area around the XZ plane + void MirrorXZ(void); + + /// Mirrors the entire area around the YZ plane + void MirrorYZ(void); + + /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta + void RotateCCWNoMeta(void); + + /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta + void RotateCWNoMeta(void); + + /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta + void MirrorXYNoMeta(void); + + /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta + void MirrorXZNoMeta(void); + + /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta + void MirrorYZNoMeta(void); + + // Setters: + void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); + void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta); + void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight); + void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight); + void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight); + void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight); + + // Getters: + BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const; + BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; + + void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + + int GetSizeX(void) const { return m_SizeX; } + int GetSizeY(void) const { return m_SizeY; } + int GetSizeZ(void) const { return m_SizeZ; } + + int GetOriginX(void) const { return m_OriginX; } + int GetOriginY(void) const { return m_OriginY; } + int GetOriginZ(void) const { return m_OriginZ; } + + /// Returns the datatypes that are stored in the object (bitmask of baXXX values) + int GetDataTypes(void) const; + + bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); } + bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); } + bool HasBlockLights (void) const { return (m_BlockLight != NULL); } + bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); } + + // tolua_end + + // Clients can use these for faster access to all blocktypes. Be careful though! + /// Returns the internal pointer to the block types + BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; } + NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! + NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! + NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block! + int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; } + int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const; + +protected: + friend class cChunkDesc; + + class cChunkReader : + public cChunkDataCallback + { + public: + cChunkReader(cBlockArea & a_Area); + + protected: + cBlockArea & m_Area; + int m_OriginX; + int m_OriginY; + int m_OriginZ; + int m_CurrentChunkX; + int m_CurrentChunkZ; + + void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc); + + // cChunkDataCallback overrides: + virtual bool Coords (int a_ChunkX, int a_ChunkZ) override; + virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override; + virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override; + virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override; + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override; + } ; + + typedef NIBBLETYPE * NIBBLEARRAY; + + + int m_OriginX; + int m_OriginY; + int m_OriginZ; + int m_SizeX; + int m_SizeY; + int m_SizeZ; + + BLOCKTYPE * m_BlockTypes; + NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access + NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access + NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access + + /// Clears the data stored and prepares a fresh new block area with the specified dimensions + bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes); + + // Basic Setters: + void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); + void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); + + // Basic Getters: + NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const; + NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const; + + // Crop helpers: + void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + + // Expand helpers: + void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. + bool LoadFromSchematicNBT(cParsedNBT & a_NBT); + + /// Sets the specified datatypes at the specified location. + void RelSetData( + int a_RelX, int a_RelY, int a_RelZ, + int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight + ); + // tolua_begin +} ; +// tolua_end + + + + diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp new file mode 100644 index 000000000..41a488717 --- /dev/null +++ b/src/BlockEntities/BlockEntity.cpp @@ -0,0 +1,44 @@ + +// BlockEntity.cpp + +// Implements the cBlockEntity class that is the common ancestor for all block entities + +#include "Globals.h" +#include "BlockEntity.h" +#include "ChestEntity.h" +#include "DispenserEntity.h" +#include "DropperEntity.h" +#include "FurnaceEntity.h" +#include "HopperEntity.h" +#include "JukeboxEntity.h" +#include "NoteEntity.h" +#include "SignEntity.h" + + + + + +cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) +{ + switch (a_BlockType) + { + case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_DISPENSER: return new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + } + LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)", + __FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str() + ); + return NULL; +} + + + + diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h new file mode 100644 index 000000000..0d358b556 --- /dev/null +++ b/src/BlockEntities/BlockEntity.h @@ -0,0 +1,106 @@ + +#pragma once + +#include "../ClientHandle.h" +#include "../World.h" + + + + + +namespace Json +{ + class Value; +}; + +class cPlayer; +class cPacket; + + + + + +// tolua_begin +class cBlockEntity +{ +protected: + cBlockEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + m_PosX(a_BlockX), + m_PosY(a_BlockY), + m_PosZ(a_BlockZ), + m_RelX(a_BlockX - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockX, cChunkDef::Width)), + m_RelZ(a_BlockZ - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockZ, cChunkDef::Width)), + m_BlockType(a_BlockType), + m_World(a_World) + { + } + +public: + // tolua_end + + virtual ~cBlockEntity() {}; // force a virtual destructor in all descendants + + virtual void Destroy(void) {}; + + void SetWorld(cWorld * a_World) + { + m_World = a_World; + } + + /// Creates a new block entity for the specified block type + /// If a_World is valid, then the entity is created bound to that world + /// Returns NULL for unknown block types + static cBlockEntity * CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World = NULL); + + static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates + { + return "cBlockEntity"; + } + + // tolua_begin + + // Position, in absolute block coordinates: + int GetPosX(void) const { return m_PosX; } + int GetPosY(void) const { return m_PosY; } + int GetPosZ(void) const { return m_PosZ; } + + BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + + cWorld * GetWorld(void) const {return m_World; } + + int GetChunkX(void) const { return FAST_FLOOR_DIV(m_PosX, cChunkDef::Width); } + int GetChunkZ(void) const { return FAST_FLOOR_DIV(m_PosZ, cChunkDef::Width); } + + int GetRelX(void) const { return m_RelX; } + int GetRelZ(void) const { return m_RelZ; } + + // tolua_end + + virtual void SaveToJson (Json::Value & a_Value) = 0; + + /// Called when a player uses this entity; should open the UI window + virtual void UsedBy( cPlayer * a_Player ) = 0; + + /** Sends the packet defining the block entity to the client specified. + To send to all eligible clients, use cWorld::BroadcastBlockEntity() + */ + virtual void SendTo(cClientHandle & a_Client) = 0; + + /// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing. + virtual bool Tick(float a_Dt, cChunk & a_Chunk) { return false; } + +protected: + /// Position in absolute block coordinates + int m_PosX, m_PosY, m_PosZ; + + /// Position relative to the chunk, used to speed up ticking + int m_RelX, m_RelZ; + + BLOCKTYPE m_BlockType; + + cWorld * m_World; +} ; // tolua_export + + + + diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h new file mode 100644 index 000000000..0846ae17e --- /dev/null +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -0,0 +1,86 @@ + +// BlockEntityWithItems.h + +// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid + + + + + +#pragma once + +#include "BlockEntity.h" +#include "../ItemGrid.h" + + + + + +// tolua_begin +class cBlockEntityWithItems : + public cBlockEntity + // tolua_end + // tolua doesn't seem to support multiple inheritance? + , public cItemGrid::cListener + // tolua_begin +{ + typedef cBlockEntity super; + +public: + // tolua_end + + cBlockEntityWithItems( + BLOCKTYPE a_BlockType, // Type of the block that the entity represents + int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity + int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid + cWorld * a_World // Optional world to assign to the entity + ) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Contents(a_ItemGridWidth, a_ItemGridHeight) + { + m_Contents.AddListener(*this); + } + + virtual void Destroy(void) override + { + // Drop the contents as pickups: + ASSERT(m_World != NULL); + cItems Pickups; + m_Contents.CopyToItems(Pickups); + m_Contents.Clear(); + m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); + } + + // tolua_begin + + const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); } + const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); } + + void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); } + void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); } + + /// Returns the ItemGrid used for storing the contents + cItemGrid & GetContents(void) { return m_Contents; } + + // tolua_end + + /// Const version of the GetContents() function for C++ type-safety + const cItemGrid & GetContents(void) const { return m_Contents; } + +protected: + cItemGrid m_Contents; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + { + ASSERT(a_Grid == &m_Contents); + if (m_World != NULL) + { + m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); + } + } +} ; // tolua_export + + + + diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp new file mode 100644 index 000000000..0bde5c9f2 --- /dev/null +++ b/src/BlockEntities/ChestEntity.cpp @@ -0,0 +1,172 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChestEntity.h" +#include "../Item.h" +#include "../Entities/Player.h" +#include "../UI/Window.h" +#include "lib/jsoncpp/include/json/json.h" + + + + + +cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) +{ + cBlockEntityWindowOwner::SetBlockEntity(this); +} + + + + + +cChestEntity::~cChestEntity() +{ + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +bool cChestEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Item; + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); + SlotIdx++; + } + return true; +} + + + + + +void cChestEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cChestEntity::SendTo(cClientHandle & a_Client) +{ + // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the chest is rclked + + UNUSED(a_Client); +} + + + + + +void cChestEntity::UsedBy(cPlayer * a_Player) +{ + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +void cChestEntity::OpenNewWindow(void) +{ + // Callback for opening together with neighbor chest: + class cOpenDouble : + public cChestCallback + { + cChestEntity * m_ThisChest; + public: + cOpenDouble(cChestEntity * a_ThisChest) : + m_ThisChest(a_ThisChest) + { + } + + virtual bool Item(cChestEntity * a_Chest) override + { + // The primary chest should eb the one with lesser X or Z coord: + cChestEntity * Primary = a_Chest; + cChestEntity * Secondary = m_ThisChest; + if ( + (Primary->GetPosX() > Secondary->GetPosX()) || + (Primary->GetPosZ() > Secondary->GetPosZ()) + ) + { + std::swap(Primary, Secondary); + } + m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary)); + return false; + } + } ; + + // Scan neighbors for adjacent chests: + cOpenDouble OpenDbl(this); + if ( + m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) || + m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) || + m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ - 1, OpenDbl) || + m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ + 1, OpenDbl) + ) + { + // The double-chest window has been opened in the callback + return; + } + + // There is no chest neighbor, open a single-chest window: + OpenWindow(new cChestWindow(this)); +} + + + + diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h new file mode 100644 index 000000000..4f2c21e91 --- /dev/null +++ b/src/BlockEntities/ChestEntity.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cServer; +class cNBTData; + + + + + +class cChestEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 3, + ContentsWidth = 9, + } ; + + // tolua_end + + /// Constructor used for normal operation + cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + virtual ~cChestEntity(); + + static const char * GetClassStatic(void) { return "cChestEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + void OpenNewWindow(void); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp new file mode 100644 index 000000000..374f3d6e3 --- /dev/null +++ b/src/BlockEntities/DispenserEntity.cpp @@ -0,0 +1,215 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "DispenserEntity.h" +#include "../Entities/Player.h" +#include "../Simulator/FluidSimulator.h" +#include "../Chunk.h" + + + + + +cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + int DispX = m_RelX; + int DispY = m_PosY; + int DispZ = m_RelZ; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + AddDropSpenserDir(DispX, DispY, DispZ, Meta); + cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ); + if (DispChunk == NULL) + { + // Would dispense into / interact with a non-loaded chunk, ignore the tick + return; + } + BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ); + + // Dispense the item: + switch (m_Contents.GetSlot(a_SlotNum).m_ItemType) + { + case E_ITEM_BUCKET: + { + LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + switch (DispBlock) + { + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); + } + break; + } + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_LAVA: + { + if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); + } + break; + } + default: + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + } + break; + } // E_ITEM_BUCKET + + case E_ITEM_WATER_BUCKET: + { + LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + if (EmptyLiquidBucket(DispBlock, a_SlotNum)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0); + } + else + { + DropFromSlot(a_Chunk, a_SlotNum); + } + break; + } + + case E_ITEM_LAVA_BUCKET: + { + LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + if (EmptyLiquidBucket(DispBlock, a_SlotNum)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0); + } + else + { + DropFromSlot(a_Chunk, a_SlotNum); + } + break; + } + + case E_ITEM_SPAWN_EGG: + { + double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); + double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); + if (m_World->SpawnMob(MobX, DispY, MobZ, (cMonster::eType)m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_BLOCK_TNT: + { + // Spawn a primed TNT entity, if space allows: + if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + { + double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); + double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); + m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_ITEM_FLINT_AND_STEEL: + { + // Spawn fire if the block in front is air. + if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0); + m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1); + // If the durability has run out destroy the item. + if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + } + break; + } + + default: + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + } // switch (ItemType) +} + + + + + + +bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType) +{ + cItem LiquidBucket(a_BucketItemType, 1); + if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) + { + // Special case: replacing one empty bucket with one full bucket + m_Contents.SetSlot(a_SlotNum, LiquidBucket); + return true; + } + + // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else + if (m_Contents.HowManyCanFit(LiquidBucket) < 1) + { + // Cannot fit into m_Contents + return false; + } + + m_Contents.ChangeSlotCount(a_SlotNum, -1); + m_Contents.AddItem(LiquidBucket); + return true; +} + + + + + +bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum) +{ + if ( + (a_BlockInFront != E_BLOCK_AIR) && + !IsBlockLiquid(a_BlockInFront) && + !cFluidSimulator::CanWashAway(a_BlockInFront) + ) + { + // Not a suitable block in front + return false; + } + + cItem EmptyBucket(E_ITEM_BUCKET, 1); + if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) + { + // Change the single full bucket present into a single empty bucket + m_Contents.SetSlot(a_SlotNum, EmptyBucket); + return true; + } + + // There are full buckets stacked at this slot, check if we can fit in the empty bucket + if (m_Contents.HowManyCanFit(EmptyBucket) < 1) + { + // The empty bucket wouldn't fit into m_Contents + return false; + } + + // The empty bucket fits in, remove one full bucket and add the empty one + m_Contents.ChangeSlotCount(a_SlotNum, -1); + m_Contents.AddItem(EmptyBucket); + return true; +} + + + + diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h new file mode 100644 index 000000000..fdfe4e5b4 --- /dev/null +++ b/src/BlockEntities/DispenserEntity.h @@ -0,0 +1,38 @@ + +#pragma once + +#include "DropSpenserEntity.h" + + + + + +// tolua_begin +class cDispenserEntity : + public cDropSpenserEntity +{ + typedef cDropSpenserEntity super; + +public: + + // tolua_end + + /// Constructor used for normal operation + cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cDispenserEntity"; } + +private: + // cDropSpenser overrides: + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; + + /// If such a bucket can fit, adds it to m_Contents and returns true + bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType); + + /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true + bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp new file mode 100644 index 000000000..823ed598f --- /dev/null +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -0,0 +1,266 @@ + +// DropSpenserEntity.cpp + +// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity +// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior + +#include "Globals.h" +#include "DropSpenserEntity.h" +#include "../Entities/Player.h" +#include "../Chunk.h" + + + + + +cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_ShouldDropSpense(false), + m_IsPowered(false) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cDropSpenserEntity::~cDropSpenserEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction) +{ + switch (a_Direction) + { + case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return; + case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return; + case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return; + case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return; + case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return; + case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return; + } + LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction); + return; +} + + + + + +void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) +{ + // Pick one of the occupied slots: + int OccupiedSlots[9]; + int SlotsCnt = 0; + for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + { + if (!m_Contents.GetSlot(i).IsEmpty()) + { + OccupiedSlots[SlotsCnt] = i; + SlotsCnt++; + } + } // for i - m_Contents[] + + if (SlotsCnt == 0) + { + // Nothing in the dropspenser, play the click sound + m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f); + return; + } + + int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1); + + // DropSpense the item, using the specialized behavior in the subclasses: + DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]); + + // Broadcast a smoke and click effects: + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + int SmokeDir = 0; + switch (Meta) + { + case E_META_DROPSPENSER_FACING_YP: SmokeDir = 4; break; // YP & YM don't have associated smoke dirs, just do 4 (centre of block) + case E_META_DROPSPENSER_FACING_YM: SmokeDir = 4; break; + case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break; + case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break; + case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break; + case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break; + } + m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); + m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); + + // Update the UI window, if open: + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastWholeWindow(); + } +} + + + + + +void cDropSpenserEntity::Activate(void) +{ + m_ShouldDropSpense = true; +} + + + + + +void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) +{ + if (a_IsPowered && !m_IsPowered) + { + Activate(); + } + m_IsPowered = a_IsPowered; +} + + + + + +bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (!m_ShouldDropSpense) + { + return false; + } + + m_ShouldDropSpense = false; + DropSpense(a_Chunk); + return true; +} + + + + + +bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Contents; + Contents.FromJson(*itr); + m_Contents.SetSlot(SlotIdx, Contents); + SlotIdx++; + if (SlotIdx >= m_Contents.GetNumSlots()) + { + return true; + } + } + + return true; +} + + + + + +void cDropSpenserEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + int NumSlots = m_Contents.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cDropSpenserEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cDropSpenserEntity::UsedBy(cPlayer * a_Player) +{ + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this)); + Window = GetWindow(); + } + + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } +} + + + + + +void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + int DispX = m_PosX; + int DispY = m_PosY; + int DispZ = m_PosZ; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + AddDropSpenserDir(DispX, DispY, DispZ, Meta); + + cItems Pickups; + Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); + + const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2; // At least 2, at most 6 + int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0; + switch (Meta) + { + case E_META_DROPSPENSER_FACING_YP: PickupSpeedY = PickupSpeed; break; + case E_META_DROPSPENSER_FACING_YM: PickupSpeedY = -PickupSpeed; break; + case E_META_DROPSPENSER_FACING_XM: PickupSpeedX = -PickupSpeed; break; + case E_META_DROPSPENSER_FACING_XP: PickupSpeedX = PickupSpeed; break; + case E_META_DROPSPENSER_FACING_ZM: PickupSpeedZ = -PickupSpeed; break; + case E_META_DROPSPENSER_FACING_ZP: PickupSpeedZ = PickupSpeed; break; + } + + double MicroX, MicroY, MicroZ; + MicroX = DispX + 0.5; + MicroY = DispY + 0.4; // Slightly less than half, to accomodate actual texture hole on DropSpenser + MicroZ = DispZ + 0.5; + + + m_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ, PickupSpeedX, PickupSpeedY, PickupSpeedZ); +} + + + + diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h new file mode 100644 index 000000000..0e9039915 --- /dev/null +++ b/src/BlockEntities/DropSpenserEntity.h @@ -0,0 +1,89 @@ + +// DropSpenser.h + +// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity +// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +} + +class cClientHandle; +class cServer; + + + + + +class cDropSpenserEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 3, + ContentsWidth = 3, + } ; + + // tolua_end + + cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cDropSpenserEntity(); + + static const char * GetClassStatic(void) { return "cDropSpenserEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + // tolua_begin + + /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize) + void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction); + + /// Sets the dropspenser to dropspense an item in the next tick + void Activate(void); + + /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate + void SetRedstonePower(bool a_IsPowered); + + // tolua_end + +protected: + bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick + bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power. + + /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects + void DropSpense(cChunk & a_Chunk); + + /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...) + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0; + + /// Helper function, drops one item from the specified slot (like a dropper) + void DropFromSlot(cChunk & a_Chunk, int a_SlotNum); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/DropperEntity.cpp b/src/BlockEntities/DropperEntity.cpp new file mode 100644 index 000000000..5d4a8ad97 --- /dev/null +++ b/src/BlockEntities/DropperEntity.cpp @@ -0,0 +1,32 @@ + +// DropperEntity.cpp + +// Implements the cRtopperEntity class representing a Dropper block entity + +#include "Globals.h" +#include "DropperEntity.h" +#include "../Entities/Player.h" +#include "../Simulator/FluidSimulator.h" + + + + + +cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + DropFromSlot(a_Chunk, a_SlotNum); +} + + + + diff --git a/src/BlockEntities/DropperEntity.h b/src/BlockEntities/DropperEntity.h new file mode 100644 index 000000000..8e07bc6f8 --- /dev/null +++ b/src/BlockEntities/DropperEntity.h @@ -0,0 +1,46 @@ + +// DropperEntity.h + +// Declares the cDropperEntity class representing a dropper block entity + + + + + +#pragma once + +#include "DropSpenserEntity.h" + + + + + +// tolua_begin +class cDropperEntity : + public cDropSpenserEntity +{ + typedef cDropSpenserEntity super; + +public: + + // tolua_end + + /// Constructor used for normal operation + cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cDropperEntity"; } + +protected: + // cDropSpenserEntity overrides: + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; + + /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper. + Called when there's a container directly in front of the dropper, + so the dropper should store items there, rather than dropping. + */ + void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp new file mode 100644 index 000000000..d49e4f53b --- /dev/null +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -0,0 +1,479 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceEntity.h" +#include "../UI/Window.h" +#include "../Entities/Player.h" +#include "../Root.h" +#include "../Chunk.h" +#include "lib/jsoncpp/include/json/json.h" + + + + + + +enum +{ + PROGRESSBAR_SMELTING = 0, + PROGRESSBAR_FUEL = 1, +} ; + + + + + +cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World) : + super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_CurrentRecipe(NULL), + m_IsCooking((a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_LIT_FURNACE)), + m_NeedCookTime(0), + m_TimeCooked(0), + m_FuelBurnTime(0), + m_TimeBurned(0), + m_LastProgressFuel(0), + m_LastProgressCook(0) +{ + cBlockEntityWindowOwner::SetBlockEntity(this); + m_Contents.AddListener(*this); +} + + + + + +cFurnaceEntity::~cFurnaceEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cFurnaceEntity::UsedBy(cPlayer * a_Player) +{ + if (GetWindow() == NULL) + { + OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); + } + cWindow * Window = GetWindow(); + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + BroadcastProgress(PROGRESSBAR_FUEL, m_LastProgressFuel); + BroadcastProgress(PROGRESSBAR_SMELTING, m_LastProgressCook); + } + } +} + + + + + +/// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. +bool cFurnaceEntity::ContinueCooking(void) +{ + UpdateInput(); + UpdateFuel(); + return m_IsCooking; +} + + + + + +bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_FuelBurnTime <= 0) + { + // No fuel is burning, reset progressbars and bail out + if ((m_LastProgressCook > 0) || (m_LastProgressFuel > 0)) + { + UpdateProgressBars(); + } + return false; + } + + if (m_IsCooking) + { + m_TimeCooked++; + if (m_TimeCooked >= m_NeedCookTime) + { + // Finished smelting one item + FinishOne(a_Chunk); + } + } + + m_TimeBurned++; + if (m_TimeBurned >= m_FuelBurnTime) + { + // The current fuel has been exhausted, use another one, if possible + BurnNewFuel(); + } + + UpdateProgressBars(); + + return true; +} + + + + + +bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Item; + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); + SlotIdx++; + } + + m_NeedCookTime = (int)(a_Value.get("CookTime", 0).asDouble() / 50); + m_TimeCooked = (int)(a_Value.get("TimeCooked", 0).asDouble() / 50); + m_FuelBurnTime = (int)(a_Value.get("BurnTime", 0).asDouble() / 50); + m_TimeBurned = (int)(a_Value.get("TimeBurned", 0).asDouble() / 50); + + return true; +} + + + + + +void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + int NumSlots = m_Contents.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; + + a_Value["CookTime"] = m_NeedCookTime * 50; + a_Value["TimeCooked"] = m_TimeCooked * 50; + a_Value["BurnTime"] = m_FuelBurnTime * 50; + a_Value["TimeBurned"] = m_TimeBurned * 50; +} + + + + + +void cFurnaceEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) +{ + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastProgress(a_ProgressbarID, a_Value); + } +} + + + + + +/// One item finished cooking +void cFurnaceEntity::FinishOne(cChunk & a_Chunk) +{ + m_TimeCooked = 0; + + if (m_Contents.GetSlot(fsOutput).IsEmpty()) + { + m_Contents.SetSlot(fsOutput, *m_CurrentRecipe->Out); + } + else + { + m_Contents.ChangeSlotCount(fsOutput, m_CurrentRecipe->Out->m_ItemCount); + } + m_Contents.ChangeSlotCount(fsInput, -m_CurrentRecipe->In->m_ItemCount); + + UpdateIsCooking(); +} + + + + + +void cFurnaceEntity::BurnNewFuel(void) +{ + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel)); + if (NewTime == 0) + { + // The item in the fuel slot is not suitable + m_FuelBurnTime = 0; + m_TimeBurned = 0; + SetIsCooking(false); + return; + } + + // Is the input and output ready for cooking? + if (!CanCookInputToOutput()) + { + return; + } + + // Burn one new fuel: + m_FuelBurnTime = NewTime; + m_TimeBurned = 0; + SetIsCooking(true); + if (m_Contents.GetSlot(fsFuel).m_ItemType == E_ITEM_LAVA_BUCKET) + { + m_Contents.SetSlot(fsFuel, cItem(E_ITEM_BUCKET)); + } + else + { + m_Contents.ChangeSlotCount(fsFuel, -1); + } +} + + + + + +void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + super::OnSlotChanged(a_ItemGrid, a_SlotNum); + + if (m_World == NULL) + { + // The furnace isn't initialized yet, do no processing + return; + } + + ASSERT(a_ItemGrid == &m_Contents); + switch (a_SlotNum) + { + case fsInput: + { + UpdateInput(); + break; + } + + case fsFuel: + { + UpdateFuel(); + break; + } + + case fsOutput: + { + UpdateOutput(); + break; + } + } +} + + + + + + +/// Updates the current recipe, based on the current input +void cFurnaceEntity::UpdateInput(void) +{ + if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput)) + { + // The input is different from what we had before, reset the cooking time + m_TimeCooked = 0; + } + m_LastInput = m_Contents.GetSlot(fsInput); + + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + m_CurrentRecipe = FR->GetRecipeFrom(m_Contents.GetSlot(fsInput)); + if (!CanCookInputToOutput()) + { + // This input cannot be cooked + m_NeedCookTime = 0; + SetIsCooking(false); + } + else + { + m_NeedCookTime = m_CurrentRecipe->CookTime; + SetIsCooking(true); + + // Start burning new fuel if there's no flame now: + if (GetFuelBurnTimeLeft() <= 0) + { + BurnNewFuel(); + } + } +} + + + + + +/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate +void cFurnaceEntity::UpdateFuel(void) +{ + if (m_FuelBurnTime > m_TimeBurned) + { + // The current fuel is still burning, don't modify anything: + return; + } + + // The current fuel is spent, try to burn some more: + BurnNewFuel(); +} + + + + + +/// Called when the output slot changes; starts burning if space became available +void cFurnaceEntity::UpdateOutput(void) +{ + if (!CanCookInputToOutput()) + { + // Cannot cook anymore: + m_TimeCooked = 0; + m_NeedCookTime = 0; + SetIsCooking(false); + return; + } + + // No need to burn new fuel, the Tick() function will take care of that + + // Can cook, start cooking if not already underway: + m_NeedCookTime = m_CurrentRecipe->CookTime; + SetIsCooking(m_FuelBurnTime > 0); +} + + + + + +/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned +void cFurnaceEntity::UpdateIsCooking(void) +{ + if ( + !CanCookInputToOutput() || // Cannot cook this + (m_FuelBurnTime <= 0) || // No fuel + (m_TimeBurned >= m_FuelBurnTime) // Fuel burnt out + ) + { + // Reset everything + SetIsCooking(false); + m_TimeCooked = 0; + m_NeedCookTime = 0; + return; + } + + SetIsCooking(true); +} + + + + + +/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation +bool cFurnaceEntity::CanCookInputToOutput(void) const +{ + if (m_CurrentRecipe == NULL) + { + // This input cannot be cooked + return false; + } + + if (m_Contents.GetSlot(fsOutput).IsEmpty()) + { + // The output is empty, can cook + return true; + } + + if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out)) + { + // The output slot is blocked with something that cannot be stacked with the recipe's output + return false; + } + + if (m_Contents.GetSlot(fsOutput).IsFullStack()) + { + // Cannot add any more items to the output slot + return false; + } + + return true; +} + + + + + +/// Broadcasts progressbar updates, if needed +void cFurnaceEntity::UpdateProgressBars(void) +{ + // In order to preserve bandwidth, an update is sent only every 10th tick + // That's why the comparisons use the division by eight + + int CurFuel = (m_FuelBurnTime > 0) ? (200 - 200 * m_TimeBurned / m_FuelBurnTime) : 0; + if ((CurFuel / 8) != (m_LastProgressFuel / 8)) + { + BroadcastProgress(PROGRESSBAR_FUEL, CurFuel); + m_LastProgressFuel = CurFuel; + } + + int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0; + if ((CurCook / 8) != (m_LastProgressCook / 8)) + { + BroadcastProgress(PROGRESSBAR_SMELTING, CurCook); + m_LastProgressCook = CurCook; + } +} + + + + + +void cFurnaceEntity::SetIsCooking(bool a_IsCooking) +{ + if (a_IsCooking == m_IsCooking) + { + return; + } + + m_IsCooking = a_IsCooking; + + // Light or extinguish the furnace: + m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, m_IsCooking ? E_BLOCK_LIT_FURNACE : E_BLOCK_FURNACE, m_BlockMeta); +} + + + + diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h new file mode 100644 index 000000000..9464fd175 --- /dev/null +++ b/src/BlockEntities/FurnaceEntity.h @@ -0,0 +1,164 @@ + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" +#include "../FurnaceRecipe.h" + + + + + +namespace Json +{ + class Value; +} + +class cClientHandle; +class cServer; + + + + + +class cFurnaceEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum + { + fsInput = 0, // Input slot number + fsFuel = 1, // Fuel slot number + fsOutput = 2, // Output slot number + + ContentsWidth = 3, + ContentsHeight = 1, + }; + + // tolua_end + + /// Constructor used for normal operation + cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World); + + virtual ~cFurnaceEntity(); + + static const char * GetClassStatic() { return "cFurnaceEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. + bool ContinueCooking(void); + + void ResetCookTimer(); + + // tolua_begin + + /// Returns the item in the input slot + const cItem & GetInputSlot(void) const { return GetSlot(fsInput); } + + /// Returns the item in the fuel slot + const cItem & GetFuelSlot(void) const { return GetSlot(fsFuel); } + + /// Returns the item in the output slot + const cItem & GetOutputSlot(void) const { return GetSlot(fsOutput); } + + /// Sets the item in the input slot + void SetInputSlot(const cItem & a_Item) { SetSlot(fsInput, a_Item); } + + /// Sets the item in the fuel slot + void SetFuelSlot(const cItem & a_Item) { SetSlot(fsFuel, a_Item); } + + /// Sets the item in the output slot + void SetOutputSlot(const cItem & a_Item) { SetSlot(fsOutput, a_Item); } + + /// Returns the time that the current item has been cooking, in ticks + int GetTimeCooked(void) const {return m_TimeCooked; } + + /// Returns the time until the current item finishes cooking, in ticks + int GetCookTimeLeft(void) const { return m_NeedCookTime - m_TimeCooked; } + + /// Returns the time until the current fuel is depleted, in ticks + int GetFuelBurnTimeLeft(void) const {return m_FuelBurnTime - m_TimeBurned; } + + /// Returns true if there's time left before the current fuel is depleted + bool HasFuelTimeLeft(void) const { return (GetFuelBurnTimeLeft() > 0); } + + // tolua_end + + void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = 0; } + void SetCookTimes(int a_NeedCookTime, int a_TimeCooked) {m_NeedCookTime = a_NeedCookTime; m_TimeCooked = a_TimeCooked; } + +protected: + + /// Block type of the block currently represented by this entity (changes when furnace lights up) + BLOCKTYPE m_BlockType; + + /// Block meta of the block currently represented by this entity + NIBBLETYPE m_BlockMeta; + + /// The recipe for the current input slot + const cFurnaceRecipe::Recipe * m_CurrentRecipe; + + /// The item that is being smelted + cItem m_LastInput; + + bool m_IsCooking; ///< Set to true if the furnace is cooking an item + + // All timers are in ticks + int m_NeedCookTime; ///< Amount of time needed to fully cook current item + int m_TimeCooked; ///< Amount of time that the current item has been cooking + int m_FuelBurnTime; ///< Amount of time that the current fuel can burn (in total); zero if no fuel burning + int m_TimeBurned; ///< Amount of time that the current fuel has been burning + + int m_LastProgressFuel; ///< Last value sent as the progress for the fuel + int m_LastProgressCook; ///< Last value sent as the progress for the cooking + + + /// Sends the specified progressbar value to all clients of the window + void BroadcastProgress(int a_ProgressbarID, short a_Value); + + /// One item finished cooking + void FinishOne(cChunk & a_Chunk); + + /// Starts burning a new fuel, if possible + void BurnNewFuel(void); + + /// Updates the recipe, based on the current input + void UpdateInput(void); + + /// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate + void UpdateFuel(void); + + /// Called when the output slot changes + void UpdateOutput(void); + + /// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned + void UpdateIsCooking(void); + + /// Returns true if the input can be cooked into output and the item counts allow for another cooking operation + bool CanCookInputToOutput(void) const; + + /// Broadcasts progressbar updates, if needed + void UpdateProgressBars(void); + + /// Sets the m_IsCooking variable, updates the furnace block type based on the value + void SetIsCooking(bool a_IsCooking); + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + +} ; // tolua_export + + + + diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp new file mode 100644 index 000000000..41849b1b3 --- /dev/null +++ b/src/BlockEntities/HopperEntity.cpp @@ -0,0 +1,566 @@ + +// HopperEntity.cpp + +// Implements the cHopperEntity representing a hopper block entity + +#include "Globals.h" +#include "HopperEntity.h" +#include "../Chunk.h" +#include "../Entities/Player.h" +#include "../PluginManager.h" +#include "ChestEntity.h" +#include "DropSpenserEntity.h" +#include "FurnaceEntity.h" + + + + + +cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_LastMoveItemsInTick(0), + m_LastMoveItemsOutTick(0) +{ +} + + + + + +/** Returns the block coords of the block receiving the output items, based on the meta +Returns false if unattached +*/ +bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ) +{ + a_OutputX = m_PosX; + a_OutputY = m_PosY; + a_OutputZ = m_PosZ; + switch (a_BlockMeta) + { + case E_META_HOPPER_FACING_XM: a_OutputX--; return true; + case E_META_HOPPER_FACING_XP: a_OutputX++; return true; + case E_META_HOPPER_FACING_YM: a_OutputY--; return true; + case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true; + case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true; + default: + { + // Not attached + return false; + } + } +} + + + + + +bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); + + bool res = false; + res = MoveItemsIn (a_Chunk, CurrentTick) || res; + res = MovePickupsIn(a_Chunk, CurrentTick) || res; + res = MoveItemsOut (a_Chunk, CurrentTick) || res; + return res; +} + + + + + +void cHopperEntity::SaveToJson(Json::Value & a_Value) +{ + // TODO + LOGWARNING("%s: Not implemented yet", __FUNCTION__); +} + + + + + +void cHopperEntity::SendTo(cClientHandle & a_Client) +{ + // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked + + UNUSED(a_Client); +} + + + + + +void cHopperEntity::UsedBy(cPlayer * a_Player) +{ + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +/// Opens a new window UI for this hopper +void cHopperEntity::OpenNewWindow(void) +{ + OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this)); +} + + + + + +/// Moves items from the container above it into this hopper. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (m_PosY >= cChunkDef::Height) + { + // This hopper is at the top of the world, no more blocks above + return false; + } + + if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + // Try moving an item in: + bool res = false; + switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) + { + case E_BLOCK_CHEST: + { + // Chests have special handling because of double-chests + res = MoveItemsFromChest(a_Chunk); + break; + } + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + { + // Furnaces have special handling because only the output and leftover fuel buckets shall be moved + res = MoveItemsFromFurnace(a_Chunk); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_HOPPER: + { + res = MoveItemsFromGrid(*(cBlockEntityWithItems *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ)); + break; + } + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsInTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves pickups from above this hopper into it. Returns true if the contents have changed. +bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + // TODO + return false; +} + + + + + +/// Moves items out from this hopper into the destination. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + int bx, by, bz; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + if (!GetOutputBlockPos(Meta, bx, by, bz)) + { + // Not attached to another container + return false; + } + if (by < 0) + { + // Cannot output below the zero-th block level + return false; + } + + // Convert coords to relative: + int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; + int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); + if (DestChunk == NULL) + { + // The destination chunk has been unloaded, don't tick + return false; + } + + // Call proper moving function, based on the blocktype present at the coords: + bool res = false; + switch (DestChunk->GetBlock(rx, by, rz)) + { + case E_BLOCK_CHEST: + { + // Chests have special handling because of double-chests + res = MoveItemsToChest(*DestChunk, bx, by, bz); + break; + } + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + { + // Furnaces have special handling because of the direction-to-slot relation + res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_HOPPER: + { + res = MoveItemsToGrid(*(cBlockEntityWithItems *)DestChunk->GetBlockEntity(bx, by, bz)); + break; + } + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsOutTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) +{ + if (MoveItemsFromGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))) + { + // Moved the item from the chest directly above the hopper + return true; + } + + // Check if the chest is a double-chest, if so, try to move from there: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsFromGrid(*(cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) +{ + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + ASSERT(Furnace != NULL); + + // Try move from the output slot: + if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) + { + cItem NewOutput(Furnace->GetOutputSlot()); + Furnace->SetOutputSlot(NewOutput.AddCount(-1)); + return true; + } + + // No output moved, check if we can move an empty bucket out of the fuel slot: + if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) + { + if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true)) + { + Furnace->SetFuelSlot(cItem()); + return true; + } + } + + // Nothing can be moved + return false; +} + + + + + +bool cHopperEntity::MoveItemsFromGrid(cBlockEntityWithItems & a_Entity) +{ + cItemGrid & Grid = a_Entity.GetContents(); + int NumSlots = Grid.GetNumSlots(); + + // First try adding items of types already in the hopper: + for (int i = 0; i < NumSlots; i++) + { + if (Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Entity, i, false)) + { + Grid.ChangeSlotCount(i, -1); + return true; + } + } + + // No already existing stack can be topped up, try again with allowing new stacks: + for (int i = 0; i < NumSlots; i++) + { + if (Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Entity, i, true)) + { + Grid.ChangeSlotCount(i, -1); + return true; + } + } + return false; +} + + + + + +/// Moves one piece of the specified a_Entity's slot itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. +bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SlotNum, bool a_AllowNewStacks) +{ + cItem One(a_Entity.GetSlot(a_SlotNum).CopyOne()); + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.IsSlotEmpty(i)) + { + if (a_AllowNewStacks) + { + if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) + { + // Plugin disagrees with the move + continue; + } + } + m_Contents.SetSlot(i, One); + return true; + } + else if (m_Contents.GetSlot(i).IsStackableWith(One)) + { + if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum)) + { + // Plugin disagrees with the move + continue; + } + + m_Contents.ChangeSlotCount(i, 1); + return true; + } + } + return false; +} + + + + + +/// Moves items to the chest at the specified coords. Returns true if contents have changed +bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Try the chest directly connected to the hopper: + if (MoveItemsToGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + + // Check if the chest is a double-chest, if so, try to move into the other half: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsToGrid(*(cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items to the furnace at the specified coords. Returns true if contents have changed +bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta) +{ + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (a_HopperMeta == E_META_HOPPER_FACING_YM) + { + // Feed the input slot of the furnace + return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsInput); + } + else + { + // Feed the fuel slot of the furnace + return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsFuel); + } + return false; +} + + + + + +bool cHopperEntity::MoveItemsToGrid(cBlockEntityWithItems & a_Entity) +{ + // Iterate through our slots, try to move from each one: + int NumSlots = a_Entity.GetContents().GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + if (MoveItemsToSlot(a_Entity, i)) + { + return true; + } + } + return false; +} + + + + + +bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum) +{ + cItemGrid & Grid = a_Entity.GetContents(); + if (Grid.IsSlotEmpty(a_DstSlotNum)) + { + // The slot is empty, move the first non-empty slot from our contents: + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (!m_Contents.IsSlotEmpty(i)) + { + if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) + { + // A plugin disagrees with the move + continue; + } + Grid.SetSlot(a_DstSlotNum, m_Contents.GetSlot(i).CopyOne()); + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; + } + else + { + // The slot is taken, try to top it up: + const cItem & DestSlot = Grid.GetSlot(a_DstSlotNum); + if (DestSlot.IsFullStack()) + { + return false; + } + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.GetSlot(i).IsStackableWith(DestSlot)) + { + if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum)) + { + // A plugin disagrees with the move + continue; + } + Grid.ChangeSlotCount(a_DstSlotNum, 1); + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; + } +} + + + + diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h new file mode 100644 index 000000000..3eaa05b7c --- /dev/null +++ b/src/BlockEntities/HopperEntity.h @@ -0,0 +1,96 @@ + +// HopperEntity.h + +// Declares the cHopperEntity representing a hopper block entity + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +class cHopperEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 1, + ContentsWidth = 5, + TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper + } ; + + // tolua_end + + /// Constructor used for normal operation + cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + /** Returns the block coords of the block receiving the output items, based on the meta + Returns false if unattached. + Exported in ManualBindings.cpp + */ + bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ); + + static const char * GetClassStatic(void) { return "cHopperEntity"; } + +protected: + + Int64 m_LastMoveItemsInTick; + Int64 m_LastMoveItemsOutTick; + + // cBlockEntity overrides: + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + void OpenNewWindow(void); + + /// Moves items from the container above it into this hopper. Returns true if the contents have changed. + bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves pickups from above this hopper into it. Returns true if the contents have changed. + bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items out from this hopper into the destination. Returns true if the contents have changed. + bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromChest(cChunk & a_Chunk); + + /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromFurnace(cChunk & a_Chunk); + + /// Moves items from the specified a_Entity's Contents into this hopper. Returns true if contents have changed. + bool MoveItemsFromGrid(cBlockEntityWithItems & a_Entity); + + /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. + bool MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_SrcSlotNum, bool a_AllowNewStacks); + + /// Moves items to the chest at the specified coords. Returns true if contents have changed + bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Moves items to the furnace at the specified coords. Returns true if contents have changed + bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta); + + /// Moves items to the specified ItemGrid. Returns true if contents have changed + bool MoveItemsToGrid(cBlockEntityWithItems & a_Entity); + + /// Moves one piece to the specified entity's contents' slot. Returns true if contents have changed. + bool MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum); +} ; // tolua_export + + + + diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp new file mode 100644 index 000000000..8e90ee70d --- /dev/null +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -0,0 +1,125 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "JukeboxEntity.h" +#include "../World.h" +#include "lib/jsoncpp/include/json/json.h" + + + + + +cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Record(0) +{ +} + + + + + +cJukeboxEntity::~cJukeboxEntity() +{ + EjectRecord(); +} + + + + + +void cJukeboxEntity::UsedBy(cPlayer * a_Player) +{ + if (m_Record == 0) + { + const cItem & HeldItem = a_Player->GetEquippedItem(); + if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267) + { + m_Record = HeldItem.m_ItemType; + a_Player->GetInventory().RemoveOneEquippedItem(); + PlayRecord(); + } + } + else + { + EjectRecord(); + } +} + + + + + +void cJukeboxEntity::PlayRecord(void) +{ + m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, m_Record); +} + + + + + +void cJukeboxEntity::EjectRecord(void) +{ + if ((m_Record < E_ITEM_FIRST_DISC) || (m_Record > E_ITEM_LAST_DISC)) + { + // There's no record here + return; + } + + cItems Drops; + Drops.push_back(cItem(m_Record, 1, 0)); + m_World->SpawnItemPickups(Drops, m_PosX + 0.5, m_PosY + 1, m_PosZ + 0.5, 8); + m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, 0); + m_Record = 0; +} + + + + + +int cJukeboxEntity::GetRecord(void) +{ + return m_Record; +} + + + + + +void cJukeboxEntity::SetRecord(int a_Record) +{ + m_Record = a_Record; +} + + + + + +bool cJukeboxEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Record = a_Value.get("Record", 0).asInt(); + + return true; +} + + + + + +void cJukeboxEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Record"] = m_Record; +} + + + + diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h new file mode 100644 index 000000000..fcafdc479 --- /dev/null +++ b/src/BlockEntities/JukeboxEntity.h @@ -0,0 +1,56 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../Entities/Player.h" + + + + + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin + +class cJukeboxEntity : + public cBlockEntity +{ + typedef cBlockEntity super; +public: + + // tolua_end + + cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cJukeboxEntity(); + + bool LoadFromJson(const Json::Value & a_Value); + virtual void SaveToJson(Json::Value & a_Value) override; + + // tolua_begin + + int GetRecord(void); + void SetRecord(int a_Record); + void PlayRecord(void); + + /// Ejects the currently held record as a pickup. Does nothing when no record inserted. + void EjectRecord(void); + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + int m_Record; +} ; // tolua_end + + + + diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp new file mode 100644 index 000000000..493316bce --- /dev/null +++ b/src/BlockEntities/NoteEntity.cpp @@ -0,0 +1,154 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "NoteEntity.h" +#include "../World.h" +#include "lib/jsoncpp/include/json/json.h" + + + + + +cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Pitch(0) +{ +} + + + + + +void cNoteEntity::UsedBy(cPlayer * a_Player) +{ + IncrementPitch(); + MakeSound(); +} + + + + + +void cNoteEntity::MakeSound(void) +{ + char instrument; + AString sampleName; + + switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) + { + case E_BLOCK_PLANKS: + case E_BLOCK_LOG: + case E_BLOCK_NOTE_BLOCK: + { + // TODO: add other wood-based blocks if needed + instrument = E_INST_DOUBLE_BASS; + sampleName = "note.db"; + break; + } + + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_SOULSAND: + { + instrument = E_INST_SNARE_DRUM; + sampleName = "note.snare"; + break; + } + + case E_BLOCK_GLASS: + case E_BLOCK_GLASS_PANE: + case E_BLOCK_GLOWSTONE: + { + instrument = E_INST_CLICKS; + sampleName = "note.hat"; + break; + } + + case E_BLOCK_STONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_OBSIDIAN: + case E_BLOCK_NETHERRACK: + case E_BLOCK_BRICK: + case E_BLOCK_NETHER_BRICK: + { + // TODO: add other stone-based blocks if needed + instrument = E_INST_BASS_DRUM; + sampleName = "note.bassattack"; + break; + } + + default: + { + instrument = E_INST_HARP_PIANO; + sampleName = "note.harp"; + break; + } + } + + m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); + + // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all + float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); + m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); +} + + + + + +char cNoteEntity::GetPitch(void) +{ + return m_Pitch; +} + + + + + +void cNoteEntity::SetPitch(char a_Pitch) +{ + m_Pitch = a_Pitch % 25; +} + + + + + +void cNoteEntity::IncrementPitch(void) +{ + SetPitch(m_Pitch + 1); +} + + + + + +bool cNoteEntity::LoadFromJson(const Json::Value & a_Value) +{ + + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Pitch = (char)a_Value.get("p", 0).asInt(); + + return true; +} + + + + + +void cNoteEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["p"] = m_Pitch; +} + + + + diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h new file mode 100644 index 000000000..e2d088f44 --- /dev/null +++ b/src/BlockEntities/NoteEntity.h @@ -0,0 +1,63 @@ + +#pragma once + +#include "BlockEntity.h" + + +namespace Json +{ + class Value; +} + + + + + +enum ENUM_NOTE_INSTRUMENTS +{ + E_INST_HARP_PIANO = 0, + E_INST_DOUBLE_BASS = 1, + E_INST_SNARE_DRUM = 2, + E_INST_CLICKS = 3, + E_INST_BASS_DRUM = 4 +}; + + + + + +// tolua_begin + +class cNoteEntity : + public cBlockEntity +{ + typedef cBlockEntity super; +public: + + // tolua_end + + /// Creates a new note entity. a_World may be NULL + cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + + bool LoadFromJson(const Json::Value & a_Value); + virtual void SaveToJson(Json::Value & a_Value) override; + + // tolua_begin + + char GetPitch(void); + void SetPitch(char a_Pitch); + void IncrementPitch(void); + void MakeSound(void); + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + char m_Pitch; +} ; // tolua_export + + + + diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp new file mode 100644 index 000000000..39fcb6efc --- /dev/null +++ b/src/BlockEntities/SignEntity.cpp @@ -0,0 +1,115 @@ + +// SignEntity.cpp + +// Implements the cSignEntity class representing a single sign in the world + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "lib/jsoncpp/include/json/json.h" +#include "SignEntity.h" +#include "../Entities/Player.h" + + + + + +cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) : + super(a_BlockType, a_X, a_Y, a_Z, a_World) +{ +} + + + + + +// It don't do anything when 'used' +void cSignEntity::UsedBy(cPlayer * a_Player) +{ + UNUSED(a_Player); +} + + + + + +void cSignEntity::SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + m_Line[0] = a_Line1; + m_Line[1] = a_Line2; + m_Line[2] = a_Line3; + m_Line[3] = a_Line4; +} + + + + + +void cSignEntity::SetLine(int a_Index, const AString & a_Line) +{ + if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) + { + LOGWARNING("%s: setting a non-existent line %d (value \"%s\"", __FUNCTION__, a_Index, a_Line.c_str()); + return; + } + m_Line[a_Index] = a_Line; +} + + + + + +AString cSignEntity::GetLine(int a_Index) const +{ + if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) + { + LOGWARNING("%s: requesting a non-existent line %d", __FUNCTION__, a_Index); + return ""; + } + return m_Line[a_Index]; +} + + + + + +void cSignEntity::SendTo(cClientHandle & a_Client) +{ + a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]); +} + + + + + +bool cSignEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Line[0] = a_Value.get("Line1", "").asString(); + m_Line[1] = a_Value.get("Line2", "").asString(); + m_Line[2] = a_Value.get("Line3", "").asString(); + m_Line[3] = a_Value.get("Line4", "").asString(); + + return true; +} + + + + + +void cSignEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Line1"] = m_Line[0]; + a_Value["Line2"] = m_Line[1]; + a_Value["Line3"] = m_Line[2]; + a_Value["Line4"] = m_Line[3]; +} + + + + diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h new file mode 100644 index 000000000..d998ff1e8 --- /dev/null +++ b/src/BlockEntities/SignEntity.h @@ -0,0 +1,67 @@ + +// SignEntity.h + +// Declares the cSignEntity class representing a single sign in the world + + + + + +#pragma once + +#include "BlockEntity.h" + + + + + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin + +class cSignEntity : + public cBlockEntity +{ + typedef cBlockEntity super; + +public: + + // tolua_end + + /// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be NULL + cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson(Json::Value& a_Value ) override; + + // tolua_begin + + /// Sets all the sign's lines + void SetLines(const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + /// Sets individual line (zero-based index) + void SetLine(int a_Index, const AString & a_Line); + + /// Retrieves individual line (zero-based index) + AString GetLine(int a_Index) const; + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override; + +private: + + AString m_Line[4]; +} ; // tolua_export + + + + diff --git a/src/BlockID.cpp b/src/BlockID.cpp new file mode 100644 index 000000000..708618d25 --- /dev/null +++ b/src/BlockID.cpp @@ -0,0 +1,958 @@ +// BlockID.cpp + +// Implements the helper functions for converting Block ID string to int etc. + +#include "Globals.h" +#include "BlockID.h" +#include "inifile/iniFile.h" +#include "Item.h" +#include "Mobs/Monster.h" + + + + + +NIBBLETYPE g_BlockLightValue[256]; +NIBBLETYPE g_BlockSpreadLightFalloff[256]; +bool g_BlockTransparent[256]; +bool g_BlockOneHitDig[256]; +bool g_BlockPistonBreakable[256]; +bool g_BlockIsSnowable[256]; +bool g_BlockRequiresSpecialTool[256]; +bool g_BlockIsSolid[256]; +bool g_BlockIsTorchPlaceable[256]; + + + + + +class cBlockIDMap +{ + // Making the map case-insensitive: + struct Comparator + { + bool operator()(const AString & a_Item1, const AString & a_Item2) const + { + return (NoCaseCompare(a_Item1, a_Item2) > 0); + } + } ; + + typedef std::map<AString, std::pair<short, short>, Comparator> ItemMap; + +public: + cBlockIDMap(void) + { + cIniFile Ini; + if (!Ini.ReadFile("items.ini")) + { + return; + } + int KeyID = Ini.FindKey("Items"); + if (KeyID == cIniFile::noID) + { + return; + } + int NumValues = Ini.GetNumValues(KeyID); + for (int i = 0; i < NumValues; i++) + { + AString Name = Ini.GetValueName(KeyID, i); + if (Name.empty()) + { + continue; + } + AString Value = Ini.GetValue(KeyID, i); + AddToMap(Name, Value); + } // for i - Ini.Values[] + } + + + int Resolve(const AString & a_ItemName) + { + ItemMap::iterator itr = m_Map.find(a_ItemName); + if (itr == m_Map.end()) + { + return -1; + } + return itr->second.first; + } + + + bool ResolveItem(const AString & a_ItemName, cItem & a_Item) + { + // Split into parts divided by either ':' or '^' + AStringVector Split = StringSplitAndTrim(a_ItemName, ":^"); + if (Split.empty()) + { + return false; + } + + ItemMap::iterator itr = m_Map.find(Split[0]); + if (itr != m_Map.end()) + { + // Resolved as a string, assign the type and the default damage / count + a_Item.m_ItemType = itr->second.first; + a_Item.m_ItemDamage = itr->second.second; + if (a_Item.m_ItemDamage == -1) + { + a_Item.m_ItemDamage = 0; + } + } + else + { + // Not a resolvable string, try pure numbers: "45:6", "45^6" etc. + a_Item.m_ItemType = (short)atoi(Split[0].c_str()); + if ((a_Item.m_ItemType == 0) && (Split[0] != "0")) + { + // Parsing the number failed + return false; + } + } + + // Parse the damage, if present: + if (Split.size() < 2) + { + // Not present, set the item as valid and return success: + a_Item.m_ItemCount = 1; + return true; + } + + a_Item.m_ItemDamage = atoi(Split[1].c_str()); + if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0")) + { + // Parsing the number failed + return false; + } + a_Item.m_ItemCount = 1; + return true; + } + + + AString Desolve(short a_ItemType, short a_ItemDamage) + { + // First try an exact match, both ItemType and ItemDamage ("birchplanks=5:2"): + for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr) + { + if ((itr->second.first == a_ItemType) && (itr->second.second == a_ItemDamage)) + { + return itr->first; + } + } // for itr - m_Map[] + + // There is no exact match, try matching ItemType only ("planks=5"): + if (a_ItemDamage == 0) + { + for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr) + { + if ((itr->second.first == a_ItemType) && (itr->second.second == -1)) + { + return itr->first; + } + } // for itr - m_Map[] + } + + // No match at all, synthesize a string ("5:1"): + AString res; + if (a_ItemDamage == -1) + { + Printf(res, "%d", a_ItemType); + } + else + { + Printf(res, "%d:%d", a_ItemType, a_ItemDamage); + } + return res; + } + + +protected: + ItemMap m_Map; + + + void AddToMap(const AString & a_Name, const AString & a_Value) + { + AStringVector Split = StringSplit(a_Value, ":"); + if (Split.size() == 1) + { + Split = StringSplit(a_Value, "^"); + } + if (Split.empty()) + { + return; + } + short ItemType = (short)atoi(Split[0].c_str()); + short ItemDamage = (Split.size() > 1) ? (short)atoi(Split[1].c_str()) : -1; + m_Map[a_Name] = std::make_pair(ItemType, ItemDamage); + } +} ; + + + + + +static cBlockIDMap gsBlockIDMap; + + + + + +/* +// Quick self-test: +class Tester +{ +public: + Tester(void) + { + cItem Item; + gsBlockIDMap.ResolveItem("charcoal", Item); + AString Charcoal = gsBlockIDMap.Desolve(Item.m_ItemType, Item.m_ItemDamage); + ASSERT(Charcoal == "charcoal"); + } +} test; +//*/ + + + + + +BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString) +{ + int res = atoi(a_BlockTypeString.c_str()); + if ((res != 0) || (a_BlockTypeString.compare("0") == 0)) + { + // It was a valid number, return that + return res; + } + + return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString)); +} + + + + +bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item) +{ + return gsBlockIDMap.ResolveItem(TrimString(a_ItemTypeString), a_Item); +} + + + + + +AString ItemToString(const cItem & a_Item) +{ + return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage); +} + + + + + +AString ItemTypeToString(short a_ItemType) +{ + return gsBlockIDMap.Desolve(a_ItemType, -1); +} + + + + + +AString ItemToFullString(const cItem & a_Item) +{ + AString res; + Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemDamage, a_Item.m_ItemCount); + return res; +} + + + + + +EMCSBiome StringToBiome(const AString & a_BiomeString) +{ + // If it is a number, return it: + int res = atoi(a_BiomeString.c_str()); + if ((res != 0) || (a_BiomeString.compare("0") == 0)) + { + // It was a valid number + return (EMCSBiome)res; + } + + // Convert using the built-in map: + static struct { + EMCSBiome m_Biome; + const char * m_String; + } BiomeMap[] = + { + {biOcean, "Ocean"} , + {biPlains, "Plains"}, + {biDesert, "Desert"}, + {biExtremeHills, "ExtremeHills"}, + {biForest, "Forest"}, + {biTaiga, "Taiga"}, + {biSwampland, "Swampland"}, + {biRiver, "River"}, + {biNether, "Hell"}, + {biNether, "Nether"}, + {biEnd, "Sky"}, + {biEnd, "End"}, + {biFrozenOcean, "FrozenOcean"}, + {biFrozenRiver, "FrozenRiver"}, + {biIcePlains, "IcePlains"}, + {biIcePlains, "Tundra"}, + {biIceMountains, "IceMountains"}, + {biMushroomIsland, "MushroomIsland"}, + {biMushroomShore, "MushroomShore"}, + {biBeach, "Beach"}, + {biDesertHills, "DesertHills"}, + {biForestHills, "ForestHills"}, + {biTaigaHills, "TaigaHills"}, + {biExtremeHillsEdge, "ExtremeHillsEdge"}, + {biJungle, "Jungle"}, + {biJungleHills, "JungleHills"}, + + // Release 1.7 biomes: + {biJungleEdge, "JungleEdge"}, + {biDeepOcean, "DeepOcean"}, + {biStoneBeach, "StoneBeach"}, + {biColdBeach, "ColdBeach"}, + {biBirchForest, "BirchForest"}, + {biBirchForestHills, "BirchForestHills"}, + {biRoofedForest, "RoofedForest"}, + {biColdTaiga, "ColdTaiga"}, + {biColdTaigaHills, "ColdTaigaHills"}, + {biMegaTaiga, "MegaTaiga"}, + {biMegaTaigaHills, "MegaTaigaHills"}, + {biExtremeHillsPlus, "ExtremeHillsPlus"}, + {biSavanna, "Savanna"}, + {biSavannaPlateau, "SavannaPlateau"}, + {biMesa, "Mesa"}, + {biMesaPlateauF, "MesaPlateauF"}, + {biMesaPlateau, "MesaPlateau"}, + + // Release 1.7 variants: + {biSunflowerPlains, "SunflowerPlains"}, + {biDesertM, "DesertM"}, + {biExtremeHillsM, "ExtremeHillsM"}, + {biFlowerForest, "FlowerForest"}, + {biTaigaM, "TaigaM"}, + {biSwamplandM, "SwamplandM"}, + {biIcePlainsSpikes, "IcePlainsSpikes"}, + {biJungleM, "JungleM"}, + {biJungleEdgeM, "JungleEdgeM"}, + {biBirchForestM, "BirchForestM"}, + {biBirchForestHillsM, "BirchForestHillsM"}, + {biRoofedForestM, "RoofedForestM"}, + {biColdTaigaM, "ColdTaigaM"}, + {biMegaSpruceTaiga, "MegaSpruceTaiga"}, + {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, + {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, + {biSavannaM, "SavannaM"}, + {biSavannaPlateauM, "SavannaPlateauM"}, + {biMesaBryce, "MesaBryce"}, + {biMesaPlateauFM, "MesaPlateauFM"}, + {biMesaPlateauM, "MesaPlateauM"}, + } ; + + for (int i = 0; i < ARRAYCOUNT(BiomeMap); i++) + { + if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0) + { + return BiomeMap[i].m_Biome; + } + } // for i - BiomeMap[] + return (EMCSBiome)-1; +} + + + + + +int StringToMobType(const AString & a_MobString) +{ + static struct { + int m_MobType; + const char * m_String; + } MobMap [] = + { + {cMonster::mtCreeper, "Creeper"}, + {cMonster::mtSkeleton, "Skeleton"}, + {cMonster::mtSpider, "Spider"}, + {cMonster::mtGiant, "Giant"}, + {cMonster::mtZombie, "Zombie"}, + {cMonster::mtSlime, "Slime"}, + {cMonster::mtGhast, "Ghast"}, + {cMonster::mtZombiePigman, "ZombiePigman"}, + {cMonster::mtEnderman, "Enderman"}, + {cMonster::mtCaveSpider, "CaveSpider"}, + {cMonster::mtSilverfish, "SilverFish"}, + {cMonster::mtBlaze, "Blaze"}, + {cMonster::mtMagmaCube, "MagmaCube"}, + {cMonster::mtEnderDragon, "EnderDragon"}, + {cMonster::mtWither, "Wither"}, + {cMonster::mtBat, "Bat"}, + {cMonster::mtWitch, "Witch"}, + {cMonster::mtPig, "Pig"}, + {cMonster::mtSheep, "Sheep"}, + {cMonster::mtCow, "Cow"}, + {cMonster::mtChicken, "Chicken"}, + {cMonster::mtSquid, "Squid"}, + {cMonster::mtWolf, "Wolf"}, + {cMonster::mtMooshroom, "Mooshroom"}, + {cMonster::mtSnowGolem, "SnowGolem"}, + {cMonster::mtOcelot, "Ocelot"}, + {cMonster::mtIronGolem, "IronGolem"}, + {cMonster::mtVillager, "Villager"}, + }; + for (int i = 0; i < ARRAYCOUNT(MobMap); i++) + { + if (NoCaseCompare(MobMap[i].m_String, a_MobString) == 0) + { + return MobMap[i].m_MobType; + } + } // for i - MobMap[] + return -1; +} + + + + + +eDimension StringToDimension(const AString & a_DimensionString) +{ + // First try decoding as a number + int res = atoi(a_DimensionString.c_str()); + if ((res != 0) || (a_DimensionString == "0")) + { + // It was a valid number + return (eDimension)res; + } + + // Decode using a built-in map: + static struct + { + eDimension m_Dimension; + const char * m_String; + } DimensionMap [] = + { + { dimOverworld, "Overworld"}, + { dimOverworld, "Normal"}, + { dimOverworld, "World"}, + { dimNether, "Nether"}, + { dimNether, "Hell"}, // Alternate name for End + { dimEnd, "End"}, + { dimEnd, "Sky"}, // Old name for End + } ; + for (int i = 0; i < ARRAYCOUNT(DimensionMap); i++) + { + if (NoCaseCompare(DimensionMap[i].m_String, a_DimensionString) == 0) + { + return DimensionMap[i].m_Dimension; + } + } // for i - DimensionMap[] + + // Not found + return (eDimension)-1000; +} + + + + + +/// Translates damage type constant to a string representation (built-in). +AString DamageTypeToString(eDamageType a_DamageType) +{ + // Make sure to keep this alpha-sorted. + switch (a_DamageType) + { + case dtAdmin: return "dtAdmin"; + case dtAttack: return "dtAttack"; + case dtCactusContact: return "dtCactusContact"; + case dtDrowning: return "dtDrowning"; + case dtEnderPearl: return "dtEnderPearl"; + case dtFalling: return "dtFalling"; + case dtFireContact: return "dtFireContact"; + case dtInVoid: return "dtInVoid"; + case dtLavaContact: return "dtLavaContact"; + case dtLightning: return "dtLightning"; + case dtOnFire: return "dtOnFire"; + case dtPoisoning: return "dtPoisoning"; + case dtPotionOfHarming: return "dtPotionOfHarming"; + case dtRangedAttack: return "dtRangedAttack"; + case dtStarving: return "dtStarving"; + case dtSuffocating: return "dtSuffocation"; + + } + + // Unknown damage type: + ASSERT(!"Unknown DamageType"); + return Printf("dtUnknown_%d", (int)a_DamageType); +} + + + + + +/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure +eDamageType StringToDamageType(const AString & a_DamageTypeString) +{ + // First try decoding as a number: + int res = atoi(a_DamageTypeString.c_str()); + if ((res != 0) || (a_DamageTypeString == "0")) + { + // It was a valid number + return (eDamageType)res; + } + + // Decode using a built-in map: + static struct + { + eDamageType m_DamageType; + const char * m_String; + } DamageTypeMap [] = + { + // Cannonical names: + { dtAttack, "dtAttack"}, + { dtRangedAttack, "dtRangedAttack"}, + { dtLightning, "dtLightning"}, + { dtFalling, "dtFalling"}, + { dtDrowning, "dtDrowning"}, + { dtSuffocating, "dtSuffocation"}, + { dtStarving, "dtStarving"}, + { dtCactusContact, "dtCactusContact"}, + { dtLavaContact, "dtLavaContact"}, + { dtPoisoning, "dtPoisoning"}, + { dtOnFire, "dtOnFire"}, + { dtFireContact, "dtFireContact"}, + { dtInVoid, "dtInVoid"}, + { dtPotionOfHarming, "dtPotionOfHarming"}, + { dtAdmin, "dtAdmin"}, + + // Common synonyms: + { dtAttack, "dtPawnAttack"}, + { dtAttack, "dtEntityAttack"}, + { dtAttack, "dtMob"}, + { dtAttack, "dtMobAttack"}, + { dtRangedAttack, "dtArrowAttack"}, + { dtRangedAttack, "dtArrow"}, + { dtRangedAttack, "dtProjectile"}, + { dtFalling, "dtFall"}, + { dtDrowning, "dtDrown"}, + { dtSuffocating, "dtSuffocation"}, + { dtStarving, "dtStarvation"}, + { dtStarving, "dtHunger"}, + { dtCactusContact, "dtCactus"}, + { dtCactusContact, "dtCactuses"}, + { dtCactusContact, "dtCacti"}, + { dtLavaContact, "dtLava"}, + { dtPoisoning, "dtPoison"}, + { dtOnFire, "dtBurning"}, + { dtFireContact, "dtInFire"}, + { dtAdmin, "dtPlugin"}, + } ; + for (int i = 0; i < ARRAYCOUNT(DamageTypeMap); i++) + { + if (NoCaseCompare(DamageTypeMap[i].m_String, a_DamageTypeString) == 0) + { + return DamageTypeMap[i].m_DamageType; + } + } // for i - DamageTypeMap[] + + // Not found: + return (eDamageType)-1; +} + + + + + +cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default) +{ + AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default); + cItem res; + if (!StringToItem(ItemStr, res)) + { + res.Empty(); + } + return res; +} + + + + + +// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: +class cBlockPropertiesInitializer +{ +public: + cBlockPropertiesInitializer(void) + { + memset(g_BlockLightValue, 0x00, sizeof(g_BlockLightValue)); + memset(g_BlockSpreadLightFalloff, 0x0f, sizeof(g_BlockSpreadLightFalloff)); // 0x0f means total falloff + memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent)); + memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig)); + memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable)); + memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable)); + + // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 + for (int i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++) + { + g_BlockIsSnowable[i] = true; + } + memset(g_BlockRequiresSpecialTool, 0x00, sizeof(g_BlockRequiresSpecialTool)); // Set all blocks to false + + // Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415 + for (int i = 0; i < ARRAYCOUNT(g_BlockIsSolid); i++) + { + g_BlockIsSolid[i] = true; + } + + // Emissive blocks + g_BlockLightValue[E_BLOCK_FIRE] = 15; + g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15; + g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15; + g_BlockLightValue[E_BLOCK_LAVA] = 15; + g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15; + g_BlockLightValue[E_BLOCK_END_PORTAL] = 15; + g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15; + g_BlockLightValue[E_BLOCK_TORCH] = 14; + g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13; + g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11; + g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9; + g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9; + g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7; + g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1; + g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1; + g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1; + + // Spread blocks + g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_CAKE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_CHEST] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_COBWEB] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_CROPS] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_FENCE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_FENCE_GATE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_GLASS_PANE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_IRON_BARS] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_IRON_DOOR] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_VINES] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1; + g_BlockSpreadLightFalloff[E_BLOCK_WOODEN_DOOR] = 1; + + // Light in water and lava dissapears faster: + g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 3; + g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 3; + g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 3; + g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 3; + + // Transparent blocks + g_BlockTransparent[E_BLOCK_ACTIVATOR_RAIL] = true; + g_BlockTransparent[E_BLOCK_AIR] = true; + g_BlockTransparent[E_BLOCK_BIG_FLOWER] = true; + g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true; + g_BlockTransparent[E_BLOCK_CARROTS] = true; + g_BlockTransparent[E_BLOCK_CHEST] = true; + g_BlockTransparent[E_BLOCK_COBWEB] = true; + g_BlockTransparent[E_BLOCK_CROPS] = true; + g_BlockTransparent[E_BLOCK_DANDELION] = true; + g_BlockTransparent[E_BLOCK_DETECTOR_RAIL] = true; + g_BlockTransparent[E_BLOCK_FENCE] = true; + g_BlockTransparent[E_BLOCK_FENCE_GATE] = true; + g_BlockTransparent[E_BLOCK_FIRE] = true; + g_BlockTransparent[E_BLOCK_FLOWER] = 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_HEAVY_WEIGHTED_PRESSURE_PLATE] = true; + g_BlockTransparent[E_BLOCK_ICE] = true; + g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; + g_BlockTransparent[E_BLOCK_LAVA] = true; + g_BlockTransparent[E_BLOCK_LEAVES] = true; + g_BlockTransparent[E_BLOCK_LEVER] = true; + g_BlockTransparent[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = true; + g_BlockTransparent[E_BLOCK_MELON_STEM] = true; + g_BlockTransparent[E_BLOCK_NETHER_BRICK_FENCE] = true; + g_BlockTransparent[E_BLOCK_NEW_LEAVES] = true; + g_BlockTransparent[E_BLOCK_POTATOES] = true; + g_BlockTransparent[E_BLOCK_POWERED_RAIL] = true; + g_BlockTransparent[E_BLOCK_PISTON_EXTENSION] = true; + g_BlockTransparent[E_BLOCK_PUMPKIN_STEM] = true; + g_BlockTransparent[E_BLOCK_RAIL] = true; + g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true; + g_BlockTransparent[E_BLOCK_SIGN_POST] = true; + g_BlockTransparent[E_BLOCK_SNOW] = true; + g_BlockTransparent[E_BLOCK_STAINED_GLASS] = true; + g_BlockTransparent[E_BLOCK_STAINED_GLASS_PANE] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true; + g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true; + g_BlockTransparent[E_BLOCK_TALL_GRASS] = true; + g_BlockTransparent[E_BLOCK_TORCH] = true; + g_BlockTransparent[E_BLOCK_VINES] = true; + g_BlockTransparent[E_BLOCK_WALLSIGN] = true; + g_BlockTransparent[E_BLOCK_WATER] = true; + g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true; + g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; + + // TODO: Any other transparent blocks? + + // One hit break blocks: + g_BlockOneHitDig[E_BLOCK_ACTIVE_COMPARATOR] = true; + g_BlockOneHitDig[E_BLOCK_BIG_FLOWER] = true; + g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true; + g_BlockOneHitDig[E_BLOCK_CARROTS] = true; + g_BlockOneHitDig[E_BLOCK_CROPS] = true; + g_BlockOneHitDig[E_BLOCK_DANDELION] = true; + g_BlockOneHitDig[E_BLOCK_FIRE] = true; + g_BlockOneHitDig[E_BLOCK_FLOWER] = true; + g_BlockOneHitDig[E_BLOCK_FLOWER_POT] = true; + g_BlockOneHitDig[E_BLOCK_INACTIVE_COMPARATOR] = true; + g_BlockOneHitDig[E_BLOCK_MELON_STEM] = true; + g_BlockOneHitDig[E_BLOCK_POTATOES] = true; + g_BlockOneHitDig[E_BLOCK_PUMPKIN_STEM] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true; + g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true; + 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_RED_MUSHROOM] = true; + g_BlockOneHitDig[E_BLOCK_REEDS] = true; + g_BlockOneHitDig[E_BLOCK_SAPLING] = true; + g_BlockOneHitDig[E_BLOCK_TNT] = true; + g_BlockOneHitDig[E_BLOCK_TALL_GRASS] = true; + g_BlockOneHitDig[E_BLOCK_TORCH] = true; + + // Blocks that break when pushed by piston: + g_BlockPistonBreakable[E_BLOCK_ACTIVE_COMPARATOR] = true; + g_BlockPistonBreakable[E_BLOCK_AIR] = true; + g_BlockPistonBreakable[E_BLOCK_BED] = true; + g_BlockPistonBreakable[E_BLOCK_BIG_FLOWER] = true; + g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true; + g_BlockPistonBreakable[E_BLOCK_COBWEB] = true; + g_BlockPistonBreakable[E_BLOCK_CROPS] = true; + g_BlockPistonBreakable[E_BLOCK_DANDELION] = true; + g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true; + g_BlockPistonBreakable[E_BLOCK_FIRE] = true; + g_BlockPistonBreakable[E_BLOCK_FLOWER] = true; + g_BlockPistonBreakable[E_BLOCK_INACTIVE_COMPARATOR] = true; + g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true; + g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true; + g_BlockPistonBreakable[E_BLOCK_LADDER] = true; + g_BlockPistonBreakable[E_BLOCK_LAVA] = true; + g_BlockPistonBreakable[E_BLOCK_LEVER] = true; + g_BlockPistonBreakable[E_BLOCK_MELON] = true; + g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true; + g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true; + g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_OFF] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_REPEATER_ON] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true; + g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true; + g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true; + g_BlockPistonBreakable[E_BLOCK_REEDS] = true; + g_BlockPistonBreakable[E_BLOCK_SNOW] = true; + g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = true; + g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = true; + g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true; + g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true; + g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true; + g_BlockPistonBreakable[E_BLOCK_TORCH] = true; + g_BlockPistonBreakable[E_BLOCK_VINES] = true; + g_BlockPistonBreakable[E_BLOCK_WATER] = true; + g_BlockPistonBreakable[E_BLOCK_WOODEN_BUTTON] = true; + g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true; + g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; + + + // Blocks that cannot be snowed over: + g_BlockIsSnowable[E_BLOCK_ACTIVE_COMPARATOR] = false; + g_BlockIsSnowable[E_BLOCK_AIR] = false; + g_BlockIsSnowable[E_BLOCK_BIG_FLOWER] = false; + g_BlockIsSnowable[E_BLOCK_BROWN_MUSHROOM] = false; + g_BlockIsSnowable[E_BLOCK_CACTUS] = false; + g_BlockIsSnowable[E_BLOCK_CHEST] = false; + g_BlockIsSnowable[E_BLOCK_CROPS] = false; + g_BlockIsSnowable[E_BLOCK_DANDELION] = false; + g_BlockIsSnowable[E_BLOCK_FIRE] = false; + g_BlockIsSnowable[E_BLOCK_FLOWER] = false; + g_BlockIsSnowable[E_BLOCK_GLASS] = false; + g_BlockIsSnowable[E_BLOCK_ICE] = false; + g_BlockIsSnowable[E_BLOCK_INACTIVE_COMPARATOR] = false; + g_BlockIsSnowable[E_BLOCK_LAVA] = false; + g_BlockIsSnowable[E_BLOCK_LILY_PAD] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_OFF] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_REPEATER_ON] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_OFF] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_TORCH_ON] = false; + g_BlockIsSnowable[E_BLOCK_REDSTONE_WIRE] = false; + g_BlockIsSnowable[E_BLOCK_RED_MUSHROOM] = false; + g_BlockIsSnowable[E_BLOCK_REEDS] = false; + g_BlockIsSnowable[E_BLOCK_SAPLING] = false; + g_BlockIsSnowable[E_BLOCK_SIGN_POST] = false; + g_BlockIsSnowable[E_BLOCK_SNOW] = false; + g_BlockIsSnowable[E_BLOCK_STAINED_GLASS] = false; + g_BlockIsSnowable[E_BLOCK_STAINED_GLASS_PANE] = false; + g_BlockIsSnowable[E_BLOCK_STATIONARY_LAVA] = false; + g_BlockIsSnowable[E_BLOCK_STATIONARY_WATER] = false; + g_BlockIsSnowable[E_BLOCK_TALL_GRASS] = false; + g_BlockIsSnowable[E_BLOCK_TNT] = false; + g_BlockIsSnowable[E_BLOCK_TORCH] = false; + g_BlockIsSnowable[E_BLOCK_VINES] = false; + g_BlockIsSnowable[E_BLOCK_WALLSIGN] = false; + g_BlockIsSnowable[E_BLOCK_WATER] = false; + + + // Blocks that don't drop without a special tool: + g_BlockRequiresSpecialTool[E_BLOCK_BRICK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_CAULDRON] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COAL_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COBBLESTONE_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_COBWEB] = true; + g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_DIAMOND_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_DOUBLE_STONE_SLAB] = true; + g_BlockRequiresSpecialTool[E_BLOCK_EMERALD_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_END_STONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_GOLD_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_GOLD_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_IRON_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_IRON_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_BLOCK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_LAPIS_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_MOSSY_COBBLESTONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_NETHERRACK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK] = true; + g_BlockRequiresSpecialTool[E_BLOCK_NETHER_BRICK_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_OBSIDIAN] = true; + g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_REDSTONE_ORE_GLOWING] = true; + g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_SANDSTONE_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_SNOW] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICKS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_BRICK_STAIRS] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_PRESSURE_PLATE] = true; + g_BlockRequiresSpecialTool[E_BLOCK_STONE_SLAB] = true; + g_BlockRequiresSpecialTool[E_BLOCK_VINES] = true; + + // Nonsolid blocks: + g_BlockIsSolid[E_BLOCK_ACTIVATOR_RAIL] = false; + g_BlockIsSolid[E_BLOCK_AIR] = false; + g_BlockIsSolid[E_BLOCK_BIG_FLOWER] = false; + g_BlockIsSolid[E_BLOCK_BROWN_MUSHROOM] = false; + g_BlockIsSolid[E_BLOCK_CARROTS] = false; + g_BlockIsSolid[E_BLOCK_COBWEB] = false; + g_BlockIsSolid[E_BLOCK_CROPS] = false; + g_BlockIsSolid[E_BLOCK_DANDELION] = false; + g_BlockIsSolid[E_BLOCK_DETECTOR_RAIL] = false; + g_BlockIsSolid[E_BLOCK_END_PORTAL] = false; + g_BlockIsSolid[E_BLOCK_FIRE] = false; + g_BlockIsSolid[E_BLOCK_FLOWER] = false; + g_BlockIsSolid[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_LAVA] = false; + g_BlockIsSolid[E_BLOCK_LEVER] = false; + g_BlockIsSolid[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_MELON_STEM] = false; + g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false; + g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false; + g_BlockIsSolid[E_BLOCK_RAIL] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_ON] = false; + g_BlockIsSolid[E_BLOCK_REDSTONE_WIRE] = false; + g_BlockIsSolid[E_BLOCK_RED_MUSHROOM] = false; + g_BlockIsSolid[E_BLOCK_REEDS] = false; + g_BlockIsSolid[E_BLOCK_SAPLING] = false; + g_BlockIsSolid[E_BLOCK_SIGN_POST] = false; + g_BlockIsSolid[E_BLOCK_SNOW] = false; + g_BlockIsSolid[E_BLOCK_STATIONARY_LAVA] = false; + g_BlockIsSolid[E_BLOCK_STATIONARY_WATER] = false; + g_BlockIsSolid[E_BLOCK_STONE_BUTTON] = false; + g_BlockIsSolid[E_BLOCK_STONE_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_TALL_GRASS] = false; + g_BlockIsSolid[E_BLOCK_TORCH] = false; + g_BlockIsSolid[E_BLOCK_TRIPWIRE] = false; + g_BlockIsSolid[E_BLOCK_VINES] = false; + g_BlockIsSolid[E_BLOCK_WALLSIGN] = false; + g_BlockIsSolid[E_BLOCK_WATER] = false; + g_BlockIsSolid[E_BLOCK_WOODEN_BUTTON] = false; + g_BlockIsSolid[E_BLOCK_WOODEN_PRESSURE_PLATE] = false; + g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false; + + // Torch placeable blocks: + g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true; + g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true; + g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true; + g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PACKED_ICE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true; + g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true; + g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true; + g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true; + g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true; + g_BlockIsTorchPlaceable[E_BLOCK_STONE_BRICKS] = true; + } +} BlockPropertiesInitializer; + + + + + diff --git a/src/BlockID.h b/src/BlockID.h new file mode 100644 index 000000000..f3cbc46d6 --- /dev/null +++ b/src/BlockID.h @@ -0,0 +1,907 @@ +#pragma once + +// tolua_begin +enum ENUM_BLOCK_ID +{ + E_BLOCK_AIR = 0, + E_BLOCK_STONE = 1, + E_BLOCK_GRASS = 2, + E_BLOCK_DIRT = 3, + E_BLOCK_COBBLESTONE = 4, + E_BLOCK_PLANKS = 5, + E_BLOCK_SAPLING = 6, + E_BLOCK_BEDROCK = 7, + E_BLOCK_WATER = 8, + E_BLOCK_STATIONARY_WATER = 9, + E_BLOCK_LAVA = 10, + E_BLOCK_STATIONARY_LAVA = 11, + E_BLOCK_SAND = 12, + E_BLOCK_GRAVEL = 13, + E_BLOCK_GOLD_ORE = 14, + E_BLOCK_IRON_ORE = 15, + E_BLOCK_COAL_ORE = 16, + E_BLOCK_LOG = 17, + E_BLOCK_LEAVES = 18, + E_BLOCK_SPONGE = 19, + E_BLOCK_GLASS = 20, + E_BLOCK_LAPIS_ORE = 21, + E_BLOCK_LAPIS_BLOCK = 22, + E_BLOCK_DISPENSER = 23, + E_BLOCK_SANDSTONE = 24, + E_BLOCK_NOTE_BLOCK = 25, + E_BLOCK_BED = 26, + E_BLOCK_POWERED_RAIL = 27, + E_BLOCK_DETECTOR_RAIL = 28, + E_BLOCK_STICKY_PISTON = 29, + E_BLOCK_COBWEB = 30, + E_BLOCK_TALL_GRASS = 31, + E_BLOCK_DEAD_BUSH = 32, + E_BLOCK_PISTON = 33, + E_BLOCK_PISTON_EXTENSION = 34, + E_BLOCK_WOOL = 35, + E_BLOCK_PISTON_MOVED_BLOCK = 36, + E_BLOCK_DANDELION = 37, + E_BLOCK_FLOWER = 38, + E_BLOCK_BROWN_MUSHROOM = 39, + E_BLOCK_RED_MUSHROOM = 40, + E_BLOCK_GOLD_BLOCK = 41, + E_BLOCK_IRON_BLOCK = 42, + E_BLOCK_DOUBLE_STONE_SLAB = 43, + E_BLOCK_STONE_SLAB = 44, + E_BLOCK_BRICK = 45, + E_BLOCK_TNT = 46, + E_BLOCK_BOOKCASE = 47, + E_BLOCK_MOSSY_COBBLESTONE = 48, + E_BLOCK_OBSIDIAN = 49, + E_BLOCK_TORCH = 50, + E_BLOCK_FIRE = 51, + E_BLOCK_MOB_SPAWNER = 52, + E_BLOCK_WOODEN_STAIRS = 53, + E_BLOCK_CHEST = 54, + E_BLOCK_REDSTONE_WIRE = 55, + E_BLOCK_DIAMOND_ORE = 56, + E_BLOCK_DIAMOND_BLOCK = 57, + E_BLOCK_CRAFTING_TABLE = 58, + E_BLOCK_WORKBENCH = 58, + E_BLOCK_CROPS = 59, + E_BLOCK_FARMLAND = 60, + E_BLOCK_FURNACE = 61, + E_BLOCK_LIT_FURNACE = 62, + E_BLOCK_BURNING_FURNACE = 62, + E_BLOCK_SIGN_POST = 63, + E_BLOCK_WOODEN_DOOR = 64, + E_BLOCK_LADDER = 65, + E_BLOCK_RAIL = 66, + E_BLOCK_MINECART_TRACKS = 66, + E_BLOCK_COBBLESTONE_STAIRS = 67, + E_BLOCK_WALLSIGN = 68, + E_BLOCK_LEVER = 69, + E_BLOCK_STONE_PRESSURE_PLATE = 70, + E_BLOCK_IRON_DOOR = 71, + E_BLOCK_WOODEN_PRESSURE_PLATE = 72, + E_BLOCK_REDSTONE_ORE = 73, + E_BLOCK_REDSTONE_ORE_GLOWING = 74, + E_BLOCK_REDSTONE_TORCH_OFF = 75, + E_BLOCK_REDSTONE_TORCH_ON = 76, + E_BLOCK_STONE_BUTTON = 77, + E_BLOCK_SNOW = 78, + E_BLOCK_ICE = 79, + E_BLOCK_SNOW_BLOCK = 80, + E_BLOCK_CACTUS = 81, + E_BLOCK_CLAY = 82, + E_BLOCK_SUGARCANE = 83, + E_BLOCK_REEDS = 83, + E_BLOCK_JUKEBOX = 84, + E_BLOCK_FENCE = 85, + E_BLOCK_PUMPKIN = 86, + E_BLOCK_NETHERRACK = 87, + E_BLOCK_SOULSAND = 88, + E_BLOCK_GLOWSTONE = 89, + E_BLOCK_NETHER_PORTAL = 90, + E_BLOCK_JACK_O_LANTERN = 91, + E_BLOCK_CAKE = 92, + E_BLOCK_REDSTONE_REPEATER_OFF = 93, + E_BLOCK_REDSTONE_REPEATER_ON = 94, + E_BLOCK_STAINED_GLASS = 95, + E_BLOCK_TRAPDOOR = 96, + E_BLOCK_SILVERFISH_EGG = 97, + E_BLOCK_STONE_BRICKS = 98, + E_BLOCK_HUGE_BROWN_MUSHROOM = 99, + E_BLOCK_HUGE_RED_MUSHROOM = 100, + E_BLOCK_IRON_BARS = 101, + E_BLOCK_GLASS_PANE = 102, + E_BLOCK_MELON = 103, + E_BLOCK_PUMPKIN_STEM = 104, + E_BLOCK_MELON_STEM = 105, + E_BLOCK_VINES = 106, + E_BLOCK_FENCE_GATE = 107, + E_BLOCK_BRICK_STAIRS = 108, + E_BLOCK_STONE_BRICK_STAIRS = 109, + E_BLOCK_MYCELIUM = 110, + E_BLOCK_LILY_PAD = 111, + E_BLOCK_NETHER_BRICK = 112, + E_BLOCK_NETHER_BRICK_FENCE = 113, + E_BLOCK_NETHER_BRICK_STAIRS = 114, + E_BLOCK_NETHER_WART = 115, + E_BLOCK_ENCHANTMENT_TABLE = 116, + E_BLOCK_BREWING_STAND = 117, + E_BLOCK_CAULDRON = 118, + E_BLOCK_END_PORTAL = 119, + E_BLOCK_END_PORTAL_FRAME = 120, + E_BLOCK_END_STONE = 121, + E_BLOCK_DRAGON_EGG = 122, + E_BLOCK_REDSTONE_LAMP_OFF = 123, + E_BLOCK_REDSTONE_LAMP_ON = 124, + E_BLOCK_DOUBLE_WOODEN_SLAB = 125, + E_BLOCK_WOODEN_SLAB = 126, + E_BLOCK_COCOA_POD = 127, + E_BLOCK_SANDSTONE_STAIRS = 128, + E_BLOCK_EMERALD_ORE = 129, + E_BLOCK_ENDER_CHEST = 130, + E_BLOCK_TRIPWIRE_HOOK = 131, + E_BLOCK_TRIPWIRE = 132, + E_BLOCK_EMERALD_BLOCK = 133, + E_BLOCK_SPRUCE_WOOD_STAIRS = 134, + E_BLOCK_BIRCH_WOOD_STAIRS = 135, + E_BLOCK_JUNGLE_WOOD_STAIRS = 136, + E_BLOCK_COMMAND_BLOCK = 137, + E_BLOCK_BEACON = 138, + E_BLOCK_COBBLESTONE_WALL = 139, + E_BLOCK_FLOWER_POT = 140, + E_BLOCK_CARROTS = 141, + E_BLOCK_POTATOES = 142, + E_BLOCK_WOODEN_BUTTON = 143, + E_BLOCK_HEAD = 144, + E_BLOCK_ANVIL = 145, + E_BLOCK_TRAPPED_CHEST = 146, + E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE = 147, + E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE = 148, + + E_BLOCK_INACTIVE_COMPARATOR = 149, + E_BLOCK_ACTIVE_COMPARATOR = 150, + E_BLOCK_DAYLIGHT_SENSOR = 151, + E_BLOCK_BLOCK_OF_REDSTONE = 152, + + E_BLOCK_NETHER_QUARTZ_ORE = 153, + E_BLOCK_HOPPER = 154, + E_BLOCK_QUARTZ_BLOCK = 155, + E_BLOCK_QUARTZ_STAIRS = 156, + E_BLOCK_ACTIVATOR_RAIL = 157, + + E_BLOCK_DROPPER = 158, + E_BLOCK_STAINED_CLAY = 159, + E_BLOCK_STAINED_GLASS_PANE = 160, + E_BLOCK_NEW_LEAVES = 161, // Acacia and Dark Oak IDs in Minecraft 1.7.x + E_BLOCK_NEW_LOG = 162, + E_BLOCK_ACACIA_WOOD_STAIRS = 163, + E_BLOCK_DARK_OAK_WOOD_STAIRS = 164, ///////////////////////////////// + E_BLOCK_HAY_BALE = 170, + E_BLOCK_CARPET = 171, + E_BLOCK_HARDENED_CLAY = 172, + E_BLOCK_BLOCK_OF_COAL = 173, + E_BLOCK_PACKED_ICE = 174, + E_BLOCK_BIG_FLOWER = 175, + + // Keep these two as the last values, without a number - they will get their correct number assigned automagically by C++ + // IsValidBlock() depends on this + E_BLOCK_NUMBER_OF_TYPES, ///< Number of individual (different) blocktypes + E_BLOCK_MAX_TYPE_ID = E_BLOCK_NUMBER_OF_TYPES - 1, ///< Maximum BlockType number used + + // Synonym or ID compatibility + + E_BLOCK_YELLOW_FLOWER = E_BLOCK_DANDELION, + E_BLOCK_RED_ROSE = E_BLOCK_FLOWER, + E_BLOCK_LOCKED_CHEST = E_BLOCK_STAINED_GLASS, +}; +// tolua_end + +// tolua_begin +enum ENUM_ITEM_ID +{ + E_ITEM_EMPTY = -1, + + E_ITEM_FIRST = 256, // First true item type + + E_ITEM_IRON_SHOVEL = 256, + E_ITEM_IRON_PICKAXE = 257, + E_ITEM_IRON_AXE = 258, + E_ITEM_FLINT_AND_STEEL = 259, + E_ITEM_RED_APPLE = 260, + E_ITEM_BOW = 261, + E_ITEM_ARROW = 262, + E_ITEM_COAL = 263, + E_ITEM_DIAMOND = 264, + E_ITEM_IRON = 265, + E_ITEM_GOLD = 266, + E_ITEM_IRON_SWORD = 267, + E_ITEM_WOODEN_SWORD = 268, + E_ITEM_WOODEN_SHOVEL = 269, + E_ITEM_WOODEN_PICKAXE = 270, + E_ITEM_WOODEN_AXE = 271, + E_ITEM_STONE_SWORD = 272, + E_ITEM_STONE_SHOVEL = 273, + E_ITEM_STONE_PICKAXE = 274, + E_ITEM_STONE_AXE = 275, + E_ITEM_DIAMOND_SWORD = 276, + E_ITEM_DIAMOND_SHOVEL = 277, + E_ITEM_DIAMOND_PICKAXE = 278, + E_ITEM_DIAMOND_AXE = 279, + E_ITEM_STICK = 280, + E_ITEM_BOWL = 281, + E_ITEM_MUSHROOM_SOUP = 282, + E_ITEM_GOLD_SWORD = 283, + E_ITEM_GOLD_SHOVEL = 284, + E_ITEM_GOLD_PICKAXE = 285, + E_ITEM_GOLD_AXE = 286, + E_ITEM_STRING = 287, + E_ITEM_FEATHER = 288, + E_ITEM_GUNPOWDER = 289, + E_ITEM_WOODEN_HOE = 290, + E_ITEM_STONE_HOE = 291, + E_ITEM_IRON_HOE = 292, + E_ITEM_DIAMOND_HOE = 293, + E_ITEM_GOLD_HOE = 294, + E_ITEM_SEEDS = 295, + E_ITEM_WHEAT = 296, + E_ITEM_BREAD = 297, + E_ITEM_LEATHER_CAP = 298, + E_ITEM_LEATHER_TUNIC = 299, + E_ITEM_LEATHER_PANTS = 300, + E_ITEM_LEATHER_BOOTS = 301, + E_ITEM_CHAIN_HELMET = 302, + E_ITEM_CHAIN_CHESTPLATE = 303, + E_ITEM_CHAIN_LEGGINGS = 304, + E_ITEM_CHAIN_BOOTS = 305, + E_ITEM_IRON_HELMET = 306, + E_ITEM_IRON_CHESTPLATE = 307, + E_ITEM_IRON_LEGGINGS = 308, + E_ITEM_IRON_BOOTS = 309, + E_ITEM_DIAMOND_HELMET = 310, + E_ITEM_DIAMOND_CHESTPLATE = 311, + E_ITEM_DIAMOND_LEGGINGS = 312, + E_ITEM_DIAMOND_BOOTS = 313, + E_ITEM_GOLD_HELMET = 314, + E_ITEM_GOLD_CHESTPLATE = 315, + E_ITEM_GOLD_LEGGINGS = 316, + E_ITEM_GOLD_BOOTS = 317, + E_ITEM_FLINT = 318, + E_ITEM_RAW_PORKCHOP = 319, + E_ITEM_COOKED_PORKCHOP = 320, + E_ITEM_PAINTINGS = 321, + E_ITEM_GOLDEN_APPLE = 322, + E_ITEM_SIGN = 323, + E_ITEM_WOODEN_DOOR = 324, + E_ITEM_BUCKET = 325, + E_ITEM_WATER_BUCKET = 326, + E_ITEM_LAVA_BUCKET = 327, + E_ITEM_MINECART = 328, + E_ITEM_SADDLE = 329, + E_ITEM_IRON_DOOR = 330, + E_ITEM_REDSTONE_DUST = 331, + E_ITEM_SNOWBALL = 332, + E_ITEM_BOAT = 333, + E_ITEM_LEATHER = 334, + E_ITEM_MILK = 335, + E_ITEM_CLAY_BRICK = 336, + E_ITEM_CLAY = 337, + E_ITEM_SUGARCANE = 338, + E_ITEM_SUGAR_CANE = 338, + E_ITEM_PAPER = 339, + E_ITEM_BOOK = 340, + E_ITEM_SLIMEBALL = 341, + E_ITEM_CHEST_MINECART = 342, + E_ITEM_FURNACE_MINECART = 343, + E_ITEM_EGG = 344, + E_ITEM_COMPASS = 345, + E_ITEM_FISHING_ROD = 346, + E_ITEM_CLOCK = 347, + E_ITEM_GLOWSTONE_DUST = 348, + E_ITEM_RAW_FISH = 349, + E_ITEM_COOKED_FISH = 350, + E_ITEM_DYE = 351, + E_ITEM_BONE = 352, + E_ITEM_SUGAR = 353, + E_ITEM_CAKE = 354, + E_ITEM_BED = 355, + E_ITEM_REDSTONE_REPEATER = 356, + E_ITEM_COOKIE = 357, + E_ITEM_MAP = 358, + E_ITEM_SHEARS = 359, + E_ITEM_MELON_SLICE = 360, + E_ITEM_PUMPKIN_SEEDS = 361, + E_ITEM_MELON_SEEDS = 362, + E_ITEM_RAW_BEEF = 363, + E_ITEM_STEAK = 364, + E_ITEM_RAW_CHICKEN = 365, + E_ITEM_COOKED_CHICKEN = 366, + E_ITEM_ROTTEN_FLESH = 367, + E_ITEM_ENDER_PEARL = 368, + E_ITEM_BLAZE_ROD = 369, + E_ITEM_GHAST_TEAR = 370, + E_ITEM_GOLD_NUGGET = 371, + E_ITEM_NETHER_WART = 372, + E_ITEM_POTIONS = 373, + E_ITEM_GLASS_BOTTLE = 374, + E_ITEM_SPIDER_EYE = 375, + E_ITEM_FERMENTED_SPIDER_EYE = 376, + E_ITEM_BLAZE_POWDER = 377, + E_ITEM_MAGMA_CREAM = 378, + E_ITEM_BREWING_STAND = 379, + E_ITEM_CAULDRON = 380, + E_ITEM_EYE_OF_ENDER = 381, + E_ITEM_GLISTERING_MELON = 382, + E_ITEM_SPAWN_EGG = 383, + E_ITEM_BOTTLE_O_ENCHANTING = 384, + E_ITEM_FIRE_CHARGE = 385, + E_ITEM_BOOK_AND_QUILL = 386, + E_ITEM_WRITTEN_BOOK = 387, + E_ITEM_EMERALD = 388, + E_ITEM_ITEM_FRAME = 389, + E_ITEM_FLOWER_POT = 390, + E_ITEM_CARROT = 391, + E_ITEM_POTATO = 392, + E_ITEM_BAKED_POTATO = 393, + E_ITEM_POISONOUS_POTATO = 394, + E_ITEM_EMPTY_MAP = 395, + E_ITEM_GOLDEN_CARROT = 396, + E_ITEM_HEAD = 397, + E_ITEM_CARROT_ON_STICK = 398, + E_ITEM_NETHER_STAR = 399, + E_ITEM_PUMPKIN_PIE = 400, + E_ITEM_FIREWORK_ROCKET = 401, + E_ITEM_FIREWORK_STAR = 402, + E_ITEM_ENCHANTED_BOOK = 403, + E_ITEM_COMPARATOR = 404, + E_ITEM_NETHER_BRICK = 405, + E_ITEM_NETHER_QUARTZ = 406, + E_ITEM_MINECART_WITH_TNT = 407, + E_ITEM_MINECART_WITH_HOPPER = 408, + E_ITEM_IRON_HORSE_ARMOR = 417, + E_ITEM_GOLD_HORSE_ARMOR = 418, + E_ITEM_DIAMOND_HORSE_ARMOR = 419, + E_ITEM_LEAD = 420, + E_ITEM_NAME_TAG = 421, + E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422, + + // Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++ + // IsValidItem() depends on this! + E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES, ///< Number of individual (different) consecutive itemtypes + E_ITEM_MAX_CONSECUTIVE_TYPE_ID = E_ITEM_NUMBER_OF_CONSECUTIVE_TYPES - 1, ///< Maximum consecutive ItemType number used + + E_ITEM_FIRST_DISC = 2256, + E_ITEM_13_DISC = 2256, + E_ITEM_CAT_DISC = 2257, + E_ITEM_BLOCKS_DISC = 2258, + E_ITEM_CHIRP_DISC = 2259, + E_ITEM_FAR_DISC = 2260, + E_ITEM_MALL_DISC = 2261, + E_ITEM_MELLOHI_DISC = 2262, + E_ITEM_STAL_DISC = 2263, + E_ITEM_STRAD_DISC = 2264, + E_ITEM_WARD_DISC = 2265, + E_ITEM_11_DISC = 2266, + E_ITEM_WAIT_DISC = 2267, + + // Keep these two as the last values of the disc list, without a number - they will get their correct number assigned automagically by C++ + // IsValidItem() depends on this! + E_ITEM_LAST_DISC_PLUS_ONE, ///< Useless, really, but needs to be present for the following value + E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1, ///< Maximum disc itemtype number used + + E_ITEM_LAST = E_ITEM_LAST_DISC, ///< Maximum valid ItemType +}; + + + + + +enum +{ + // Please keep this list alpha-sorted by the blocktype / itemtype part + // then number-sorted for the same block / item + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Block metas: + + // E_BLOCK_CHEST metas: + E_META_CHEST_FACING_ZM = 2, + E_META_CHEST_FACING_ZP = 3, + E_META_CHEST_FACING_XM = 4, + E_META_CHEST_FACING_XP = 5, + + // E_BLOCK_DISPENSER / E_BLOCK_DROPPER metas: + E_META_DROPSPENSER_FACING_YM = 0, + E_META_DROPSPENSER_FACING_YP = 1, + E_META_DROPSPENSER_FACING_ZM = 2, + E_META_DROPSPENSER_FACING_ZP = 3, + E_META_DROPSPENSER_FACING_XM = 4, + E_META_DROPSPENSER_FACING_XP = 5, + + // E_BLOCK_DOUBLE_STONE_SLAB metas: + E_META_DOUBLE_STONE_SLAB_STONE = 0, + E_META_DOUBLE_STONE_SLAB_SANDSTONE = 1, + E_META_DOUBLE_STONE_SLAB_WOODEN = 2, + E_META_DOUBLE_STONE_SLAB_COBBLESTONE = 3, + E_META_DOUBLE_STONE_SLAB_BRICK = 4, + E_META_DOUBLE_STONE_SLAB_STONE_BRICK = 5, + E_META_DOUBLE_STONE_SLAB_NETHER_BRICK = 6, + E_META_DOUBLE_STONE_SLAB_STONE_SECRET = 7, + + + + // E_BLOCK_HOPPER metas: + E_META_HOPPER_FACING_YM = 0, + E_META_HOPPER_UNATTACHED = 1, // Hopper doesn't move items up, there's no YP + E_META_HOPPER_FACING_ZM = 2, + E_META_HOPPER_FACING_ZP = 3, + E_META_HOPPER_FACING_XM = 4, + E_META_HOPPER_FACING_XP = 5, + + // E_BLOCK_LEAVES metas: + E_META_LEAVES_APPLE = 0, + E_META_LEAVES_CONIFER = 1, + E_META_LEAVES_BIRCH = 2, + E_META_LEAVES_JUNGLE = 3, + + // E_BLOCK_LOG metas: + E_META_LOG_APPLE = 0, + E_META_LOG_CONIFER = 1, + E_META_LOG_BIRCH = 2, + E_META_LOG_JUNGLE = 3, + + // E_BLOCK_PLANKS metas: + E_META_PLANKS_APPLE = 0, + E_META_PLANKS_CONIFER = 1, + E_META_PLANKS_BIRCH = 2, + E_META_PLANKS_JUNGLE = 3, + + // E_BLOCK_SANDSTONE metas: + E_META_SANDSTONE_NORMAL = 0, + E_META_SANDSTONE_ORNAMENT = 1, + E_META_SANDSTONE_SMOOTH = 2, + + // E_BLOCK_SAPLING metas (lowest 3 bits): + E_META_SAPLING_APPLE = 0, + E_META_SAPLING_CONIFER = 1, + E_META_SAPLING_BIRCH = 2, + E_META_SAPLING_JUNGLE = 3, + + // E_BLOCK_SILVERFISH_EGG metas: + E_META_SILVERFISH_EGG_STONE = 0, + E_META_SILVERFISH_EGG_COBBLESTONE = 1, + E_META_SILVERFISH_EGG_STONE_BRICK = 2, + + // E_BLOCK_STONE_SLAB metas: + E_META_STONE_SLAB_STONE = 0, + E_META_STONE_SLAB_SANDSTONE = 1, + E_META_STONE_SLAB_PLANKS = 2, + E_META_STONE_SLAB_COBBLESTONE = 3, + E_META_STONE_SLAB_BRICK = 4, + E_META_STONE_SLAB_STONE_BRICK = 5, + E_META_STONE_SLAB_NETHER_BRICK = 6, + E_META_STONE_SLAB_STONE_SECRET = 7, + + // E_BLOCK_STONE_BRICKS metas: + E_META_STONE_BRICK_NORMAL = 0, + E_META_STONE_BRICK_MOSSY = 1, + E_META_STONE_BRICK_CRACKED = 2, + E_META_STONE_BRICK_ORNAMENT = 3, + + // E_BLOCK_TALL_GRASS metas: + E_META_TALL_GRASS_DEAD_SHRUB = 0, + E_META_TALL_GRASS_GRASS = 1, + E_META_TALL_GRASS_FERN = 2, + + // E_BLOCK_TORCH, E_BLOCK_REDSTONE_TORCH_OFF, E_BLOCK_REDSTONE_TORCH_ON metas: + E_META_TORCH_EAST = 1, // east face of the block, pointing east + E_META_TORCH_WEST = 2, + E_META_TORCH_SOUTH = 3, + E_META_TORCH_NORTH = 4, + E_META_TORCH_FLOOR = 5, + E_META_TORCH_XM = 1, // Torch attached to the XM side of its block + E_META_TORCH_XP = 2, // Torch attached to the XP side of its block + E_META_TORCH_ZM = 3, // Torch attached to the ZM side of its block + E_META_TORCH_ZP = 4, // Torch attached to the ZP side of its block + + // E_BLOCK_WOODEN_DOUBLE_SLAB metas: + E_META_WOODEN_DOUBLE_SLAB_APPLE = 0, + E_META_WOODEN_DOUBLE_SLAB_CONIFER = 1, + E_META_WOODEN_DOUBLE_SLAB_BIRCH = 2, + E_META_WOODEN_DOUBLE_SLAB_JUNGLE = 3, + E_META_WOODEN_DOUBLE_SLAB_ACACIA = 4, + E_META_WOODEN_DOUBLE_SLAB_DARK_OAK = 5, + + // E_BLOCK_WOODEN_SLAB metas: + E_META_WOODEN_SLAB_APPLE = 0, + E_META_WOODEN_SLAB_CONIFER = 1, + E_META_WOODEN_SLAB_BIRCH = 2, + E_META_WOODEN_SLAB_JUNGLE = 3, + E_META_WOODEN_SLAB_ACACIA = 4, + E_META_WOODEN_SLAB_DARK_OAK = 5, + + // E_BLOCK_WOOL metas: + E_META_WOOL_WHITE = 0, + E_META_WOOL_ORANGE = 1, + E_META_WOOL_MAGENTA = 2, + E_META_WOOL_LIGHTBLUE = 3, + E_META_WOOL_YELLOW = 4, + E_META_WOOL_LIGHTGREEN = 5, + E_META_WOOL_PINK = 6, + E_META_WOOL_GRAY = 7, + E_META_WOOL_LIGHTGRAY = 8, + E_META_WOOL_CYAN = 9, + E_META_WOOL_PURPLE = 10, + E_META_WOOL_BLUE = 11, + E_META_WOOL_BROWN = 12, + E_META_WOOL_GREEN = 13, + E_META_WOOL_RED = 14, + E_META_WOOL_BLACK = 15, + + // E_BLOCK_CARPET metas: + E_META_CARPET_WHITE = 0, + E_META_CARPET_ORANGE = 1, + E_META_CARPET_MAGENTA = 2, + E_META_CARPET_LIGHTBLUE = 3, + E_META_CARPET_YELLOW = 4, + E_META_CARPET_LIGHTGREEN = 5, + E_META_CARPET_PINK = 6, + E_META_CARPET_GRAY = 7, + E_META_CARPET_LIGHTGRAY = 8, + E_META_CARPET_CYAN = 9, + E_META_CARPET_PURPLE = 10, + E_META_CARPET_BLUE = 11, + E_META_CARPET_BROWN = 12, + E_META_CARPET_GREEN = 13, + E_META_CARPET_RED = 14, + E_META_CARPET_BLACK = 15, + + // E_BLOCK_STAINED_CLAY metas + E_META_STAINED_CLAY_WHITE = 0, + E_META_STAINED_CLAY_ORANGE = 1, + E_META_STAINED_CLAY_MAGENTA = 2, + E_META_STAINED_CLAY_LIGHTBLUE = 3, + E_META_STAINED_CLAY_YELLOW = 4, + E_META_STAINED_CLAY_LIGHTGREEN = 5, + E_META_STAINED_CLAY_PINK = 6, + E_META_STAINED_CLAY_GRAY = 7, + E_META_STAINED_CLAY_LIGHTGRAY = 8, + E_META_STAINED_CLAY_CYAN = 9, + E_META_STAINED_CLAY_PURPLE = 10, + E_META_STAINED_CLAY_BLUE = 11, + E_META_STAINED_CLAY_BROWN = 12, + E_META_STAINED_CLAY_GREEN = 13, + E_META_STAINED_CLAY_RED = 14, + E_META_STAINED_CLAY_BLACK = 15, + + // E_BLOCK_STAINED_GLASS metas + E_META_STAINED_GLASS_WHITE = 0, + E_META_STAINED_GLASS_ORANGE = 1, + E_META_STAINED_GLASS_MAGENTA = 2, + E_META_STAINED_GLASS_LIGHTBLUE = 3, + E_META_STAINED_GLASS_YELLOW = 4, + E_META_STAINED_GLASS_LIGHTGREEN = 5, + E_META_STAINED_GLASS_PINK = 6, + E_META_STAINED_GLASS_GRAY = 7, + E_META_STAINED_GLASS_LIGHTGRAY = 8, + E_META_STAINED_GLASS_CYAN = 9, + E_META_STAINED_GLASS_PURPLE = 10, + E_META_STAINED_GLASS_BLUE = 11, + E_META_STAINED_GLASS_BROWN = 12, + E_META_STAINED_GLASS_GREEN = 13, + E_META_STAINED_GLASS_RED = 14, + E_META_STAINED_GLASS_BLACK = 15, + + // E_BLOCK_STAINED_GLASS_PANE metas + E_META_STAINED_GLASS_PANE_WHITE = 0, + E_META_STAINED_GLASS_PANE_ORANGE = 1, + E_META_STAINED_GLASS_PANE_MAGENTA = 2, + E_META_STAINED_GLASS_PANE_LIGHTBLUE = 3, + E_META_STAINED_GLASS_PANE_YELLOW = 4, + E_META_STAINED_GLASS_PANE_LIGHTGREEN = 5, + E_META_STAINED_GLASS_PANE_PINK = 6, + E_META_STAINED_GLASS_PANE_GRAY = 7, + E_META_STAINED_GLASS_PANE_LIGHTGRAY = 8, + E_META_STAINED_GLASS_PANE_CYAN = 9, + E_META_STAINED_GLASS_PANE_PURPLE = 10, + E_META_STAINED_GLASS_PANE_BLUE = 11, + E_META_STAINED_GLASS_PANE_BROWN = 12, + E_META_STAINED_GLASS_PANE_GREEN = 13, + E_META_STAINED_GLASS_PANE_RED = 14, + E_META_STAINED_GLASS_PANE_BLACK = 15, + + // E_BLOCK_SNOW metas: + E_META_SNOW_LAYER_ONE = 0, + E_META_SNOW_LAYER_TWO = 1, + E_META_SNOW_LAYER_THREE = 2, + E_META_SNOW_LAYER_FOUR = 3, + E_META_SNOW_LAYER_FIVE = 4, + E_META_SNOW_LAYER_SIX = 5, + E_META_SNOW_LAYER_SEVEN = 6, + E_META_SNOW_LAYER_EIGHT = 7, + + // E_BLOCK_RAIL metas + E_META_RAIL_ZM_ZP = 0, + E_META_RAIL_XM_XP = 1, + E_META_RAIL_ASCEND_XP = 2, + E_META_RAIL_ASCEND_XM = 3, + E_META_RAIL_ASCEND_ZM = 4, + E_META_RAIL_ASCEND_ZP = 5, + E_META_RAIL_CURVED_ZP_XP = 6, + E_META_RAIL_CURVED_ZP_XM = 7, + E_META_RAIL_CURVED_ZM_XM = 8, + E_META_RAIL_CURVED_ZM_XP = 9, + + //E_BLOCK_NEW_LEAVES metas + E_META_NEW_LEAVES_ACACIA_WOOD = 0, + E_META_NEW_LEAVES_DARK_OAK_WOOD = 1, + + //E_BLOCK_NEW_LOG metas + E_META_NEW_LOG_ACACIA_WOOD = 0, + E_META_NEW_LOG_DARK_OAK_WOOD = 1, + + //E_BLOCK_FLOWER metas + E_META_FLOWER_POPPY = 0, + E_META_FLOWER_BLUE_ORCHID = 1, + E_META_FLOWER_ALLIUM = 2, + E_META_FLOWER_RED_TULIP = 4, + E_META_FLOWER_ORANGE_TULIP = 5, + E_META_FLOWER_WHITE_TULIP = 6, + E_META_FLOWER_PINK_TULIP = 7, + E_META_FLOWER_OXEYE_DAISY = 8, + + //E_BLOCK_BIG_FLOWER metas + E_META_BIG_FLOWER_SUNFLOWER = 0, + E_META_BIG_FLOWER_LILAC = 1, + E_META_BIG_FLOWER_DOUBLE_TALL_GRASS = 2, + E_META_BIG_FLOWER_LARGE_FERN = 3, + E_META_BIG_FLOWER_ROSE_BUSH = 4, + E_META_BIG_FLOWER_PEONY = 5, + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Item metas: + + // E_ITEM_COAL metas: + E_META_COAL_NORMAL = 0, + E_META_COAL_CHARCOAL = 1, + + // E_ITEM_DYE metas: + E_META_DYE_BLACK = 0, + E_META_DYE_RED = 1, + E_META_DYE_GREEN = 2, + E_META_DYE_BROWN = 3, + E_META_DYE_BLUE = 4, + E_META_DYE_PURPLE = 5, + E_META_DYE_CYAN = 6, + E_META_DYE_LIGHTGRAY = 7, + E_META_DYE_GRAY = 8, + E_META_DYE_PINK = 9, + E_META_DYE_LIGHTGREEN = 10, + E_META_DYE_YELLOW = 11, + E_META_DYE_LIGHTBLUE = 12, + E_META_DYE_MAGENTA = 13, + E_META_DYE_ORANGE = 14, + E_META_DYE_WHITE = 15, + + // E_ITEM_GOLDEN_APPLE metas: + E_META_GOLDEN_APPLE_NORMAL = 0, + E_META_GOLDEN_APPLE_ENCHANTED = 1, + + // E_ITEM_RAW_FISH metas: + E_META_RAW_FISH_FISH = 0, + E_META_RAW_FISH_SALMON = 1, + E_META_RAW_FISH_CLOWNFISH = 2, + E_META_RAW_FISH_PUFFERFISH = 3, + + // E_ITEM_COOKED_FISH metas: + E_META_COOKED_FISH_FISH = 0, + E_META_COOKED_FISH_SALMON = 1, + E_META_COOKED_FISH_CLOWNFISH = 2, + E_META_COOKED_FISH_PUFFERFISH = 3, + + // E_ITEM_MINECART_TRACKS metas: + E_META_TRACKS_X = 1, + E_META_TRACKS_Z = 0, + + // E_ITEM_SPAWN_EGG metas: + // See also cMonster::eType, since monster type and spawn egg meta are the same + E_META_SPAWN_EGG_PICKUP = 1, + E_META_SPAWN_EGG_EXPERIENCE_ORB = 2, + E_META_SPAWN_EGG_LEASH_KNOT = 8, + E_META_SPAWN_EGG_PAINTING = 9, + E_META_SPAWN_EGG_ARROW = 10, + E_META_SPAWN_EGG_SNOWBALL = 11, + E_META_SPAWN_EGG_FIREBALL = 12, + E_META_SPAWN_EGG_SMALL_FIREBALL = 13, + E_META_SPAWN_EGG_ENDER_PEARL = 14, + E_META_SPAWN_EGG_EYE_OF_ENDER = 15, + E_META_SPAWN_EGG_SPLASH_POTION = 16, + E_META_SPAWN_EGG_EXP_BOTTLE = 17, + E_META_SPAWN_EGG_ITEM_FRAME = 18, + E_META_SPAWN_EGG_WITHER_SKULL = 19, + E_META_SPAWN_EGG_PRIMED_TNT = 20, + E_META_SPAWN_EGG_FALLING_BLOCK = 21, + E_META_SPAWN_EGG_FIREWORK = 22, + E_META_SPAWN_EGG_BOAT = 41, + E_META_SPAWN_EGG_MINECART = 42, + E_META_SPAWN_EGG_MINECART_CHEST = 43, + E_META_SPAWN_EGG_MINECART_FURNACE = 44, + E_META_SPAWN_EGG_MINECART_TNT = 45, + E_META_SPAWN_EGG_MINECART_HOPPER = 46, + E_META_SPAWN_EGG_MINECART_SPAWNER = 47, + E_META_SPAWN_EGG_CREEPER = 50, + E_META_SPAWN_EGG_SKELETON = 51, + E_META_SPAWN_EGG_SPIDER = 52, + E_META_SPAWN_EGG_GIANT = 53, + E_META_SPAWN_EGG_ZOMBIE = 54, + E_META_SPAWN_EGG_SLIME = 55, + E_META_SPAWN_EGG_GHAST = 56, + E_META_SPAWN_EGG_ZOMBIE_PIGMAN = 57, + E_META_SPAWN_EGG_ENDERMAN = 58, + E_META_SPAWN_EGG_CAVE_SPIDER = 59, + E_META_SPAWN_EGG_SILVERFISH = 60, + E_META_SPAWN_EGG_BLAZE = 61, + E_META_SPAWN_EGG_MAGMA_CUBE = 62, + E_META_SPAWN_EGG_ENDER_DRAGON = 63, + E_META_SPAWN_EGG_WITHER = 64, + E_META_SPAWN_EGG_BAT = 65, + E_META_SPAWN_EGG_WITCH = 66, + E_META_SPAWN_EGG_PIG = 90, + E_META_SPAWN_EGG_SHEEP = 91, + E_META_SPAWN_EGG_COW = 92, + E_META_SPAWN_EGG_CHICKEN = 93, + E_META_SPAWN_EGG_SQUID = 94, + E_META_SPAWN_EGG_WOLF = 95, + E_META_SPAWN_EGG_MOOSHROOM = 96, + E_META_SPAWN_EGG_SNOW_GOLEM = 97, + E_META_SPAWN_EGG_OCELOT = 98, + E_META_SPAWN_EGG_IRON_GOLEM = 99, + E_META_SPAWN_EGG_HORSE = 100, + E_META_SPAWN_EGG_VILLAGER = 120, + E_META_SPAWN_EGG_ENDER_CRYSTAL = 200, +} ; + + + + + +/// Dimension of a world +enum eDimension +{ + dimNether = -1, + dimOverworld = 0, + dimEnd = 1, +} ; + + + + + +/// Damage type, used in the TakeDamageInfo structure and related functions +enum eDamageType +{ + // Canonical names for the types (as documented in the plugin wiki): + dtAttack, // Being attacked by a mob + dtRangedAttack, // Being attacked by a projectile, possibly from a mob + dtLightning, // Hit by a lightning strike + dtFalling, // Falling down; dealt when hitting the ground + dtDrowning, // Drowning in water / lava + dtSuffocating, // Suffocating inside a block + dtStarving, // Hunger + dtCactusContact, // Contact with a cactus block + dtLavaContact, // Contact with a lava block + dtPoisoning, // Having the poison effect + dtOnFire, // Being on fire + dtFireContact, // Standing inside a fire block + dtInVoid, // Falling into the Void (Y < 0) + dtPotionOfHarming, + dtEnderPearl, // Thrown an ender pearl, teleported by it + dtAdmin, // Damage applied by an admin command + + // Some common synonyms: + dtPawnAttack = dtAttack, + dtEntityAttack = dtAttack, + dtMob = dtAttack, + dtMobAttack = dtAttack, + dtArrowAttack = dtRangedAttack, + dtArrow = dtRangedAttack, + dtProjectile = dtRangedAttack, + dtFall = dtFalling, + dtDrown = dtDrowning, + dtSuffocation = dtSuffocating, + dtStarvation = dtStarving, + dtHunger = dtStarving, + dtCactus = dtCactusContact, + dtCactuses = dtCactusContact, + dtCacti = dtCactusContact, + dtLava = dtLavaContact, + dtPoison = dtPoisoning, + dtBurning = dtOnFire, + dtInFire = dtFireContact, + dtPlugin = dtAdmin, +} ; + + + + + +enum eExplosionSource +{ + esOther, + esPrimedTNT, + esCreeper, + esBed, + esEnderCrystal, + esGhastFireball, + esWitherSkullBlack, + esWitherSkullBlue, + esWitherBirth, + esPlugin +} ; + +// tolua_end + + + + +// fwd: +class cItem; +class cIniFile; + + + + + +// tolua_begin + +/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure. +extern BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString); + +/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful. +extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item); + +/// Translates a full item into a string. If the ItemType is not recognized, the ItemType number is output into the string. +extern AString ItemToString(const cItem & a_Item); + +/// Translates itemtype into a string. If the type is not recognized, the itemtype number is output into the string. +extern AString ItemTypeToString(short a_ItemType); + +/// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string. +extern AString ItemToFullString(const cItem & a_Item); + +/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure. +extern EMCSBiome StringToBiome(const AString & a_BiomeString); + +/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT) +extern int StringToMobType(const AString & a_MobString); + +/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns -1000 on failure +extern eDimension StringToDimension(const AString & a_DimensionString); + +/// Translates damage type constant to a string representation (built-in). +extern AString DamageTypeToString(eDamageType a_DamageType); + +/// Translates a damage type string to damage type. Takes either a number or a damage type alias (built-in). Returns -1 on failure +extern eDamageType StringToDamageType(const AString & a_DamageString); + +/// Returns a cItem representing the item described in an IniFile's value; if the value doesn't exist, creates it with the provided default. +extern cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default); + +// tolua_end + + + + + +// Block properties: +extern NIBBLETYPE g_BlockLightValue[256]; +extern NIBBLETYPE g_BlockSpreadLightFalloff[256]; +extern bool g_BlockTransparent[256]; +extern bool g_BlockOneHitDig[256]; +extern bool g_BlockPistonBreakable[256]; +extern bool g_BlockIsSnowable[256]; +extern bool g_BlockRequiresSpecialTool[256]; +extern bool g_BlockIsSolid[256]; +extern bool g_BlockIsTorchPlaceable[256]; + + + + diff --git a/src/BlockTracer.h b/src/BlockTracer.h new file mode 100644 index 000000000..d0a34811d --- /dev/null +++ b/src/BlockTracer.h @@ -0,0 +1,104 @@ + +// BlockTracer.h + +// Declares the classes common for all blocktracers + + + + + +#pragma once + + + + + +// fwd: World.h +class cWorld; + + + + + +class cBlockTracer abstract +{ +public: + /** The callback class is used to notify the caller of individual events that are being traced. + */ + class cCallbacks abstract + { + public: + /** Called on each block encountered along the path, including the first block (path start) + When this callback returns true, the tracing is aborted. + */ + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) = 0; + + /** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded + When this callback returns true, the tracing is aborted. + */ + virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) { return false; } + + /** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height) + The coords specify the exact point at which the path exited the world. + If this callback returns true, the tracing is aborted. + Note that some paths can go out of the world and come back again (parabola), + in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls + */ + virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; } + + /** Called when the path goes into the world, from either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height) + The coords specify the exact point at which the path entered the world. + If this callback returns true, the tracing is aborted. + Note that some paths can go out of the world and come back again (parabola), + in such a case this callback is followed by further OnNextBlock() calls + */ + virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; } + + /** Called when the path is sure not to hit any more blocks. + Note that for some shapes this might never happen (line with constant Y) + */ + virtual void OnNoMoreHits(void) {} + + /** Called when the block tracing walks into a chunk that is not allocated. + This usually means that the tracing is aborted. + */ + virtual void OnNoChunk(void) {} + } ; + + + /// Creates the BlockTracer parent with the specified callbacks + cBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : + m_World(&a_World), + m_Callbacks(&a_Callbacks) + { + } + + + /// Sets new world, returns the old one. Note that both need to be valid + cWorld & SetWorld(cWorld & a_World) + { + cWorld & Old = *m_World; + m_World = &a_World; + return Old; + } + + + /// Sets new callbacks, returns the old ones. Note that both need to be valid + cCallbacks & SetCallbacks(cCallbacks & a_NewCallbacks) + { + cCallbacks & Old = *m_Callbacks; + m_Callbacks = &a_NewCallbacks; + return Old; + } + +protected: + /// The world upon which to operate + cWorld * m_World; + + /// The callback to use for reporting + cCallbacks * m_Callbacks; +} ; + + + + diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp new file mode 100644 index 000000000..66eb9130c --- /dev/null +++ b/src/Blocks/BlockBed.cpp @@ -0,0 +1,88 @@ +#include "Globals.h" +#include "BlockBed.h" + + + + + +void cBlockBedHandler::OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + if (a_BlockMeta < 8) + { + Vector3i Direction = MetaDataToDirection(a_BlockMeta); + a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); + } +} + + + + + +void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ ); + Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 ); + if (OldMeta & 0x8) + { + // Was pillow + if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED) + { + a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0); + } + } + else + { + // Was foot end + if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED) + { + a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0); + } + } +} + + + + + +void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + if (a_World->GetDimension() != dimOverworld) + { + Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ); + a_World->DoExplosionAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords); + } + else + { + if (a_World->GetTimeOfDay() > 13000) + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta & 0x8) + { + // Is pillow + a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + // Is foot end + Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); + if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping + { + a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); + } + } + } else { + a_Player->SendMessage("You can only sleep at night"); + } + } +} + + + + diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h new file mode 100644 index 000000000..8a289b22c --- /dev/null +++ b/src/Blocks/BlockBed.h @@ -0,0 +1,67 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cBlockBedHandler : + public cBlockHandler +{ +public: + cBlockBedHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to zero + a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); + } + + + // Bed specific helper functions + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 180 + (180 / 4); // So its not aligned with axis + if (a_Rotation > 360) a_Rotation -= 360; + + a_Rotation = (a_Rotation / 360) * 4; + + return ((char)a_Rotation + 2) % 4; + } + + + static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData) + { + switch (a_MetaData) + { + case 0: return Vector3i(0, 0, 1); + case 1: return Vector3i(-1, 0, 0); + case 2: return Vector3i(0, 0, -1); + case 3: return Vector3i(1, 0, 0); + } + return Vector3i(); + } +} ; + + + + diff --git a/src/Blocks/BlockBrewingStand.h b/src/Blocks/BlockBrewingStand.h new file mode 100644 index 000000000..57642bcb6 --- /dev/null +++ b/src/Blocks/BlockBrewingStand.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockBrewingStandHandler : + public cBlockHandler +{ +public: + cBlockBrewingStandHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0)); + } + + virtual bool IsUseable() override + { + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockButton.cpp b/src/Blocks/BlockButton.cpp new file mode 100644 index 000000000..19b055b62 --- /dev/null +++ b/src/Blocks/BlockButton.cpp @@ -0,0 +1,32 @@ + +#include "Globals.h" +#include "BlockButton.h" + + + + + +cBlockButtonHandler::cBlockButtonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockButtonHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + // Flip the ON bit on/off using the XOR bitwise operation + NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta); + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); + + // Queue a button reset (unpress) + a_World->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f), m_BlockType == E_BLOCK_STONE_BUTTON ? 20 : 30); +} + + + + diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h new file mode 100644 index 000000000..15649acc0 --- /dev/null +++ b/src/Blocks/BlockButton.h @@ -0,0 +1,93 @@ +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockButtonHandler : + public cBlockHandler +{ +public: + cBlockButtonHandler(BLOCKTYPE a_BlockType); + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = BlockFaceToMetaData(a_BlockFace); + return true; + } + + + virtual const char * GetStepSound(void) override + { + return m_BlockType == E_BLOCK_WOODEN_BUTTON ? "step.wood" : "step.stone"; + } + + + inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_ZM: { return 0x4; } + case BLOCK_FACE_ZP: { return 0x3; } + case BLOCK_FACE_XM: { return 0x2; } + case BLOCK_FACE_XP: { return 0x1; } + default: + { + ASSERT(!"Unhandled block face!"); + return 0x0; // No idea, give a special meta (button in centre of block) + } + } + } + + inline static NIBBLETYPE BlockMetaDataToBlockFace(NIBBLETYPE a_Meta) + { + switch (a_Meta & 0x7) + { + case 0x1: return BLOCK_FACE_XP; + case 0x2: return BLOCK_FACE_XM; + case 0x3: return BLOCK_FACE_ZP; + case 0x4: return BLOCK_FACE_ZM; + default: + { + ASSERT(!"Unhandled block meta!"); + return BLOCK_FACE_NONE; + } + } + } + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + + AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockMetaDataToBlockFace(Meta), true); + return (a_RelY > 0) && (g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY, a_RelZ)]); + } +} ; + + + + diff --git a/src/Blocks/BlockCactus.h b/src/Blocks/BlockCactus.h new file mode 100644 index 000000000..4147ad473 --- /dev/null +++ b/src/Blocks/BlockCactus.h @@ -0,0 +1,82 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCactusHandler : + public cBlockHandler +{ +public: + cBlockCactusHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); + if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS)) + { + // Cactus can only be placed on sand and itself + return false; + } + + // Check surroundings. Cacti may ONLY be surrounded by air + static const struct + { + int x, z; + } Coords[] = + { + {-1, 0}, + { 1, 0}, + { 0, -1}, + { 0, 1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ( + a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && + (BlockType != E_BLOCK_AIR) + ) + { + return false; + } + } // for i - Coords[] + + return true; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1); + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/src/Blocks/BlockCarpet.h b/src/Blocks/BlockCarpet.h new file mode 100644 index 000000000..5eafd8c21 --- /dev/null +++ b/src/Blocks/BlockCarpet.h @@ -0,0 +1,60 @@ + +// BlockCarpet.h + +// Declares the cBlockCarpetHandler class representing the handler for the carpet block + + + + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCarpetHandler : + public cBlockHandler +{ +public: + cBlockCarpetHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = a_Player->GetEquippedItem().m_ItemDamage & 0x0f; + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_CARPET, 1, a_BlockMeta)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR); + } +} ; + + + + diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h new file mode 100644 index 000000000..b0e00f869 --- /dev/null +++ b/src/Blocks/BlockCauldron.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCauldronHandler : + public cBlockHandler +{ +public: + cBlockCauldronHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0)); + } + + void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); + switch( a_Player->GetEquippedItem().m_ItemType ) + { + case E_ITEM_WATER_BUCKET: + { + a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 ); + a_Player->GetInventory().RemoveOneEquippedItem(); + cItem NewItem(E_ITEM_BUCKET, 1); + a_Player->GetInventory().AddItem(NewItem); + break; + } + case E_ITEM_GLASS_BOTTLE: + { + if( Meta > 0 ) + { + a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta); + a_Player->GetInventory().RemoveOneEquippedItem(); + cItem NewItem(E_ITEM_POTIONS, 1, 0); + a_Player->GetInventory().AddItem(NewItem); + } + break; + } + } + } + + virtual bool IsUseable() override + { + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h new file mode 100644 index 000000000..488c58ac5 --- /dev/null +++ b/src/Blocks/BlockChest.h @@ -0,0 +1,223 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" +#include "../BlockArea.h" +#include "../Entities/Player.h" + + + + + +class cBlockChestHandler : + public cBlockEntityHandler +{ +public: + cBlockChestHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // Is there a doublechest already next to this block? + if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ)) + { + // Yup, cannot form a triple-chest, refuse: + return false; + } + + // Check if this forms a doublechest, if so, need to adjust the meta: + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) + { + return false; + } + double rot = a_Player->GetRotation(); + if ( + (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + ) + { + a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + return true; + } + if ( + (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + ) + { + a_BlockMeta = (rot < 0) ? 4 : 5; + return true; + } + + // Single chest, get meta from rotation only + a_BlockMeta = RotationToMetaData(rot); + return true; + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override + { + // Check if this forms a doublechest, if so, need to adjust the meta: + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) + { + return; + } + + double rot = a_Player->GetRotation(); + // Choose meta from player rotation, choose only between 2 or 3 + NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + if ( + CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) || + CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta) + ) + { + // Forming a double chest in the X direction + return; + } + // Choose meta from player rotation, choose only between 4 or 5 + NewMeta = (rot < 0) ? 4 : 5; + if ( + CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) || + CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta) + ) + { + // Forming a double chest in the Z direction + return; + } + + // Single chest, no further processing needed + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2)) + { + // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow. + return false; + } + + int NumChestNeighbors = 0; + if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + return (NumChestNeighbors < 2); + } + + + /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + + if (a_Rotation > 360.f) + { + a_Rotation -= 360.f; + } + if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) + { + return 0x4; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x5; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + + + /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. + bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) + { + if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST) + { + return false; + } + a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta); + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockCloth.h b/src/Blocks/BlockCloth.h new file mode 100644 index 000000000..a136d3b9d --- /dev/null +++ b/src/Blocks/BlockCloth.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockClothHandler : + public cBlockHandler +{ +public: + cBlockClothHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/src/Blocks/BlockCobWeb.h b/src/Blocks/BlockCobWeb.h new file mode 100644 index 000000000..982bfaa30 --- /dev/null +++ b/src/Blocks/BlockCobWeb.h @@ -0,0 +1,30 @@ + +// BlockCobWeb.h + +// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs + +#pragma once + + + + + +class cBlockCobWebHandler : + public cBlockHandler +{ +public: + cBlockCobWebHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override + { + a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockComparator.cpp b/src/Blocks/BlockComparator.cpp new file mode 100644 index 000000000..8bc0ac5ac --- /dev/null +++ b/src/Blocks/BlockComparator.cpp @@ -0,0 +1,53 @@ + +#include "Globals.h" +#include "BlockComparator.h" +#include "BlockRedstoneRepeater.h" +#include "../Entities/Player.h" + + + + + +cBlockComparatorHandler::cBlockComparatorHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockComparatorHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + + +void cBlockComparatorHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + Meta ^= 0x04; // Toggle 3rd (addition/subtraction) bit with XOR + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); +} + + + + +bool cBlockComparatorHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; +} + + + + diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h new file mode 100644 index 000000000..cb2941d3c --- /dev/null +++ b/src/Blocks/BlockComparator.h @@ -0,0 +1,55 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockComparatorHandler : + public cBlockHandler +{ +public: + cBlockComparatorHandler(BLOCKTYPE a_BlockType); + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_COMPARATOR, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockCrops.h b/src/Blocks/BlockCrops.h new file mode 100644 index 000000000..9dd65aae2 --- /dev/null +++ b/src/Blocks/BlockCrops.h @@ -0,0 +1,114 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +/// Common class that takes care of carrots, potatoes and wheat +class cBlockCropsHandler : + public cBlockHandler +{ +public: + cBlockCropsHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override + { + MTRand rand; + + if (a_Meta == 0x7) + { + // Is fully grown, drop the entire produce: + switch (m_BlockType) + { + case E_BLOCK_CROPS: + { + a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0)); + a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + break; + } + case E_BLOCK_CARROTS: + { + a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + break; + } + case E_BLOCK_POTATOES: + { + a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + if (rand.randInt(20) == 0) + { + // With a 5% chance, drop a poisonous potato as well + a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0)); + } + break; + } + default: + { + ASSERT(!"Unhandled block type"); + break; + } + } // switch (m_BlockType) + } + else + { + // Drop 1 item of whatever is growing + switch (m_BlockType) + { + case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break; + case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break; + case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break; + default: + { + ASSERT(!"Unhandled block type"); + break; + } + } + } + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE Light = a_World->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE SkyLight = a_World->GetBlockSkyLight(a_BlockX, a_BlockY, a_BlockZ); + + if (SkyLight > Light) + { + Light = SkyLight; + } + + if ((Meta < 7) && (Light > 8)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); + } + else if (Light < 9) + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockDeadBush.h b/src/Blocks/BlockDeadBush.h new file mode 100644 index 000000000..14617d006 --- /dev/null +++ b/src/Blocks/BlockDeadBush.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockDeadBushHandler : + public cBlockHandler +{ +public: + cBlockDeadBushHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Don't drop anything + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND); + } +} ; + + + + diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h new file mode 100644 index 000000000..c694d79f6 --- /dev/null +++ b/src/Blocks/BlockDirt.h @@ -0,0 +1,88 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +/// Handler used for both dirt and grass +class cBlockDirtHandler : + public cBlockHandler +{ +public: + cBlockDirtHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + if (m_BlockType != E_BLOCK_GRASS) + { + return; + } + + // Grass becomes dirt if there is something on top of it: + if (a_BlockY < cChunkDef::Height - 1) + { + BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + if ((!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) || IsBlockWater(Above)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); + return; + } + } + + // Grass spreads to adjacent blocks: + MTRand rand; + for (int i = 0; i < 2; i++) // Pick two blocks to grow to + { + int OfsX = rand.randInt(2) - 1; // [-1 .. 1] + int OfsY = rand.randInt(4) - 3; // [-3 .. 1] + int OfsZ = rand.randInt(2) - 1; // [-1 .. 1] + + BLOCKTYPE DestBlock; + NIBBLETYPE DestMeta; + if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1)) + { + // Y Coord out of range + continue; + } + bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta); + if (!IsValid || (DestBlock != E_BLOCK_DIRT)) + { + continue; + } + + BLOCKTYPE AboveDest; + NIBBLETYPE AboveMeta; + IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta); + ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid? + if ((g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) && !IsBlockWater(AboveDest)) + { + a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0); + } + } // for i - repeat twice + } + + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp new file mode 100644 index 000000000..e71ccd368 --- /dev/null +++ b/src/Blocks/BlockDoor.cpp @@ -0,0 +1,90 @@ + +#include "Globals.h" +#include "BlockDoor.h" +#include "../Item.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (OldMeta & 8) + { + // Was upper part of door + if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + } + } + else + { + // Was lower part + if (IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); + } + } +} + + + + + +void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) + { + ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cBlockDoorHandler::OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + NIBBLETYPE a_TopBlockMeta = 8; + if ( + (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) || + (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) || + (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) || + (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType) + ) + { + a_TopBlockMeta = 9; + } + a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); +} + + + + + +const char * cBlockDoorHandler::GetStepSound(void) +{ + return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone"; +} + + + + diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h new file mode 100644 index 000000000..03a79d47d --- /dev/null +++ b/src/Blocks/BlockDoor.h @@ -0,0 +1,175 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cBlockDoorHandler : + public cBlockHandler +{ +public: + cBlockDoorHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual const char * GetStepSound(void) override; + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // If clicking a bottom face, place the door one block lower: + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + a_BlockY--; + } + + if ( + !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) || + !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) + ) + { + return false; + } + + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0)); + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override; + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + bool CanReplaceBlock(BLOCKTYPE a_BlockType) + { + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_SNOW: + case E_BLOCK_FIRE: + { + return true; + } + } + return false; + } + + + /// Converts the player's yaw to placed door's blockmeta + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 90 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + + + /// Returns true if the specified blocktype is any kind of door + inline static bool IsDoor(BLOCKTYPE a_Block) + { + return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR); + } + + + /// Returns the metadata for the opposite door state (open vs closed) + static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData) + { + return a_MetaData ^ 4; + } + + + /// Changes the door at the specified coords from open to close or vice versa + static void ChangeDoor(cWorld * a_World, int a_X, int a_Y, int a_Z) + { + NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); + + a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData)); + + if (OldMetaData & 8) + { + // Current block is top of the door + BLOCKTYPE BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); + NIBBLETYPE BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); + + if (IsDoor(BottomBlock) && !(BottomMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta)); + } + } + else + { + // Current block is bottom of the door + BLOCKTYPE TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); + NIBBLETYPE TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); + + if (IsDoor(TopBlock) && (TopMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta)); + } + } + } + + +} ; + + + + diff --git a/src/Blocks/BlockDropSpenser.h b/src/Blocks/BlockDropSpenser.h new file mode 100644 index 000000000..b7f20825d --- /dev/null +++ b/src/Blocks/BlockDropSpenser.h @@ -0,0 +1,41 @@ + +// BlockDropSpenser.h + +// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks + +#pragma once + +#include "../Piston.h" + + + + + +class cBlockDropSpenserHandler : + public cBlockEntityHandler +{ +public: + cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : + cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // FIXME: Do not use cPiston class for dispenser placement! + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h new file mode 100644 index 000000000..0ce813f1c --- /dev/null +++ b/src/Blocks/BlockEnderchest.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockEnderchestHandler : + public cBlockHandler +{ +public: + cBlockEnderchestHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + //todo: Drop Ender Chest if using silk touch pickaxe + a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockEntity.h b/src/Blocks/BlockEntity.h new file mode 100644 index 000000000..9c6b23665 --- /dev/null +++ b/src/Blocks/BlockEntity.h @@ -0,0 +1,31 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockEntityHandler : public cBlockHandler +{ +public: + cBlockEntityHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void OnUse(cWorld * a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + + virtual bool IsUseable() override + { + return true; + } +}; + + + + diff --git a/src/Blocks/BlockFarmland.h b/src/Blocks/BlockFarmland.h new file mode 100644 index 000000000..7bc71f7f3 --- /dev/null +++ b/src/Blocks/BlockFarmland.h @@ -0,0 +1,107 @@ + +// BlockFarmland.h + +// Declares the cBlcokFarmlandHandler representing the block handler for farmland + + + + + +#pragma once + +#include "BlockHandler.h" +#include "../BlockArea.h" + + + + + +class cBlockFarmlandHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockFarmlandHandler(void) : + super(E_BLOCK_FARMLAND) + { + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + bool Found = false; + + int Biome = a_World->GetBiomeAt(a_BlockX, a_BlockZ); + if (a_World->IsWeatherWet() && (Biome != biDesert) && (Biome != biDesertHills)) + { + // Rain hydrates farmland, too, except in Desert biomes. + Found = true; + } + else + { + // 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)) + { + // 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); + + if (Found) + { + // Water was found, hydrate the block until hydration reaches 7: + if (BlockMeta < 7) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta); + } + return; + } + + // Water wasn't found, de-hydrate block: + if (BlockMeta > 0) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta); + return; + } + + // Farmland too dry. If nothing is growing on top, turn back to dirt: + switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) + { + case E_BLOCK_CROPS: + case E_BLOCK_MELON_STEM: + case E_BLOCK_PUMPKIN_STEM: + { + // Produce on top, don't revert + break; + } + default: + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); + break; + } + } + } +} ; + + + + diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h new file mode 100644 index 000000000..6423a7cb0 --- /dev/null +++ b/src/Blocks/BlockFenceGate.h @@ -0,0 +1,88 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFenceGateHandler : + public cBlockHandler +{ +public: + cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation()); + OldMetaData ^= 4; // Toggle the gate + if ((OldMetaData & 1) == (NewMetaData & 1)) + { + // Standing in front of the gate - apply new direction + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3)); + } + else + { + // Standing aside - use last direction + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); + } + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + /// Converts the player's yaw to placed gate's blockmeta + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 360 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } +} ; + + + + diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h new file mode 100644 index 000000000..46b56d7e0 --- /dev/null +++ b/src/Blocks/BlockFire.h @@ -0,0 +1,228 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFireHandler : + public cBlockHandler +{ +public: + cBlockFireHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + /// Portal boundary and direction variables + int XZP, XZM, Dir; // For wont of a better name... + + virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + /* + PORTAL FINDING ALGORITH + ======================= + -Get clicked base block + -Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered. + Uses this value as a reference (the 'ceiling') + -For both directions (if one fails, try the other), BASE (clicked) block: + -Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1) + -If a border was encountered, go the other direction and repeat above + -Write borders to XZP and XZM, write direction portal faces to Dir + -Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir + */ + + a_BlockY--; // Because we want the block below the fire + FindAndSetPortalFrame(a_BlockX, a_BlockY, a_BlockZ, a_World); // Brought to you by Aperture Science + } + + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups from this block + } + + virtual bool IsClickedThrough(void) override + { + return true; + } + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + /// Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border + /// Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding + int FindObsidianCeiling(int X, int Y, int Z, cWorld * a_World, int MaxY = 0) + { + if (a_World->GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN) + { + return 0; + } + + int newY = Y + 1; + + for (newY; newY < cChunkDef::Height; newY++) + { + BLOCKTYPE Block = a_World->GetBlock(X, newY, Z); + if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE)) + { + continue; + } + else if (Block == E_BLOCK_OBSIDIAN) + { + // We found an obsidian ceiling + // Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block + // This is because the frame is a solid obsidian pillar + if ((MaxY != 0) && (newY == Y + 1)) + { + return EvaluatePortalBorder(X, newY, Z, MaxY, a_World); + } + else + { + // Return ceiling Y, whoever called this function will decide if it's part of a portal or not + return newY; + } + } + else { return 0; } + } + + return 0; + } + + /// Evaluates if coords have a valid border on top, based on MaxY + int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cWorld * a_World) + { + for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners + { + if (a_World->GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN) + { + // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal + return 0; + } + } + // Everything was obsidian, found a border! + return -1; // Return -1 for a frame border + } + + /// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE) + void FindAndSetPortalFrame(int X, int Y, int Z, cWorld * a_World) + { + int MaxY = FindObsidianCeiling(X, Y, Z, a_World); // Get topmost obsidian block as reference for all other checks + int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above + + if (MaxY == 0) // Oh noes! Not a portal coordinate :( + { + return; + } + + if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_World)) + { + if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_World)) + { + return; // No eligible portal construct, abort abort abort!! + } + } + + for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks + { + for (int Width = XZM; Width <= XZP; Width++) + { + if (Dir == 1) + { + a_World->SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir); + } + else + { + a_World->SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, Dir); + } + } + } + + return; + } + + /// Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable + /// Takes coordinates of base block and Y coord of target obsidian ceiling + bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cWorld * a_World) + { + Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction) + bool FoundFrameXP = false, FoundFrameXM = false; + for (X1; ((a_World->GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners + { + int Value = FindObsidianCeiling(X1, Y, Z, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_World, MaxY); // For corners without obsidian + if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find + { + FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further) + break; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice' + { + return false; // Not valid slice, no portal can be formed + } + } XZP = X1 - 1; // Set boundary of frame interior, note that for some reason, the loop of X and the loop of Z go to different numbers, hence -1 here and -2 there + for (X2; ((a_World->GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM) + { + int Value = FindObsidianCeiling(X2, Y, Z, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameXM = true; + break; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZM = X2 + 1; // Set boundary, see previous + return (FoundFrameXP && FoundFrameXM); + } + + /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable + bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cWorld * a_World) + { + Dir = 2; + bool FoundFrameZP = false, FoundFrameZM = false; + for (Z1; ((a_World->GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++) + { + int Value = FindObsidianCeiling(X, Y, Z1, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameZP = true; + continue; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZP = Z1 - 2; + for (Z2; ((a_World->GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--) + { + int Value = FindObsidianCeiling(X, Y, Z2, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameZM = true; + continue; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZM = Z2 + 2; + return (FoundFrameZP && FoundFrameZM); + } +}; + + + + diff --git a/src/Blocks/BlockFlower.h b/src/Blocks/BlockFlower.h new file mode 100644 index 000000000..421e2d5d8 --- /dev/null +++ b/src/Blocks/BlockFlower.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFlowerHandler : + public cBlockHandler +{ +public: + cBlockFlowerHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockFlowerPot.h b/src/Blocks/BlockFlowerPot.h new file mode 100644 index 000000000..b0faf5218 --- /dev/null +++ b/src/Blocks/BlockFlowerPot.h @@ -0,0 +1,105 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFlowerPotHandler : + public cBlockHandler +{ +public: + cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0)); + if (a_BlockMeta == 0) + { + return; + } + cItem Plant; + switch (a_BlockMeta) + { + case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break; + case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break; + case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break; + case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break; + case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break; + case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break; + case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break; + case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break; + case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break; + case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break; + case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break; + default: return; + } + a_Pickups.push_back(Plant); + } + + + void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); + if (Meta != 0) + { + // Already filled + return; + } + + switch (a_Player->GetEquippedItem().m_ItemType) + { + case E_BLOCK_RED_ROSE: Meta = 1; break; + case E_BLOCK_YELLOW_FLOWER: Meta = 2; break; + case E_BLOCK_SAPLING: + { + switch (a_Player->GetEquippedItem().m_ItemDamage) + { + case E_META_SAPLING_APPLE: Meta = 3; break; + case E_META_SAPLING_CONIFER: Meta = 4; break; + case E_META_SAPLING_BIRCH: Meta = 5; break; + case E_META_SAPLING_JUNGLE: Meta = 6; break; + } + break; + } + case E_BLOCK_RED_MUSHROOM: Meta = 7; break; + case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break; + case E_BLOCK_CACTUS: Meta = 9; break; + case E_BLOCK_DEAD_BUSH: Meta = 10; break; + case E_BLOCK_TALL_GRASS: + { + if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN) + { + Meta = 11; + } + else + { + return; + } + break; + } + } + + if (a_Player->GetGameMode() != gmCreative) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + } + + + virtual bool IsUseable(void) override + { + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockFluid.h b/src/Blocks/BlockFluid.h new file mode 100644 index 000000000..0db2f60c4 --- /dev/null +++ b/src/Blocks/BlockFluid.h @@ -0,0 +1,56 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFluidHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockFluidHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + switch (m_BlockType) + { + case E_BLOCK_STATIONARY_LAVA: + { + a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + break; + } + case E_BLOCK_STATIONARY_WATER: + { + a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + break; + } + } + super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk); + } +} ; + + + + diff --git a/src/Blocks/BlockFurnace.h b/src/Blocks/BlockFurnace.h new file mode 100644 index 000000000..fe35893d5 --- /dev/null +++ b/src/Blocks/BlockFurnace.h @@ -0,0 +1,47 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" +#include "../Piston.h" +#include "../Entities/Player.h" + + + + + +class cBlockFurnaceHandler : + public cBlockEntityHandler +{ +public: + cBlockFurnaceHandler(BLOCKTYPE a_BlockType) : + cBlockEntityHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // FIXME: Do not use cPiston class for furnace placement! + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); + + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockGlass.h b/src/Blocks/BlockGlass.h new file mode 100644 index 000000000..f6958bbb6 --- /dev/null +++ b/src/Blocks/BlockGlass.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGlassHandler : + public cBlockHandler +{ +public: + cBlockGlassHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + } +} ; + + + + diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h new file mode 100644 index 000000000..5f0d95dee --- /dev/null +++ b/src/Blocks/BlockGlowstone.h @@ -0,0 +1,30 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGlowstoneHandler : + public cBlockHandler +{ +public: + cBlockGlowstoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + // TODO: More drops? + a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockGravel.h b/src/Blocks/BlockGravel.h new file mode 100644 index 000000000..e1c9ff390 --- /dev/null +++ b/src/Blocks/BlockGravel.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGravelHandler : + public cBlockHandler +{ +public: + cBlockGravelHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp new file mode 100644 index 000000000..cd07b3021 --- /dev/null +++ b/src/Blocks/BlockHandler.cpp @@ -0,0 +1,465 @@ + +#include "Globals.h" +#include "BlockHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Root.h" +#include "../PluginManager.h" +#include "BlockBed.h" +#include "BlockBrewingStand.h" +#include "BlockButton.h" +#include "BlockCactus.h" +#include "BlockCarpet.h" +#include "BlockCauldron.h" +#include "BlockChest.h" +#include "BlockCloth.h" +#include "BlockCobWeb.h" +#include "BlockComparator.h" +#include "BlockCrops.h" +#include "BlockDeadBush.h" +#include "BlockDirt.h" +#include "BlockDoor.h" +#include "BlockDropSpenser.h" +#include "BlockEnderchest.h" +#include "BlockEntity.h" +#include "BlockFarmland.h" +#include "BlockFenceGate.h" +#include "BlockFire.h" +#include "BlockFlower.h" +#include "BlockFlowerPot.h" +#include "BlockFluid.h" +#include "BlockFurnace.h" +#include "BlockGlass.h" +#include "BlockGlowstone.h" +#include "BlockGravel.h" +#include "BlockHopper.h" +#include "BlockIce.h" +#include "BlockLadder.h" +#include "BlockLeaves.h" +#include "BlockLever.h" +#include "BlockMelon.h" +#include "BlockMushroom.h" +#include "BlockMycelium.h" +#include "BlockNote.h" +#include "BlockOre.h" +#include "BlockPiston.h" +#include "BlockPlanks.h" +#include "BlockPortal.h" +#include "BlockPumpkin.h" +#include "BlockRail.h" +#include "BlockRedstone.h" +#include "BlockRedstoneRepeater.h" +#include "BlockRedstoneTorch.h" +#include "BlockSand.h" +#include "BlockSapling.h" +#include "BlockSign.h" +#include "BlockSlab.h" +#include "BlockSnow.h" +#include "BlockStairs.h" +#include "BlockStems.h" +#include "BlockStone.h" +#include "BlockSugarcane.h" +#include "BlockTallGrass.h" +#include "BlockTorch.h" +#include "BlockVine.h" +#include "BlockWood.h" +#include "BlockWorkbench.h" + + + + + +bool cBlockHandler::m_HandlerInitialized = false; +cBlockHandler * cBlockHandler::m_BlockHandler[256]; + + + + + +cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType) +{ + if (!m_HandlerInitialized) + { + // We have to initialize + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); + m_HandlerInitialized = true; + } + if (m_BlockHandler[a_BlockType] != NULL) + { + return m_BlockHandler[a_BlockType]; + } + + return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType); +} + + + + + +cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) +{ + switch(a_BlockType) + { + // Block handlers, alphabetically sorted: + case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType); + case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType); + case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); + case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType); + case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_CARPET: return new cBlockCarpetHandler (a_BlockType); + case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); + case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); + case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); + case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); + case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType); + case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType); + case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType); + case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType); + case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockDoubleSlabHandler (a_BlockType); + case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockDoubleSlabHandler (a_BlockType); + case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType); + case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); + case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( ); + case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); + case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); + case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); + case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType); + case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType); + case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType); + case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType); + case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); + case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); + case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); + case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); + case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType); + case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType); + case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType); + case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType); + case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType); + case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType); + case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType); + case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType); + case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType); + case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType); + case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); + case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); + case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( ); + case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType); + case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType); + case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType); + case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType); + case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); + case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); + case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType); + case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType); + case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType); + case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType); + case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); + case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType); + case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType); + case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType); + case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType); + case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType); + case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType); + case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STONE_BUTTON: return new cBlockButtonHandler (a_BlockType); + case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType); + case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); + case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); + case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); + case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType); + case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); + case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType); + case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType); + case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType); + + default: return new cBlockHandler(a_BlockType); + } +} + + + + + +void cBlockHandler::Deinit() +{ + for (int i = 0; i < 256; i++) + { + delete m_BlockHandler[i]; + } + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case + m_HandlerInitialized = false; +} + + + + + +cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType) +{ + m_BlockType = a_BlockType; +} + + + + + +bool cBlockHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + // By default, all blocks can be placed and the meta is copied over from the item's damage value: + a_BlockType = m_BlockType; + a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); + return true; +} + + + + + +void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ +} + + + + + +void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // Notify the neighbors + NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); +} + + + + + +void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Notify the neighbors + NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); +} + + + + + +void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)) + { + GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ +} + + + + + +void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) +{ + // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. + a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); +} + + + + + +void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cItems Pickups; + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + ConvertToPickups(Pickups, Meta); + + // Allow plugins to modify the pickups: + cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups); + + if (!Pickups.empty()) + { + MTRand r1; + + // Mid-block position first + double MicroX, MicroY, MicroZ; + MicroX = a_BlockX + 0.5; + MicroY = a_BlockY + 0.5; + MicroZ = a_BlockZ + 0.5; + + // Add random offset second (this causes pickups to spawn inside blocks most times, it's a little buggy) + //MicroX += (int)(r1.randInt(16) + r1.randInt(16) - 16); + //MicroY += (int)(r1.randInt(16) + r1.randInt(16) - 16); + //MicroZ += (int)(r1.randInt(16) + r1.randInt(16) - 16); + + a_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ); + } +} + + + + + +const char * cBlockHandler::GetStepSound() +{ + return "step.stone"; +} + + + + + +bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk) +{ + return true; +} + + + + + +bool cBlockHandler::IsUseable() +{ + return false; +} + + + + + +bool cBlockHandler::IsClickedThrough(void) +{ + return false; +} + + + + + +bool cBlockHandler::DoesIgnoreBuildCollision(void) +{ + return (m_BlockType == E_BLOCK_AIR); +} + + + + + +bool cBlockHandler::DoesDropOnUnsuitable(void) +{ + return true; +} + + + + + +void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) +{ + if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk)) + { + if (DoesDropOnUnsuitable()) + { + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); + } + + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + } + else + { + // Wake up the simulators for this block: + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); + } +} + + + + diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h new file mode 100644 index 000000000..81d9f240c --- /dev/null +++ b/src/Blocks/BlockHandler.h @@ -0,0 +1,152 @@ + +#pragma once + +#include "../Defines.h" +#include "../Item.h" +#include "../Chunk.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cBlockHandler +{ +public: + cBlockHandler(BLOCKTYPE a_BlockType); + + /// Called when the block gets ticked either by a random tick or by a queued tick + virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Called before a block is placed into a world. + The handler should return true to allow placement, false to refuse. + Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. + Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block + */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /// Called by cWorld::SetBlock() after the block has been set + virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ); + + /// Called before the player has destroyed a block + virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called before a block gets destroyed / replaced with air + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) + virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Notifies all neighbors of the given block about a change + static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called while the player diggs the block. + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called if the user right clicks the block and the block is useable + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + + /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); + + /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL + virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Returns step sound name of block + virtual const char * GetStepSound(void); + + /// Checks if the block can stay at the specified relative coords in the chunk + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk); + + /** Checks if the block can be placed at this point. + Default: CanBeAt(...) + NOTE: This call doesn't actually place the block + */ + // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); + + /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called + virtual bool IsUseable(void); + + /** Indicates whether the client will click through this block. + For example digging a fire will hit the block below the fire so fire is clicked through + */ + virtual bool IsClickedThrough(void); + + /** Checks if the player can build "inside" this block. + For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision + */ + virtual bool DoesIgnoreBuildCollision(void); + + /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true + virtual bool DoesDropOnUnsuitable(void); + + /** Called when one of the neighbors gets set; equivalent to MC block update. + By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), + and wakes up all simulators on the block. + */ + virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); + + /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the XY plane. Default: no change + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; } + + + /// Get the blockhandler for a specific block id + static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType); + + /// Deletes all initialised block handlers + static void Deinit(); + +protected: + BLOCKTYPE m_BlockType; + + // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead. + static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType); + static cBlockHandler *m_BlockHandler[256]; + static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized +}; + + + + + +// Shortcut to get the blockhandler for a specific block +inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType) +{ + return cBlockHandler::GetBlockHandler(a_BlockType); +} + + + + diff --git a/src/Blocks/BlockHopper.h b/src/Blocks/BlockHopper.h new file mode 100644 index 000000000..3998276d7 --- /dev/null +++ b/src/Blocks/BlockHopper.h @@ -0,0 +1,46 @@ + +// BlockHopper.h + +// Declares the cBlockHopperHandler class representing the handler for the Hopper block + + + + + +class cBlockHopperHandler : + public cBlockEntityHandler +{ +public: + cBlockHopperHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // Convert the blockface into meta: + switch (a_BlockFace) + { + case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break; + case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break; + case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break; + case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break; + case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break; + case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break; + default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break; + } + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h new file mode 100644 index 000000000..af4961114 --- /dev/null +++ b/src/Blocks/BlockIce.h @@ -0,0 +1,37 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockIceHandler : + public cBlockHandler +{ +public: + cBlockIceHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups + } + + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + // TODO: Ice destroyed with air below it should turn into air instead of water + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0); + // This is called later than the real destroying of this ice block + } +} ; + + + + diff --git a/src/Blocks/BlockLadder.h b/src/Blocks/BlockLadder.h new file mode 100644 index 000000000..c0aa25f60 --- /dev/null +++ b/src/Blocks/BlockLadder.h @@ -0,0 +1,115 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockLadderHandler : + public cBlockHandler +{ +public: + cBlockLadderHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + return false; + } + } + + a_BlockType = m_BlockType; + a_BlockMeta = DirectionToMetaData(a_BlockFace); + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export + { // tolua_export + switch (a_Direction) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: return 0x2; + } + } // tolua_export + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export + { // tolua_export + switch (a_MetaData) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: return 0x2; + } + } // tolua_export + + + /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure + static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + for (int Face = 2; Face <= 5; Face++) + { + if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face)) + { + return Face; + } + } + return BLOCK_FACE_BOTTOM; + } + + + static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP)) + { + return false; + } + + AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); + + return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)]; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison + char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h new file mode 100644 index 000000000..6e015b8fa --- /dev/null +++ b/src/Blocks/BlockLeaves.h @@ -0,0 +1,184 @@ +#pragma once +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" +#include "../BlockArea.h" + + + + + +// Leaves can be this many blocks that away (inclusive) from the log not to decay +#define LEAVES_CHECK_DISTANCE 6 + +#define PROCESS_NEIGHBOR(x,y,z) \ + switch (a_Area.GetBlockType(x, y, z)) \ + { \ + case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \ + case E_BLOCK_LOG: return true; \ + } + +bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); + + + + + +class cBlockLeavesHandler : + public cBlockHandler +{ +public: + cBlockLeavesHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + MTRand rand; + + // Only the first 2 bits contain the display information, the others are for growing + if (rand.randInt(5) == 0) + { + a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); + } + if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE) + { + if (rand.rand(100) == 0) + { + a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); + } + } + } + + + void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ); + + //0.5% chance of dropping an apple + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + //check if Oak (0x1 and 0x2 bit not set) + MTRand rand; + if(!(Meta & 3) && rand.randInt(200) == 100) + { + cItems Drops; + Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); + a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); + } + } + + + virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if ((Meta & 0x04) != 0) + { + // Player-placed leaves, don't decay + return; + } + + if ((Meta & 0x8) != 0) + { + // These leaves have been checked for decay lately and nothing around them changed + return; + } + + // Get the data around the leaves: + cBlockArea Area; + if (!Area.Read( + a_World, + a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE, + a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE, + a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE, + cBlockArea::baTypes) + ) + { + // Cannot check leaves, a chunk is missing too close + return; + } + + if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ)) + { + // Wood found, the leaves stay; mark them as checked: + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8); + return; + } + // Decay the leaves: + DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ); + + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + + +bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Filter the blocks into a {leaves, log, other (air)} set: + BLOCKTYPE * Types = a_Area.GetBlockTypes(); + for (int i = a_Area.GetBlockCount() - 1; i > 0; i--) + { + switch (Types[i]) + { + case E_BLOCK_LEAVES: + case E_BLOCK_LOG: + { + break; + } + default: + { + Types[i] = E_BLOCK_AIR; + break; + } + } + } // for i - Types[] + + // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: + // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations + a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE); + for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) + { + for (int y = a_BlockY - i; y <= a_BlockY + i; y++) + { + for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++) + { + for (int x = a_BlockX - i; x <= a_BlockX + i; x++) + { + if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) + { + continue; + } + PROCESS_NEIGHBOR(x - 1, y, z); + PROCESS_NEIGHBOR(x + 1, y, z); + PROCESS_NEIGHBOR(x, y, z - 1); + PROCESS_NEIGHBOR(x, y, z + 1); + PROCESS_NEIGHBOR(x, y + 1, z); + PROCESS_NEIGHBOR(x, y - 1, z); + } // for x + } // for z + } // for y + } // for i - BFS iterations + return false; +} + + + + diff --git a/src/Blocks/BlockLever.cpp b/src/Blocks/BlockLever.cpp new file mode 100644 index 000000000..2739fa3a9 --- /dev/null +++ b/src/Blocks/BlockLever.cpp @@ -0,0 +1,31 @@ + +#include "Globals.h" +#include "BlockLever.h" +#include "../Entities/Player.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + // Flip the ON bit on/off using the XOR bitwise operation + NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta); + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); +} + + + + diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h new file mode 100644 index 000000000..fe7ecdf7e --- /dev/null +++ b/src/Blocks/BlockLever.h @@ -0,0 +1,68 @@ +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockLeverHandler : + public cBlockHandler +{ +public: + cBlockLeverHandler(BLOCKTYPE a_BlockType); + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = LeverDirectionToMetaData(a_BlockFace); + return true; + } + + + inline static NIBBLETYPE LeverDirectionToMetaData(char a_Dir) + { + // Determine lever direction: + switch (a_Dir) + { + case BLOCK_FACE_TOP: return 0x6; + case BLOCK_FACE_EAST: return 0x1; + case BLOCK_FACE_WEST: return 0x2; + case BLOCK_FACE_SOUTH: return 0x3; + case BLOCK_FACE_NORTH: return 0x4; + case BLOCK_FACE_BOTTOM: return 0x0; + default: return 0x6; + } + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockMelon.h b/src/Blocks/BlockMelon.h new file mode 100644 index 000000000..2f7d9a461 --- /dev/null +++ b/src/Blocks/BlockMelon.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMelonHandler : + public cBlockHandler +{ +public: + cBlockMelonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + MTRand r1; + a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockMushroom.h b/src/Blocks/BlockMushroom.h new file mode 100644 index 000000000..2846a6317 --- /dev/null +++ b/src/Blocks/BlockMushroom.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMushroomHandler : + public cBlockHandler +{ +public: + cBlockMushroomHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + + // TODO: Cannot be at too much daylight + + switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) + { + case E_BLOCK_GLASS: + case E_BLOCK_CACTUS: + case E_BLOCK_ICE: + case E_BLOCK_LEAVES: + case E_BLOCK_AIR: + { + return false; + } + } + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockMycelium.h b/src/Blocks/BlockMycelium.h new file mode 100644 index 000000000..7f897c72a --- /dev/null +++ b/src/Blocks/BlockMycelium.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMyceliumHandler : + public cBlockHandler +{ +public: + cBlockMyceliumHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); + } + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/src/Blocks/BlockNote.h b/src/Blocks/BlockNote.h new file mode 100644 index 000000000..fef38d845 --- /dev/null +++ b/src/Blocks/BlockNote.h @@ -0,0 +1,13 @@ +#pragma once +#include "BlockHandler.h" +#include "BlockEntity.h" + +class cBlockNoteHandler : public cBlockEntityHandler +{ +public: + cBlockNoteHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + +}; diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h new file mode 100644 index 000000000..9684dbb19 --- /dev/null +++ b/src/Blocks/BlockOre.h @@ -0,0 +1,80 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockOreHandler : + public cBlockHandler +{ +public: + cBlockOreHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + short ItemType = m_BlockType; + char Count = 1; + short Meta = 0; + + MTRand r1; + switch (m_BlockType) + { + case E_BLOCK_LAPIS_ORE: + { + ItemType = E_ITEM_DYE; + Count = 4 + (char)r1.randInt(4); + Meta = 4; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + Count = 4 + (char)r1.randInt(1); + break; + } + default: + { + Count = 1; + break; + } + } + + switch (m_BlockType) + { + case E_BLOCK_DIAMOND_ORE: + { + ItemType = E_ITEM_DIAMOND; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + ItemType = E_ITEM_REDSTONE_DUST; + break; + } + case E_BLOCK_EMERALD_ORE: + { + ItemType = E_ITEM_EMERALD; + break; + } + case E_BLOCK_COAL_ORE: + { + ItemType = E_ITEM_COAL; + break; + } + } + a_Pickups.push_back(cItem(ItemType, Count, Meta)); + } +} ; + + + + diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp new file mode 100644 index 000000000..d5750ebdd --- /dev/null +++ b/src/Blocks/BlockPiston.cpp @@ -0,0 +1,102 @@ + +#include "Globals.h" +#include "BlockPiston.h" +#include "../Item.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Piston.h" + + + + + +#define AddPistonDir(x, y, z, dir, amount) \ + switch (dir) \ + { \ + case 0: (y) -= (amount); break; \ + case 1: (y) += (amount); break; \ + case 2: (z) -= (amount); break; \ + case 3: (z) += (amount); break; \ + case 4: (x) -= (amount); break; \ + case 5: (x) += (amount); break; \ + } + + + + +cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + int newX = a_BlockX; + int newY = a_BlockY; + int newZ = a_BlockZ; + AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1); + + if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION) + { + a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0); + } +} + + + + + +bool cBlockPistonHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBlockPistonHeadHandler: + +cBlockPistonHeadHandler::cBlockPistonHeadHandler(void) : + super(E_BLOCK_PISTON_EXTENSION) +{ +} + + + + + +void cBlockPistonHeadHandler::OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + int newX = a_BlockX; + int newY = a_BlockY; + int newZ = a_BlockZ; + AddPistonDir(newX, newY, newZ, OldMeta & ~(8), -1); + + BLOCKTYPE Block = a_World->GetBlock(newX, newY, newZ); + if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON)) + { + a_World->DigBlock(newX, newY, newZ); + } +} + + + + + diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h new file mode 100644 index 000000000..109f5ea8b --- /dev/null +++ b/src/Blocks/BlockPiston.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPistonHandler : + public cBlockHandler +{ +public: + cBlockPistonHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; +} ; + + + + + +class cBlockPistonHeadHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockPistonHeadHandler(void); + + virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; +} ; + + + + diff --git a/src/Blocks/BlockPlanks.h b/src/Blocks/BlockPlanks.h new file mode 100644 index 000000000..f3b8dbfb6 --- /dev/null +++ b/src/Blocks/BlockPlanks.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPlanksHandler : public cBlockHandler +{ +public: + cBlockPlanksHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); + a_BlockMeta = Meta; + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockPortal.h b/src/Blocks/BlockPortal.h new file mode 100644 index 000000000..c56f0cbc8 --- /dev/null +++ b/src/Blocks/BlockPortal.h @@ -0,0 +1,108 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPortalHandler : + public cBlockHandler +{ +public: + cBlockPortalHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // We set to zero so MCS doesn't stop you from building weird portals like vanilla does + // CanBeAt doesn't do anything if meta is zero + // We set to zero because the client sends meta = 2 to the server (it calculates rotation itself) + + a_BlockType = m_BlockType; + a_BlockMeta = 0; + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + return; // No pickups + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if ((a_RelY - 1 < 0) || (a_RelY + 1 > cChunkDef::Height)) + { + return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1 + } + + switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)) + { + case 0x1: + { + static const struct + { + int x, y, z; + } PortalCheck[] = + { + { 0, 1, 0}, + { 0,-1, 0}, + { 1, 0, 0}, + {-1, 0, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) + { + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); + + if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) + { + return false; + } + } + break; + } + case 0x2: + { + static const struct + { + int x, y, z; + } PortalCheck[] = + { + { 0, 1, 0}, + { 0,-1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + + for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) + { + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); + + if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) + { + return false; + } + } + break; + } + } + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h new file mode 100644 index 000000000..76abc6818 --- /dev/null +++ b/src/Blocks/BlockPumpkin.h @@ -0,0 +1,60 @@ +#pragma once + +#include "BlockHandler.h" + + + + +class cBlockPumpkinHandler : + public cBlockHandler +{ +public: + cBlockPumpkinHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation()); + return true; + } + + inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) + { + ASSERT((a_Yaw >= -180) && (a_Yaw < 180)); + + a_Yaw += 180 + 45; + if (a_Yaw > 360) + { + a_Yaw -= 360; + } + if ((a_Yaw >= 0) && (a_Yaw < 90)) + { + return 0x0; + } + else if ((a_Yaw >= 180) && (a_Yaw < 270)) + { + return 0x2; + } + else if ((a_Yaw >= 90) && (a_Yaw < 180)) + { + return 0x1; + } + else + { + return 0x3; + } + } + +} ; + + + + diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h new file mode 100644 index 000000000..24a101652 --- /dev/null +++ b/src/Blocks/BlockRail.h @@ -0,0 +1,398 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" + + + + + +enum ENUM_PURE +{ + E_PURE_UPDOWN = 0, + E_PURE_DOWN = 1, + E_PURE_NONE = 2 +}; + + + + + +class cBlockRailHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockRailHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ); + return true; + } + + + virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)); + } + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + super::ConvertToPickups(a_Pickups, 0); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]) + { + return false; + } + + NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + switch (Meta) + { + case E_META_RAIL_ASCEND_XP: + case E_META_RAIL_ASCEND_XM: + case E_META_RAIL_ASCEND_ZM: + case E_META_RAIL_ASCEND_ZP: + { + // Mapping between the meta and the neighbors that need checking + Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero + static const struct + { + int x, z; + } Coords[] = + { + { 1, 0}, // east, XP + {-1, 0}, // west, XM + { 0, -1}, // north, ZM + { 0, 1}, // south, ZP + } ; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta)) + { + // Too close to the edge, cannot simulate + return true; + } + return g_BlockIsSolid[BlockType]; + } + } + return true; + } + + NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + NIBBLETYPE Meta = 0; + char RailsCnt = 0; + bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP + memset(Neighbors, false, sizeof(Neighbors)); + Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN)); + Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)); + Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN)); + Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)); + Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE)); + Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE)); + Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE)); + Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE)); + if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST)) + Neighbors[0] = true; + if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST)) + Neighbors[1] = true; + if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH)) + Neighbors[2] = true; + if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH)) + Neighbors[3] = true; + for (int i = 0; i < 8; i++) + { + if (Neighbors[i]) + { + RailsCnt++; + } + } + if (RailsCnt == 1) + { + if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP; + else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM; + else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM; + else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP; + else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP; + else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP; + ASSERT(!"Weird neighbor count"); + return Meta; + } + for (int i = 0; i < 4; i++) + { + if (Neighbors[i + 4]) + { + Neighbors[i] = true; + } + } + if (RailsCnt > 1) + { + if (Neighbors[3] && Neighbors[0]) return E_META_RAIL_CURVED_ZP_XP; + else if (Neighbors[3] && Neighbors[1]) return E_META_RAIL_CURVED_ZP_XM; + else if (Neighbors[2] && Neighbors[0]) return E_META_RAIL_CURVED_ZM_XP; + else if (Neighbors[2] && Neighbors[1]) return E_META_RAIL_CURVED_ZM_XM; + else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP; + else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM; + else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM; + else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP; + else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP; + else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP; + ASSERT(!"Weird neighbor count"); + } + return Meta; + } + + + bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) + { + return false; + } + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + switch (Meta) + { + case E_META_RAIL_ZM_ZP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN) + ) + { + return true; + } + break; + } + + case E_META_RAIL_XM_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_XM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) || + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_ZM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) + ) + { + return true; + } + break; + } + + case E_META_RAIL_ASCEND_ZP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZM_XM: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_META_RAIL_CURVED_ZM_XP: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) + ) + { + return true; + } + break; + } + } + return false; + } + + + bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); + NIBBLETYPE Meta; + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) + { + if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN)) + { + if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE)) + { + return true; + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ); + } + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ); + } + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + } + + switch (a_BlockFace) + { + case BLOCK_FACE_NORTH: + { + if ( + (Meta == E_META_RAIL_ZM_ZP) || + (Meta == E_META_RAIL_ASCEND_ZM) || + (Meta == E_META_RAIL_ASCEND_ZP) || + (Meta == E_META_RAIL_CURVED_ZP_XP) || + (Meta == E_META_RAIL_CURVED_ZP_XM) + ) + { + return false; + } + break; + } + + case BLOCK_FACE_SOUTH: + { + if ( + (Meta == E_META_RAIL_ZM_ZP) || + (Meta == E_META_RAIL_ASCEND_ZM) || + (Meta == E_META_RAIL_ASCEND_ZP) || + (Meta == E_META_RAIL_CURVED_ZM_XP) || + (Meta == E_META_RAIL_CURVED_ZM_XM) + ) + { + return false; + } + break; + } + + case BLOCK_FACE_EAST: + { + if ( + (Meta == E_META_RAIL_XM_XP) || + (Meta == E_META_RAIL_ASCEND_XP) || + (Meta == E_META_RAIL_ASCEND_XM) || + (Meta == E_META_RAIL_CURVED_ZP_XM) || + (Meta == E_META_RAIL_CURVED_ZM_XM) + ) + { + return false; + } + break; + } + case BLOCK_FACE_WEST: + { + if ( + (Meta == E_META_RAIL_XM_XP) || + (Meta == E_META_RAIL_ASCEND_XP) || + (Meta == E_META_RAIL_ASCEND_XM) || + (Meta == E_META_RAIL_CURVED_ZP_XP) || + (Meta == E_META_RAIL_CURVED_ZM_XP) + ) + { + return false; + } + break; + } + } + return true; + } +} ; + + + + diff --git a/src/Blocks/BlockRedstone.cpp b/src/Blocks/BlockRedstone.cpp new file mode 100644 index 000000000..35cdc34cf --- /dev/null +++ b/src/Blocks/BlockRedstone.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" +#include "BlockRedstone.h" +#include "../Item.h" +#include "../World.h" + + + + + +cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + diff --git a/src/Blocks/BlockRedstone.h b/src/Blocks/BlockRedstone.h new file mode 100644 index 000000000..f28f3f2d6 --- /dev/null +++ b/src/Blocks/BlockRedstone.h @@ -0,0 +1,35 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockRedstoneHandler : + public cBlockHandler +{ +public: + cBlockRedstoneHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1)); + } +} ; + + + + diff --git a/src/Blocks/BlockRedstoneRepeater.cpp b/src/Blocks/BlockRedstoneRepeater.cpp new file mode 100644 index 000000000..3ab5bc559 --- /dev/null +++ b/src/Blocks/BlockRedstoneRepeater.cpp @@ -0,0 +1,50 @@ + +#include "Globals.h" +#include "BlockRedstoneRepeater.h" +#include "../Entities/Player.h" + + + + + +cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + + +void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); +} + + + + +bool cBlockRedstoneRepeaterHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; +} + + + + diff --git a/src/Blocks/BlockRedstoneRepeater.h b/src/Blocks/BlockRedstoneRepeater.h new file mode 100644 index 000000000..a61121d3a --- /dev/null +++ b/src/Blocks/BlockRedstoneRepeater.h @@ -0,0 +1,82 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockRedstoneRepeaterHandler : + public cBlockHandler +{ +public: + cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType); + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + inline static NIBBLETYPE RepeaterRotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + + if ((a_Rotation >= 0) && (a_Rotation < 90)) + { + return 0x1; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x3; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x0; + } + } +} ; + + + + diff --git a/src/Blocks/BlockRedstoneTorch.h b/src/Blocks/BlockRedstoneTorch.h new file mode 100644 index 000000000..cb897ba3f --- /dev/null +++ b/src/Blocks/BlockRedstoneTorch.h @@ -0,0 +1,36 @@ + +#pragma once + +#include "BlockRedstone.h" +#include "BlockTorch.h" + + + + + +class cBlockRedstoneTorchHandler : + public cBlockTorchHandler +{ +public: + cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType) + : cBlockTorchHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Always drop the ON torch, meta 0 + a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockSand.h b/src/Blocks/BlockSand.h new file mode 100644 index 000000000..3fc271483 --- /dev/null +++ b/src/Blocks/BlockSand.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSandHandler : + public cBlockHandler +{ +public: + cBlockSandHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.sand"; + } + +}; + + + + diff --git a/src/Blocks/BlockSapling.h b/src/Blocks/BlockSapling.h new file mode 100644 index 000000000..fff2fa88b --- /dev/null +++ b/src/Blocks/BlockSapling.h @@ -0,0 +1,57 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockSaplingHandler : + public cBlockHandler +{ +public: + cBlockSaplingHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Only the first 2 bits contain the display information, the others are for growing + a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if ((Meta & 0x08) != 0) + { + a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08); + } + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockSign.h b/src/Blocks/BlockSign.h new file mode 100644 index 000000000..7fbe61893 --- /dev/null +++ b/src/Blocks/BlockSign.h @@ -0,0 +1,78 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cBlockSignHandler : + public cBlockHandler +{ +public: + cBlockSignHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + static char RotationToMetaData(double a_Rotation) + { + a_Rotation += 180 + (180 / 16); // So it's not aligned with axis + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + + a_Rotation = (a_Rotation / 360) * 16; + + return ((char)a_Rotation) % 16; + } + + + static char DirectionToMetaData(char a_Direction) + { + switch (a_Direction) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: + { + break; + } + } + return 0x2; + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override + { + a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); + } +} ; + + + + diff --git a/src/Blocks/BlockSlab.h b/src/Blocks/BlockSlab.h new file mode 100644 index 000000000..7c1251b28 --- /dev/null +++ b/src/Blocks/BlockSlab.h @@ -0,0 +1,182 @@ + +// BlockSlab.h + +// Declares cBlockSlabHandler and cBlockDoubleSlabHandler classes + + + + + +#pragma once + +#include "BlockHandler.h" +#include "../Items/ItemHandler.h" + + + + + +class cBlockSlabHandler : + public cBlockHandler +{ +public: + cBlockSlabHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + BLOCKTYPE Type = (BLOCKTYPE) (a_Player->GetEquippedItem().m_ItemType); + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07); + + // HandlePlaceBlock wants a cItemHandler pointer thing, so let's give it one + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(GetDoubleSlabType(Type)); + + // Check if the block at the coordinates is a slab. Eligibility for combining has already been processed in ClientHandle + if (IsAnySlabType(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))) + { + // Call the function in ClientHandle that places a block when the client sends the packet, + // so that plugins may interfere with the placement. + + if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) + { + // Top and bottom faces need no parameter modification + a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + else + { + // The other faces need to distinguish between top and bottom cursor positions + if (a_CursorY > 7) + { + // Edit the call to use BLOCK_FACE_BOTTOM, otherwise it places incorrectly + a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_TOP, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + else + { + // Edit the call to use BLOCK_FACE_TOP, otherwise it places incorrectly + a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_BOTTOM, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + } + return false; // Cancel the event, because dblslabs were already placed, nothing else needed + } + + // Place the single-slab with correct metas: + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: + { + // Bottom half slab block + a_BlockMeta = Meta & 0x7; + break; + } + case BLOCK_FACE_BOTTOM: + { + // Top half slab block + a_BlockMeta = Meta | 0x8; + break; + } + case BLOCK_FACE_EAST: + case BLOCK_FACE_NORTH: + case BLOCK_FACE_SOUTH: + case BLOCK_FACE_WEST: + { + if (a_CursorY > 7) + { + // Cursor at top half of block, place top slab + a_BlockMeta = Meta | 0x8; break; + } + else + { + // Cursor at bottom half of block, place bottom slab + a_BlockMeta = Meta & 0x7; break; + } + } + } // switch (a_BlockFace) + return true; + } + + + virtual const char * GetStepSound(void) override + { + switch (m_BlockType) + { + case E_BLOCK_WOODEN_SLAB: return "step.wood"; + case E_BLOCK_STONE_SLAB: return "step.stone"; + } + ASSERT(!"Unhandled slab type!"); + return ""; + } + + + /// Returns true if the specified blocktype is one of the slabs handled by this handler + static bool IsAnySlabType(BLOCKTYPE a_BlockType) + { + return ((a_BlockType == E_BLOCK_WOODEN_SLAB) || (a_BlockType == E_BLOCK_STONE_SLAB)); + } + + + /// Converts the single-slab blocktype to its equivalent double-slab blocktype + static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType) + { + switch (a_SingleSlabBlockType) + { + case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB; + case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB; + } + ASSERT(!"Unhandled slab type!"); + return E_BLOCK_AIR; + } + +} ; + + + + + +class cBlockDoubleSlabHandler : + public cBlockHandler +{ +public: + cBlockDoubleSlabHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + if (m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) + { + m_BlockType = E_BLOCK_STONE_SLAB; + } + else + { + m_BlockType = E_BLOCK_WOODEN_SLAB; + } + a_Pickups.push_back(cItem(m_BlockType, 2, a_BlockMeta)); + } + + + virtual const char * GetStepSound(void) override + { + return ((m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone"; + } +} ; + + + + diff --git a/src/Blocks/BlockSnow.h b/src/Blocks/BlockSnow.h new file mode 100644 index 000000000..b8d48362c --- /dev/null +++ b/src/Blocks/BlockSnow.h @@ -0,0 +1,72 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSnowHandler : + public cBlockHandler +{ +public: + cBlockSnowHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = a_World->GetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); + + if ((Meta < 7) && (Meta != 0)) // Is height at maximum (7) or at mininum (0)? Don't do anything if so + { + Meta++; + } + + a_BlockMeta = Meta; + return true; + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]; + } + + + virtual bool DoesDropOnUnsuitable(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/src/Blocks/BlockStairs.h b/src/Blocks/BlockStairs.h new file mode 100644 index 000000000..8d259eee3 --- /dev/null +++ b/src/Blocks/BlockStairs.h @@ -0,0 +1,152 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockStairsHandler : + public cBlockHandler +{ +public: + cBlockStairsHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: break; + case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block + case BLOCK_FACE_EAST: + case BLOCK_FACE_NORTH: + case BLOCK_FACE_SOUTH: + case BLOCK_FACE_WEST: + { + // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block + if (a_CursorY > 8) + { + a_BlockMeta |= 0x4; + } + break; + } + } + return true; + } + + // TODO: step sound + + + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + if ((a_Rotation >= 0) && (a_Rotation < 90)) + { + return 0x0; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x1; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x03; // East -> North + case 0x01: return TopBits | 0x02; // West -> South + case 0x02: return TopBits | 0x00; // South -> East + case 0x03: return TopBits | 0x01; // North -> West + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x02; // East -> South + case 0x01: return TopBits | 0x03; // West -> North + case 0x02: return TopBits | 0x01; // South -> West + case 0x03: return TopBits | 0x00; // North -> East + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x00; // East -> East + case 0x01: return TopBits | 0x01; // West -> West + case 0x02: return TopBits | 0x03; // South -> North + case 0x03: return TopBits | 0x02; // North -> South + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override + { + // Toggle bit 3: + return (a_Meta & 0x0b) | ((~a_Meta) & 0x04); + } + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x01; // East -> West + case 0x01: return TopBits | 0x00; // West -> East + case 0x02: return TopBits | 0x02; // South -> South + case 0x03: return TopBits | 0x03; // North -> North + } + // Not reachable, but to avoid a compiler warning: + return 0; + } +} ; + + + + diff --git a/src/Blocks/BlockStems.h b/src/Blocks/BlockStems.h new file mode 100644 index 000000000..ce02d9cb8 --- /dev/null +++ b/src/Blocks/BlockStems.h @@ -0,0 +1,58 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockStemsHandler : + public cBlockHandler +{ +public: + cBlockStemsHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS; + a_Pickups.push_back(cItem(ItemType, 1, 0)); + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta >= 7) + { + // Grow the produce: + a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType); + } + else + { + // Grow the stem: + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockStone.h b/src/Blocks/BlockStone.h new file mode 100644 index 000000000..af4c6509a --- /dev/null +++ b/src/Blocks/BlockStone.h @@ -0,0 +1,29 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockStoneHandler : + public cBlockHandler +{ +public: + cBlockStoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0)); + } +} ; + + + + diff --git a/src/Blocks/BlockSugarcane.h b/src/Blocks/BlockSugarcane.h new file mode 100644 index 000000000..28a60df80 --- /dev/null +++ b/src/Blocks/BlockSugarcane.h @@ -0,0 +1,90 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSugarcaneHandler : + public cBlockHandler +{ +public: + cBlockSugarcaneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + case E_BLOCK_SAND: + { + static const struct + { + int x, z; + } Coords[] = + { + {-1, 0}, + { 1, 0}, + { 0, -1}, + { 0, 1}, + } ; + a_RelY -= 1; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + { + // Too close to the edge, cannot simulate + return true; + } + if (IsBlockWater(BlockType)) + { + return true; + } + } // for i - Coords[] + // Not directly neighboring a water block + return false; + } + case E_BLOCK_SUGARCANE: + { + return true; + } + } + return false; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h new file mode 100644 index 000000000..cd27ab7e6 --- /dev/null +++ b/src/Blocks/BlockTallGrass.h @@ -0,0 +1,51 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockTallGrassHandler : + public cBlockHandler +{ +public: + cBlockTallGrassHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Drop seeds, sometimes + MTRand r1; + if (r1.randInt(10) == 5) + { + a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h new file mode 100644 index 000000000..72a313126 --- /dev/null +++ b/src/Blocks/BlockTorch.h @@ -0,0 +1,275 @@ +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockTorchHandler : + public cBlockHandler +{ +public: + cBlockTorchHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // Find proper placement of torch + + if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM)) + { + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); // Top or bottom faces clicked, find a suitable face + if (a_BlockFace == BLOCK_FACE_NONE) + { + // Client wouldn't have sent anything anyway, but whatever + return false; + } + } + else + { + // Not top or bottom faces, try to preserve whatever face was clicked + if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + // Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + if (a_BlockFace == BLOCK_FACE_NONE) + { + return false; + } + } + } + + a_BlockType = m_BlockType; + a_BlockMeta = DirectionToMetaData(a_BlockFace); + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export + { // tolua_export + switch (a_Direction) + { + case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0; + case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; + case BLOCK_FACE_EAST: return E_META_TORCH_EAST; + case BLOCK_FACE_WEST: return E_META_TORCH_WEST; + case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; + case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; + default: + { + ASSERT(!"Unhandled torch direction!"); + break; + } + }; + return 0x0; + } // tolua_export + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export + { // tolua_export + switch (a_MetaData) + { + case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground + case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; + case E_META_TORCH_EAST: return BLOCK_FACE_EAST; + case E_META_TORCH_WEST: return BLOCK_FACE_WEST; + case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; + case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; + default: + { + ASSERT(!"Unhandled torch metadata"); + break; + } + } + return 0; + } // tolua_export + + + static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) + { + switch (a_TorchMeta) + { + case 0x0: + case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); + case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); + case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); + case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); + case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); + default: + { + ASSERT(!"Unhandled torch meta!"); + break; + } + } + return false; + } + + + static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace) + { + if ( !g_BlockIsTorchPlaceable[a_BlockType] ) + { + return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright + } + else + { + return true; + } + } + + + static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); + return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace); + } + + + /// Finds a suitable face to place the torch, returning BLOCK_FACE_NONE on failure + static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + for (int i = 0; i <= 5; i++) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, true); + BLOCKTYPE BlockInQuestion = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + if ( + ((BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || + (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL)) && + (i == BLOCK_FACE_TOP) + ) + { + return i; + } + else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM)) + { + return i; + } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, i, false); + } + } + return BLOCK_FACE_NONE; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + + AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true); + BLOCKTYPE BlockInQuestion; + a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockInQuestion); + + if ( + (BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || + (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL) + ) + { + // Torches can be placed on tops of glass and fences, despite them being 'untorcheable' + // No need to check for upright orientation, it was done when the torch was placed + return true; + } + else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] ) + { + return false; + } + else + { + return true; + } + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Always drop meta = 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x04; // East -> North + case 0x02: return TopBits | 0x03; // West -> South + case 0x03: return TopBits | 0x01; // South -> East + case 0x04: return TopBits | 0x02; // North -> West + default: return a_Meta; // Floor -> Floor + } + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x03; // East -> South + case 0x02: return TopBits | 0x04; // West -> North + case 0x03: return TopBits | 0x02; // South -> West + case 0x04: return TopBits | 0x01; // North -> East + default: return a_Meta; // Floor -> Floor + } + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x03: return TopBits | 0x04; // South -> North + case 0x04: return TopBits | 0x03; // North -> South + default: return a_Meta; // Keep the rest + } + } + + + // Mirroring around the XZ plane doesn't make sense for floor torches, + // the others stay the same, so let's keep all the metas the same. + // The base class does tht for us, no need to override MetaMirrorXZ() + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x02; // East -> West + case 0x02: return TopBits | 0x01; // West -> East + default: return a_Meta; // Keep the rest + } + } +} ; + + + + diff --git a/src/Blocks/BlockVine.h b/src/Blocks/BlockVine.h new file mode 100644 index 000000000..2c9f67cab --- /dev/null +++ b/src/Blocks/BlockVine.h @@ -0,0 +1,201 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockVineHandler : + public cBlockHandler +{ +public: + cBlockVineHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // TODO: Disallow placement where the vine doesn't attach to something properly + BLOCKTYPE BlockType = 0; + NIBBLETYPE BlockMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + if (BlockType == m_BlockType) + { + a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace); + } + else + { + a_BlockMeta = DirectionToMetaData(a_BlockFace); + } + a_BlockType = m_BlockType; + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_NORTH: return 0x1; + case BLOCK_FACE_SOUTH: return 0x4; + case BLOCK_FACE_WEST: return 0x8; + case BLOCK_FACE_EAST: return 0x2; + default: return 0x0; + } + } + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) + { + switch(a_MetaData) + { + case 0x1: return BLOCK_FACE_NORTH; + case 0x4: return BLOCK_FACE_SOUTH; + case 0x8: return BLOCK_FACE_WEST; + case 0x2: return BLOCK_FACE_EAST; + default: return BLOCK_FACE_TOP; + } + } + + + /// Returns true if the specified block type is good for vines to attach to + static bool IsBlockAttachable(BLOCKTYPE a_BlockType) + { + return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType]; + } + + + /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings + NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) + { + static const struct + { + int x, z; + int Bit; + } Coords[] = + { + { 0, 1, 1}, // south, ZP + {-1, 0, 2}, // west, XM + { 0, -1, 4}, // north, ZM + { 1, 0, 8}, // east, XP + } ; + int res = 0; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ( + a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && + IsBlockAttachable(BlockType) + ) + { + res |= Coords[i].Bit; + } + } + return res; + } + + + void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ); + + // Check if vine above us, add its meta to MaxMeta + if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType)) + { + MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ); + } + + NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal + if (Common != CurMeta) + { + // There is a neighbor missing, need to update the meta or even destroy the block + bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ)); + if ((Common == 0) && !HasTop) + { + // The vine just lost all its support, destroy the block: + if (DoesDropOnUnsuitable()) + { + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); + } + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return; + } + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common); + } + else + { + // Wake up the simulators for this block: + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); + } + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } + + + virtual bool DoesDropOnUnsuitable(void) override + { + return false; + } + + virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z) + { + if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR) + { + a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z)); + } + } + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bits 2 and 4 stay, bits 1 and 3 swap + return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2)); + } + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bits 1 and 3 stay, bits 2 and 4 swap + return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); + } +} ; + + + + diff --git a/src/Blocks/BlockWood.h b/src/Blocks/BlockWood.h new file mode 100644 index 000000000..cb5ee995a --- /dev/null +++ b/src/Blocks/BlockWood.h @@ -0,0 +1,72 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockWoodHandler : public cBlockHandler +{ +public: + cBlockWoodHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage); + a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta); + return true; + } + + + inline static NIBBLETYPE BlockFaceToMetaData(char a_BlockFace, NIBBLETYPE a_WoodMeta) + { + switch (a_BlockFace) + { + case BLOCK_FACE_YM: + case BLOCK_FACE_YP: + { + return a_WoodMeta; // Top or bottom, just return original + } + + case BLOCK_FACE_ZP: + case BLOCK_FACE_ZM: + { + return a_WoodMeta | 0x8; // North or south + } + + case BLOCK_FACE_XP: + case BLOCK_FACE_XM: + { + return a_WoodMeta | 0x4; // East or west + } + + default: + { + ASSERT(!"Unhandled block face!"); + return a_WoodMeta | 0xC; // No idea, give a special meta (all sides bark) + } + } + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/Blocks/BlockWorkbench.h b/src/Blocks/BlockWorkbench.h new file mode 100644 index 000000000..a2cc6119c --- /dev/null +++ b/src/Blocks/BlockWorkbench.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "BlockHandler.h" +#include "../UI/Window.h" +#include "../Entities/Player.h" + + + + + +class cBlockWorkbenchHandler: + public cBlockHandler +{ +public: + cBlockWorkbenchHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ); + a_Player->OpenWindow(Window); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp new file mode 100644 index 000000000..02602992e --- /dev/null +++ b/src/BoundingBox.cpp @@ -0,0 +1,331 @@ + +// BoundingBox.cpp + +// Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords + +#include "Globals.h" +#include "BoundingBox.h" +#include "Defines.h" + + + + + +#if 0 + +/// A simple self-test that is executed on program start, used to verify bbox functionality +class SelfTest +{ +public: + SelfTest(void) + { + Vector3d Min(1, 1, 1); + Vector3d Max(2, 2, 2); + Vector3d LineDefs[] = + { + Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP) + Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM) + Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM) + Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect + Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM) + Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect + } ; + for (int i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++) + { + double LineCoeff; + char Face; + Vector3d Line1 = LineDefs[2 * i]; + Vector3d Line2 = LineDefs[2 * i + 1]; + bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face); + printf("LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", + Line1.x, Line1.y, Line1.z, + Line2.x, Line2.y, Line2.z, + res ? 1 : 0, LineCoeff, Face + ); + } // for i - LineDefs[] + printf("BoundingBox selftest complete."); + } +} Test; + +#endif + + + + + +cBoundingBox::cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ) : + m_Min(a_MinX, a_MinY, a_MinZ), + m_Max(a_MaxX, a_MaxY, a_MaxZ) +{ +} + + + + + +cBoundingBox::cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max) : + m_Min(a_Min), + m_Max(a_Max) +{ +} + + + + + +cBoundingBox::cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height) : + m_Min(a_Pos.x - a_Radius, a_Pos.y, a_Pos.z - a_Radius), + m_Max(a_Pos.x + a_Radius, a_Pos.y + a_Height, a_Pos.z + a_Radius) +{ +} + + + + + +cBoundingBox::cBoundingBox(const cBoundingBox & a_Orig) : + m_Min(a_Orig.m_Min), + m_Max(a_Orig.m_Max) +{ +} + + + + + +void cBoundingBox::Move(double a_OffX, double a_OffY, double a_OffZ) +{ + m_Min.x += a_OffX; + m_Min.y += a_OffY; + m_Min.z += a_OffZ; + m_Max.x += a_OffX; + m_Max.y += a_OffY; + m_Max.z += a_OffZ; +} + + + + + +void cBoundingBox::Move(const Vector3d & a_Off) +{ + m_Min.x += a_Off.x; + m_Min.y += a_Off.y; + m_Min.z += a_Off.z; + m_Max.x += a_Off.x; + m_Max.y += a_Off.y; + m_Max.z += a_Off.z; +} + + + + + +void cBoundingBox::Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ) +{ + m_Min.x -= a_ExpandX; + m_Min.y -= a_ExpandY; + m_Min.z -= a_ExpandZ; + m_Max.x += a_ExpandX; + m_Max.y += a_ExpandY; + m_Max.z += a_ExpandZ; +} + + + + + +bool cBoundingBox::DoesIntersect(const cBoundingBox & a_Other) +{ + return ( + ((a_Other.m_Min.x <= m_Max.x) && (a_Other.m_Max.x >= m_Min.x)) && // X coords intersect + ((a_Other.m_Min.y <= m_Max.y) && (a_Other.m_Max.y >= m_Min.y)) && // Y coords intersect + ((a_Other.m_Min.z <= m_Max.z) && (a_Other.m_Max.z >= m_Min.z)) // Z coords intersect + ); +} + + + + + +cBoundingBox cBoundingBox::Union(const cBoundingBox & a_Other) +{ + return cBoundingBox( + std::min(m_Min.x, a_Other.m_Min.x), + std::min(m_Min.y, a_Other.m_Min.y), + std::min(m_Min.z, a_Other.m_Min.z), + std::max(m_Max.x, a_Other.m_Max.x), + std::max(m_Max.y, a_Other.m_Max.y), + std::max(m_Max.z, a_Other.m_Max.z) + ); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Point) +{ + return IsInside(m_Min, m_Max, a_Point); +} + + + + + +bool cBoundingBox::IsInside(double a_X, double a_Y,double a_Z) +{ + return IsInside(m_Min, m_Max, a_X, a_Y, a_Z); +} + + + + + +bool cBoundingBox::IsInside(cBoundingBox & a_Other) +{ + // If both a_Other's coords are inside this, then the entire a_Other is inside + return (IsInside(a_Other.m_Min) && IsInside(a_Other.m_Max)); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max) +{ + // If both coords are inside this, then the entire a_Other is inside + return (IsInside(a_Min) && IsInside(a_Max)); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point) +{ + return ( + ((a_Point.x >= a_Min.x) && (a_Point.x <= a_Max.x)) && + ((a_Point.y >= a_Min.y) && (a_Point.y <= a_Max.y)) && + ((a_Point.z >= a_Min.z) && (a_Point.z <= a_Max.z)) + ); +} + + + + + +bool cBoundingBox::IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z) +{ + return ( + ((a_X >= a_Min.x) && (a_X <= a_Max.x)) && + ((a_Y >= a_Min.y) && (a_Y <= a_Max.y)) && + ((a_Z >= a_Min.z) && (a_Z <= a_Max.z)) + ); +} + + + + + +bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face) +{ + return CalcLineIntersection(m_Min, m_Max, a_Line1, a_Line2, a_LineCoeff, a_Face); +} + + + + + +bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face) +{ + if (IsInside(a_Min, a_Max, a_Line1)) + { + // The starting point is inside the bounding box. + a_LineCoeff = 0; + a_Face = BLOCK_FACE_NONE; // No faces hit + return true; + } + + char Face = BLOCK_FACE_NONE; + double Coeff = Vector3d::NO_INTERSECTION; + + // Check each individual bbox face for intersection with the line, remember the one with the lowest coeff + double c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Min.z); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; + Coeff = c; + } + c = a_Line1.LineCoeffToXYPlane(a_Line2, a_Max.z); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.z > a_Line2.z) ? BLOCK_FACE_ZP : BLOCK_FACE_ZM; + Coeff = c; + } + c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Min.y); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; + Coeff = c; + } + c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y); + if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM; + Coeff = c; + } + c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Min.x); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; + Coeff = c; + } + c = a_Line1.LineCoeffToYZPlane(a_Line2, a_Max.x); + if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c)) + { + Face = (a_Line1.x > a_Line2.x) ? BLOCK_FACE_XP : BLOCK_FACE_XM; + Coeff = c; + } + + if (Coeff >= Vector3d::NO_INTERSECTION) + { + // There has been no intersection + return false; + } + + a_LineCoeff = Coeff; + a_Face = Face; + return true; +} + + + + + +bool cBoundingBox::Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection) +{ + a_Intersection.m_Min.x = std::max(m_Min.x, a_Other.m_Min.x); + a_Intersection.m_Max.x = std::min(m_Max.x, a_Other.m_Max.x); + if (a_Intersection.m_Min.x >= a_Intersection.m_Max.x) + { + return false; + } + a_Intersection.m_Min.y = std::max(m_Min.y, a_Other.m_Min.y); + a_Intersection.m_Max.y = std::min(m_Max.y, a_Other.m_Max.y); + if (a_Intersection.m_Min.y >= a_Intersection.m_Max.y) + { + return false; + } + a_Intersection.m_Min.z = std::max(m_Min.z, a_Other.m_Min.z); + a_Intersection.m_Max.z = std::min(m_Max.z, a_Other.m_Max.z); + if (a_Intersection.m_Min.z >= a_Intersection.m_Max.z) + { + return false; + } + return true; +} + + + + diff --git a/src/BoundingBox.h b/src/BoundingBox.h new file mode 100644 index 000000000..ff9963989 --- /dev/null +++ b/src/BoundingBox.h @@ -0,0 +1,90 @@ + +// BoundingBox.h + +// Declares the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords + + + + +#pragma once + +#include "Vector3d.h" + + + + + +// tolua_begin + +/** Represents two sets of coords, minimum and maximum for each direction. +All the coords within those limits (inclusive the edges) are considered "inside" the box. +For intersection purposes, though, if the intersection is "sharp" in any coord (i. e. zero volume), +the boxes are considered non-intersecting. +*/ +class cBoundingBox +{ +public: + cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ); + cBoundingBox(const Vector3d & a_Min, const Vector3d & a_Max); + cBoundingBox(const Vector3d & a_Pos, double a_Radius, double a_Height); + cBoundingBox(const cBoundingBox & a_Orig); + + /// Moves the entire boundingbox by the specified offset + void Move(double a_OffX, double a_OffY, double a_OffZ); + + /// Moves the entire boundingbox by the specified offset + void Move(const Vector3d & a_Off); + + /// Expands the bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each direction) + void Expand(double a_ExpandX, double a_ExpandY, double a_ExpandZ); + + /// Returns true if the two bounding boxes intersect + bool DoesIntersect(const cBoundingBox & a_Other); + + /// Returns the union of the two bounding boxes + cBoundingBox Union(const cBoundingBox & a_Other); + + /// Returns true if the point is inside the bounding box + bool IsInside(const Vector3d & a_Point); + + /// Returns true if the point is inside the bounding box + bool IsInside(double a_X, double a_Y,double a_Z); + + /// Returns true if a_Other is inside this bounding box + bool IsInside(cBoundingBox & a_Other); + + /// Returns true if a boundingbox specified by a_Min and a_Max is inside this bounding box + bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max); + + /// Returns true if the specified point is inside the bounding box specified by its min/max corners + static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Point); + + /// Returns true if the specified point is inside the bounding box specified by its min/max corners + static bool IsInside(const Vector3d & a_Min, const Vector3d & a_Max, double a_X, double a_Y, double a_Z); + + /** Returns true if this bounding box is intersected by the line specified by its two points + Also calculates the distance along the line in which the intersection occurs (0 .. 1) + Only forward collisions (a_LineCoeff >= 0) are returned. + */ + bool CalcLineIntersection(const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face); + + /** Returns true if the specified bounding box is intersected by the line specified by its two points + Also calculates the distance along the line in which the intersection occurs (0 .. 1) and the face hit (BLOCK_FACE_ constants) + Only forward collisions (a_LineCoeff >= 0) are returned. + */ + static bool CalcLineIntersection(const Vector3d & a_Min, const Vector3d & a_Max, const Vector3d & a_Line1, const Vector3d & a_Line2, double & a_LineCoeff, char & a_Face); + + // tolua_end + + /// Calculates the intersection of the two bounding boxes; returns true if nonempty + bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection); + +protected: + Vector3d m_Min; + Vector3d m_Max; + +} ; // tolua_export + + + + diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp new file mode 100644 index 000000000..8f2b76c1f --- /dev/null +++ b/src/ByteBuffer.cpp @@ -0,0 +1,841 @@ + +// ByteBuffer.cpp + +// Implements the cByteBuffer class representing a ringbuffer of bytes + +#include "Globals.h" + +#include "ByteBuffer.h" +#include "Endianness.h" +#include "OSSupport/IsThread.h" + + + + + +// Try to determine endianness: +#if ( \ + defined(__i386__) || defined(__alpha__) || \ + defined(__ia64) || defined(__ia64__) || \ + defined(_M_IX86) || defined(_M_IA64) || \ + defined(_M_ALPHA) || defined(__amd64) || \ + defined(__amd64__) || defined(_M_AMD64) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_X64) || defined(__bfin__) || \ + defined(__ARMEL__) || \ + (defined(_WIN32) && defined(__ARM__) && defined(_MSC_VER)) \ +) + #define IS_LITTLE_ENDIAN +#elif defined (__ARMEB__) + #define IS_BIG_ENDIAN +#else + #error Cannot determine endianness of this platform +#endif + +// If a string sent over the protocol is larger than this, a warning is emitted to the console +#define MAX_STRING_SIZE (512 KiB) + +#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; // Check if at least Num bytes can be read from the buffer, return false if not +#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; // Check if at least Num bytes can be written to the buffer, return false if not + + + + + +#if 0 + +/// Self-test of the VarInt-reading and writing code +class cByteBufferSelfTest +{ +public: + cByteBufferSelfTest(void) + { + TestRead(); + TestWrite(); + } + + void TestRead(void) + { + cByteBuffer buf(50); + buf.Write("\x05\xac\x02\x00", 4); + UInt32 v1; + ASSERT(buf.ReadVarInt(v1) && (v1 == 5)); + UInt32 v2; + ASSERT(buf.ReadVarInt(v2) && (v2 == 300)); + UInt32 v3; + ASSERT(buf.ReadVarInt(v3) && (v3 == 0)); + } + + void TestWrite(void) + { + cByteBuffer buf(50); + buf.WriteVarInt(5); + buf.WriteVarInt(300); + buf.WriteVarInt(0); + AString All; + buf.ReadAll(All); + ASSERT(All.size() == 4); + ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0); + } +} g_ByteBufferTest; + +#endif + + + + + +#ifdef _DEBUG + +/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously +class cSingleThreadAccessChecker +{ +public: + cSingleThreadAccessChecker(unsigned long * a_ThreadID) : + m_ThreadID(a_ThreadID) + { + ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); + } + + ~cSingleThreadAccessChecker() + { + *m_ThreadID = 0; + } + +protected: + unsigned long * m_ThreadID; +} ; + +#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID)) + +#else + #define CHECK_THREAD +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cByteBuffer: + +cByteBuffer::cByteBuffer(int a_BufferSize) : + m_Buffer(new char[a_BufferSize + 1]), + m_BufferSize(a_BufferSize + 1), + #ifdef _DEBUG + m_ThreadID(0), + #endif // _DEBUG + m_DataStart(0), + m_WritePos(0), + m_ReadPos(0) +{ + // Allocating one byte more than the buffer size requested, so that we can distinguish between + // completely-full and completely-empty states +} + + + + + +cByteBuffer::~cByteBuffer() +{ + CheckValid(); + delete[] m_Buffer; +} + + + + + +bool cByteBuffer::Write(const char * a_Bytes, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + + // Store the current free space for a check after writing: + int CurFreeSpace = GetFreeSpace(); + int CurReadableSpace = GetReadableSpace(); + int WrittenBytes = 0; + + if (GetFreeSpace() < a_Count) + { + return false; + } + int TillEnd = m_BufferSize - m_WritePos; + if (TillEnd <= a_Count) + { + // Need to wrap around the ringbuffer end + if (TillEnd > 0) + { + memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd); + a_Bytes += TillEnd; + a_Count -= TillEnd; + WrittenBytes = TillEnd; + } + m_WritePos = 0; + } + + // We're guaranteed that we'll fit in a single write op + if (a_Count > 0) + { + memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count); + m_WritePos += a_Count; + WrittenBytes += a_Count; + } + + ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes); + ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes); + return true; +} + + + + + +int cByteBuffer::GetFreeSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + if (m_WritePos >= m_DataStart) + { + // Wrap around the buffer end: + return m_BufferSize - m_WritePos + m_DataStart - 1; + } + // Single free space partition: + return m_DataStart - m_WritePos - 1; +} + + + + + +/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() +int cByteBuffer::GetUsedSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + return m_BufferSize - GetFreeSpace() - 1; +} + + + + + +/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) +int cByteBuffer::GetReadableSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + if (m_ReadPos > m_WritePos) + { + // Wrap around the buffer end: + return m_BufferSize - m_ReadPos + m_WritePos; + } + // Single readable space partition: + return m_WritePos - m_ReadPos ; +} + + + + + +bool cByteBuffer::CanReadBytes(int a_Count) const +{ + CHECK_THREAD; + CheckValid(); + return (a_Count <= GetReadableSpace()); +} + + + + + +bool cByteBuffer::CanWriteBytes(int a_Count) const +{ + CHECK_THREAD; + CheckValid(); + return (a_Count <= GetFreeSpace()); +} + + + + + +bool cByteBuffer::ReadChar(char & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + ReadBuf(&a_Value, 1); + return true; +} + + + + + +bool cByteBuffer::ReadByte(unsigned char & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + ReadBuf(&a_Value, 1); + return true; +} + + + + + +bool cByteBuffer::ReadBEShort(short & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(2); + ReadBuf(&a_Value, 2); + a_Value = ntohs(a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEInt(int & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = ntohl(a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEInt64(Int64 & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(8); + ReadBuf(&a_Value, 8); + a_Value = NetworkToHostLong8(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEFloat(float & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = NetworkToHostFloat4(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEDouble(double & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(8); + ReadBuf(&a_Value, 8); + a_Value = NetworkToHostDouble8(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBool(bool & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + char Value = 0; + ReadBuf(&Value, 1); + a_Value = (Value != 0); + return true; +} + + + + + +bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + short Length; + if (!ReadBEShort(Length)) + { + return false; + } + if (Length < 0) + { + ASSERT(!"Negative string length? Are you sure?"); + return true; + } + return ReadUTF16String(a_Value, Length); +} + + + + + +bool cByteBuffer::ReadVarInt(UInt32 & a_Value) +{ + CHECK_THREAD; + CheckValid(); + UInt32 Value = 0; + int Shift = 0; + unsigned char b = 0; + do + { + NEEDBYTES(1); + ReadBuf(&b, 1); + Value = Value | (((Int64)(b & 0x7f)) << Shift); + Shift += 7; + } while ((b & 0x80) != 0); + a_Value = Value; + return true; +} + + + + + +bool cByteBuffer::ReadVarUTF8String(AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + UInt32 Size = 0; + if (!ReadVarInt(Size)) + { + return false; + } + if (Size > MAX_STRING_SIZE) + { + LOGWARNING("%s: String too large: %llu (%llu KiB)", __FUNCTION__, Size, Size / 1024); + } + return ReadString(a_Value, (int)Size); +} + + + + + +bool cByteBuffer::ReadLEInt(int & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + + #ifdef IS_BIG_ENDIAN + // Convert: + a_Value = ((a_Value >> 24) & 0xff) | ((a_Value >> 16) & 0xff00) | ((a_Value >> 8) & 0xff0000) | (a_Value & 0xff000000); + #endif + + return true; +} + + + + + +bool cByteBuffer::WriteChar(char a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + +bool cByteBuffer::WriteByte(unsigned char a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + +bool cByteBuffer::WriteBEShort(short a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(2); + short Converted = htons(a_Value); + return WriteBuf(&Converted, 2); +} + + + + + +bool cByteBuffer::WriteBEInt(int a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(4); + int Converted = HostToNetwork4(&a_Value); + return WriteBuf(&Converted, 4); +} + + + + + +bool cByteBuffer::WriteBEInt64(Int64 a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(8); + Int64 Converted = HostToNetwork8(&a_Value); + return WriteBuf(&Converted, 8); +} + + + + + +bool cByteBuffer::WriteBEFloat(float a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(4); + int Converted = HostToNetwork4(&a_Value); + return WriteBuf(&Converted, 4); +} + + + + + +bool cByteBuffer::WriteBEDouble(double a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(8); + Int64 Converted = HostToNetwork8(&a_Value); + return WriteBuf(&Converted, 8); +} + + + + + + +bool cByteBuffer::WriteBool(bool a_Value) +{ + CHECK_THREAD; + CheckValid(); + return WriteChar(a_Value ? 1 : 0); +} + + + + + +bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(2); + AString UTF16BE; + UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE); + WriteBEShort((short)(UTF16BE.size() / 2)); + PUTBYTES(UTF16BE.size()); + WriteBuf(UTF16BE.data(), UTF16BE.size()); + return true; +} + + + + + +bool cByteBuffer::WriteVarInt(UInt32 a_Value) +{ + CHECK_THREAD; + CheckValid(); + + // A 32-bit integer can be encoded by at most 5 bytes: + unsigned char b[5]; + int idx = 0; + do + { + b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); + a_Value = a_Value >> 7; + idx++; + } while (a_Value > 0); + + return WriteBuf(b, idx); +} + + + + +bool cByteBuffer::WriteVarUTF8String(const AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early. + bool res = WriteVarInt(a_Value.size()); + if (!res) + { + return false; + } + return WriteBuf(a_Value.data(), a_Value.size()); +} + + + + + +bool cByteBuffer::WriteLEInt(int a_Value) +{ + CHECK_THREAD; + CheckValid(); + #ifdef IS_LITTLE_ENDIAN + return WriteBuf((const char *)&a_Value, 4); + #else + int Value = ((a_Value >> 24) & 0xff) | ((a_Value >> 16) & 0xff00) | ((a_Value >> 8) & 0xff0000) | (a_Value & 0xff000000); + return WriteBuf((const char *)&Value, 4); + #endif +} + + + + + +bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + NEEDBYTES(a_Count); + char * Dst = (char *)a_Buffer; // So that we can do byte math + int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; + ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + if (BytesToEndOfBuffer > 0) + { + memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer); + Dst += BytesToEndOfBuffer; + a_Count -= BytesToEndOfBuffer; + } + m_ReadPos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memcpy(Dst, m_Buffer + m_ReadPos, a_Count); + m_ReadPos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + PUTBYTES(a_Count); + char * Src = (char *)a_Buffer; // So that we can do byte math + int BytesToEndOfBuffer = m_BufferSize - m_WritePos; + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer); + Src += BytesToEndOfBuffer; + a_Count -= BytesToEndOfBuffer; + m_WritePos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memcpy(m_Buffer + m_WritePos, Src, a_Count); + m_WritePos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::ReadString(AString & a_String, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + NEEDBYTES(a_Count); + a_String.clear(); + a_String.reserve(a_Count); + int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; + ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + if (BytesToEndOfBuffer > 0) + { + a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); + a_Count -= BytesToEndOfBuffer; + } + m_ReadPos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + a_String.append(m_Buffer + m_ReadPos, a_Count); + m_ReadPos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) +{ + // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String + CHECK_THREAD; + CheckValid(); + ASSERT(a_NumChars >= 0); + AString RawData; + if (!ReadString(RawData, a_NumChars * 2)) + { + return false; + } + RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String); + return true; +} + + + + + +bool cByteBuffer::SkipRead(int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + if (!CanReadBytes(a_Count)) + { + return false; + } + AdvanceReadPos(a_Count); + return true; +} + + + + + +void cByteBuffer::ReadAll(AString & a_Data) +{ + CHECK_THREAD; + CheckValid(); + ReadString(a_Data, GetReadableSpace()); +} + + + + + +void cByteBuffer::CommitRead(void) +{ + CHECK_THREAD; + CheckValid(); + m_DataStart = m_ReadPos; +} + + + + + +void cByteBuffer::ResetRead(void) +{ + CHECK_THREAD; + CheckValid(); + m_ReadPos = m_DataStart; +} + + + + + +void cByteBuffer::ReadAgain(AString & a_Out) +{ + // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) + // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party + CHECK_THREAD; + CheckValid(); + int DataStart = m_DataStart; + if (m_ReadPos < m_DataStart) + { + // Across the ringbuffer end, read the first part and adjust next part's start: + a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); + DataStart = 0; + } + a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); +} + + + + + +void cByteBuffer::AdvanceReadPos(int a_Count) +{ + CHECK_THREAD; + CheckValid(); + m_ReadPos += a_Count; + if (m_ReadPos > m_BufferSize) + { + m_ReadPos -= m_BufferSize; + } +} + + + + + +void cByteBuffer::CheckValid(void) const +{ + ASSERT(m_ReadPos >= 0); + ASSERT(m_ReadPos < m_BufferSize); + ASSERT(m_WritePos >= 0); + ASSERT(m_WritePos < m_BufferSize); +} + + + + diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h new file mode 100644 index 000000000..a9dd7f5ea --- /dev/null +++ b/src/ByteBuffer.h @@ -0,0 +1,139 @@ + +// ByteStream.h + +// Interfaces to the cByteBuffer class representing a ringbuffer of bytes + + + + + +#pragma once + + + + + +/** An object that can store incoming bytes and lets its clients read the bytes sequentially +The bytes are stored in a ringbuffer of constant size; if more than that size +is requested, the write operation fails. +The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed +number of bytes are present in the buffer (ASSERT; for performance reasons). +The reading doesn't actually remove the bytes, it only moves the internal read ptr. +To remove the bytes, call CommitRead(). +To re-start reading from the beginning, call ResetRead(). +This class doesn't implement thread safety, the clients of this class need to provide +their own synchronization. +*/ +class cByteBuffer +{ +public: + cByteBuffer(int a_BufferSize); + ~cByteBuffer(); + + /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not + bool Write(const char * a_Bytes, int a_Count); + + /// Returns the number of bytes that can be successfully written to the ringbuffer + int GetFreeSpace(void) const; + + /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() + int GetUsedSpace(void) const; + + /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) + int GetReadableSpace(void) const; + + /// Returns true if the specified amount of bytes are available for reading + bool CanReadBytes(int a_Count) const; + + /// Returns true if the specified amount of bytes are available for writing + bool CanWriteBytes(int a_Count) const; + + // Read the specified datatype and advance the read pointer; return true if successfully read: + bool ReadChar (char & a_Value); + bool ReadByte (unsigned char & a_Value); + bool ReadBEShort (short & a_Value); + bool ReadBEInt (int & a_Value); + bool ReadBEInt64 (Int64 & a_Value); + bool ReadBEFloat (float & a_Value); + bool ReadBEDouble (double & a_Value); + bool ReadBool (bool & a_Value); + bool ReadBEUTF16String16(AString & a_Value); // string length as BE short, then string as UTF-16BE + bool ReadVarInt (UInt32 & a_Value); + bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8 + bool ReadLEInt (int & a_Value); + + /// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...) + template <typename T> bool ReadVarInt(T & a_Value) + { + UInt32 v; + bool res = ReadVarInt(v); + if (res) + { + a_Value = v; + } + return res; + } + + // Write the specified datatype; return true if successfully written + bool WriteChar (char a_Value); + bool WriteByte (unsigned char a_Value); + bool WriteBEShort (short a_Value); + bool WriteBEInt (int a_Value); + bool WriteBEInt64 (Int64 a_Value); + bool WriteBEFloat (float a_Value); + bool WriteBEDouble (double a_Value); + bool WriteBool (bool a_Value); + bool WriteBEUTF16String16(const AString & a_Value); // string length as BE short, then string as UTF-16BE + bool WriteVarInt (UInt32 a_Value); + bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8 + bool WriteLEInt (int a_Value); + + /// Reads a_Count bytes into a_Buffer; returns true if successful + bool ReadBuf(void * a_Buffer, int a_Count); + + /// Writes a_Count bytes into a_Buffer; returns true if successful + bool WriteBuf(const void * a_Buffer, int a_Count); + + /// Reads a_Count bytes into a_String; returns true if successful + bool ReadString(AString & a_String, int a_Count); + + /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String + bool ReadUTF16String(AString & a_String, int a_NumChars); + + /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer + bool SkipRead(int a_Count); + + /// Reads all available data into a_Data + void ReadAll(AString & a_Data); + + /// Removes the bytes that have been read from the ringbuffer + void CommitRead(void); + + /// Restarts next reading operation at the start of the ringbuffer + void ResetRead(void); + + /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication + void ReadAgain(AString & a_Out); + + /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs + void CheckValid(void) const; + +protected: + char * m_Buffer; + int m_BufferSize; // Total size of the ringbuffer + + #ifdef _DEBUG + unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker + #endif // _DEBUG + + int m_DataStart; // Where the data starts in the ringbuffer + int m_WritePos; // Where the data ends in the ringbuffer + int m_ReadPos; // Where the next read will start in the ringbuffer + + /// Advances the m_ReadPos by a_Count bytes + void AdvanceReadPos(int a_Count); +} ; + + + + diff --git a/src/ChatColor.cpp b/src/ChatColor.cpp new file mode 100644 index 000000000..2b223ee76 --- /dev/null +++ b/src/ChatColor.cpp @@ -0,0 +1,39 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChatColor.h" + +const std::string cChatColor::Color = "\xc2\xa7"; // or in other words: "§" in UTF-8 +const std::string cChatColor::Delimiter = "\xc2\xa7"; // or in other words: "§" in UTF-8 +const std::string cChatColor::Black = cChatColor::Color + "0"; +const std::string cChatColor::Navy = cChatColor::Color + "1"; +const std::string cChatColor::Green = cChatColor::Color + "2"; +const std::string cChatColor::Blue = cChatColor::Color + "3"; +const std::string cChatColor::Red = cChatColor::Color + "4"; +const std::string cChatColor::Purple = cChatColor::Color + "5"; +const std::string cChatColor::Gold = cChatColor::Color + "6"; +const std::string cChatColor::LightGray = cChatColor::Color + "7"; +const std::string cChatColor::Gray = cChatColor::Color + "8"; +const std::string cChatColor::DarkPurple = cChatColor::Color + "9"; +const std::string cChatColor::LightGreen = cChatColor::Color + "a"; +const std::string cChatColor::LightBlue = cChatColor::Color + "b"; +const std::string cChatColor::Rose = cChatColor::Color + "c"; +const std::string cChatColor::LightPurple = cChatColor::Color + "d"; +const std::string cChatColor::Yellow = cChatColor::Color + "e"; +const std::string cChatColor::White = cChatColor::Color + "f"; + +const std::string cChatColor::Random = cChatColor::Color + "k"; +const std::string cChatColor::Bold = cChatColor::Color + "l"; +const std::string cChatColor::Strikethrough = cChatColor::Color + "m"; +const std::string cChatColor::Underlined = cChatColor::Color + "n"; +const std::string cChatColor::Italic = cChatColor::Color + "o"; +const std::string cChatColor::Plain = cChatColor::Color + "r"; + +const std::string cChatColor::MakeColor( char a_Color ) +{ + return cChatColor::Color + a_Color; +} + + + + diff --git a/src/ChatColor.h b/src/ChatColor.h new file mode 100644 index 000000000..85b10f400 --- /dev/null +++ b/src/ChatColor.h @@ -0,0 +1,43 @@ + +#pragma once + + + + + +// tolua_begin +class cChatColor +{ +public: + static const std::string Color; + static const std::string Delimiter; + + static const std::string Black; + static const std::string Navy; + static const std::string Green; + static const std::string Blue; + static const std::string Red; + static const std::string Purple; + static const std::string Gold; + static const std::string LightGray; + static const std::string Gray; + static const std::string DarkPurple; + static const std::string LightGreen; + static const std::string LightBlue; + static const std::string Rose; + static const std::string LightPurple; + static const std::string Yellow; + static const std::string White; + + // Styles ( source: http://wiki.vg/Chat ) + static const std::string Random; + static const std::string Bold; + static const std::string Strikethrough; + static const std::string Underlined; + static const std::string Italic; + static const std::string Plain; + + static const std::string MakeColor( char a_Color ); +}; + +// tolua_end diff --git a/src/Chunk.cpp b/src/Chunk.cpp new file mode 100644 index 000000000..83f866e56 --- /dev/null +++ b/src/Chunk.cpp @@ -0,0 +1,2776 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 + #include <cstdlib> +#endif + + +#include "Chunk.h" +#include "World.h" +#include "ClientHandle.h" +#include "Server.h" +#include "zlib/zlib.h" +#include "Defines.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/DispenserEntity.h" +#include "BlockEntities/DropperEntity.h" +#include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" +#include "BlockEntities/JukeboxEntity.h" +#include "BlockEntities/NoteEntity.h" +#include "BlockEntities/SignEntity.h" +#include "Entities/Pickup.h" +#include "Item.h" +#include "Noise.h" +#include "Root.h" +#include "MersenneTwister.h" +#include "Entities/Player.h" +#include "BlockArea.h" +#include "PluginManager.h" +#include "Blocks/BlockHandler.h" +#include "Simulator/FluidSimulator.h" +#include "MobCensus.h" +#include "MobSpawner.h" + + +#include "lib/jsoncpp/include/json/json.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// sSetBlock: + +sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) // absolute block position + : x( a_BlockX ) + , y( a_BlockY ) + , z( a_BlockZ ) + , BlockType( a_BlockType ) + , BlockMeta( a_BlockMeta ) +{ + cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunk: + +cChunk::cChunk( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + cChunkMap * a_ChunkMap, cWorld * a_World, + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP +) + : m_PosX( a_ChunkX ) + , m_PosY( a_ChunkY ) + , m_PosZ( a_ChunkZ ) + , m_BlockTickX( 0 ) + , m_BlockTickY( 0 ) + , m_BlockTickZ( 0 ) + , m_World( a_World ) + , m_ChunkMap(a_ChunkMap) + , m_IsValid(false) + , m_IsLightValid(false) + , m_IsDirty(false) + , m_IsSaving(false) + , m_StayCount(0) + , m_NeighborXM(a_NeighborXM) + , m_NeighborXP(a_NeighborXP) + , m_NeighborZM(a_NeighborZM) + , m_NeighborZP(a_NeighborZP) + , m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()) + , m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()) +{ + if (a_NeighborXM != NULL) + { + a_NeighborXM->m_NeighborXP = this; + } + if (a_NeighborXP != NULL) + { + a_NeighborXP->m_NeighborXM = this; + } + if (a_NeighborZM != NULL) + { + a_NeighborZM->m_NeighborZP = this; + } + if (a_NeighborZP != NULL) + { + a_NeighborZP->m_NeighborZM = this; + } +} + + + + + +cChunk::~cChunk() +{ + cPluginManager::Get()->CallHookChunkUnloaded(m_World, m_PosX, m_PosZ); + + // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() ); + + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + delete *itr; + } + m_BlockEntities.clear(); + + // Remove and destroy all entities that are not players: + cEntityList Entities; + std::swap(Entities, m_Entities); // Need another list because cEntity destructors check if they've been removed from chunk + for (cEntityList::const_iterator itr = Entities.begin(); itr != Entities.end(); ++itr) + { + if (!(*itr)->IsPlayer()) + { + (*itr)->Destroy(false); + delete *itr; + } + } + + if (m_NeighborXM != NULL) + { + m_NeighborXM->m_NeighborXP = NULL; + } + if (m_NeighborXP != NULL) + { + m_NeighborXP->m_NeighborXM = NULL; + } + if (m_NeighborZM != NULL) + { + m_NeighborZM->m_NeighborZP = NULL; + } + if (m_NeighborZP != NULL) + { + m_NeighborZP->m_NeighborZM = NULL; + } + delete m_WaterSimulatorData; + delete m_LavaSimulatorData; +} + + + + + +void cChunk::SetValid(void) +{ + m_IsValid = true; + + m_World->GetChunkMap()->ChunkValidated(); +} + + + + + +void cChunk::MarkRegenerating(void) +{ + // Tell all clients attached to this chunk that they want this chunk: + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + (*itr)->AddWantedChunk(m_PosX, m_PosZ); + } // for itr - m_LoadedByClient[] +} + + + + + +bool cChunk::CanUnload(void) +{ + return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0); +} + + + + + +void cChunk::MarkSaving(void) +{ + m_IsSaving = true; +} + + + + + +void cChunk::MarkSaved(void) +{ + if (!m_IsSaving) + { + return; + } + m_IsDirty = false; +} + + + + + +void cChunk::MarkLoaded(void) +{ + m_IsDirty = false; + SetValid(); +} + + + + + +void cChunk::MarkLoadFailed(void) +{ + if (m_IsValid) + { + return; + } + + m_HasLoadFailed = true; +} + + + + + +void cChunk::GetAllData(cChunkDataCallback & a_Callback) +{ + a_Callback.HeightMap (&m_HeightMap); + a_Callback.BiomeData (&m_BiomeMap); + a_Callback.BlockTypes (m_BlockTypes); + a_Callback.BlockMeta (m_BlockMeta); + a_Callback.LightIsValid (m_IsLightValid); + a_Callback.BlockLight (m_BlockLight); + a_Callback.BlockSkyLight(m_BlockSkyLight); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + a_Callback.Entity(*itr); + } + + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + a_Callback.BlockEntity(*itr); + } +} + + + + + +void cChunk::SetAllData( + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const HeightMap * a_HeightMap, + const BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities +) +{ + memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap)); + + if (a_HeightMap != NULL) + { + memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap)); + } + + memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); + memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta)); + if (a_BlockLight != NULL) + { + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + } + if (a_BlockSkyLight != NULL) + { + memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); + } + + m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL); + + if (a_HeightMap == NULL) + { + CalculateHeightmap(); + } + + // Clear the block entities present - either the loader / saver has better, or we'll create empty ones: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + delete *itr; + } + std::swap(a_BlockEntities, m_BlockEntities); + + // Set all block entities' World variable: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + (*itr)->SetWorld(m_World); + } + + // Create block entities that the loader didn't load; fill them with defaults + CreateBlockEntities(); + + // Set the chunk data as valid. This may be needed for some simulators that perform actions upon block adding (Vaporize) + SetValid(); + + // Wake up all simulators for their respective blocks: + WakeUpSimulators(); + + m_HasLoadFailed = false; +} + + + + + +void cChunk::SetLight( + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation. + // Postponing until we see how bad it is :) + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight)); + m_IsLightValid = true; +} + + + + + +void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes) +{ + memcpy(a_BlockTypes, m_BlockTypes, NumBlocks); +} + + + + + +void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + if ((a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas)) != (cBlockArea::baTypes | cBlockArea::baMetas)) + { + LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas (0x%x), requested 0x%x. Ignoring.", + (cBlockArea::baTypes | cBlockArea::baMetas), a_DataTypes & (cBlockArea::baTypes | cBlockArea::baMetas) + ); + return; + } + + // SizeX, SizeZ are the dimensions of the block data to copy to the chunk (size of the geometric union) + + int BlockStartX = std::max(a_MinBlockX, m_PosX * cChunkDef::Width); + int BlockEndX = std::min(a_MinBlockX + a_Area.GetSizeX(), (m_PosX + 1) * cChunkDef::Width); + int BlockStartZ = std::max(a_MinBlockZ, m_PosZ * cChunkDef::Width); + int BlockEndZ = std::min(a_MinBlockZ + a_Area.GetSizeZ(), (m_PosZ + 1) * cChunkDef::Width); + int SizeX = BlockEndX - BlockStartX; + int SizeZ = BlockEndZ - BlockStartZ; + int OffX = BlockStartX - m_PosX * cChunkDef::Width; + int OffZ = BlockStartZ - m_PosZ * cChunkDef::Width; + int BaseX = BlockStartX - a_MinBlockX; + int BaseZ = BlockStartZ - a_MinBlockZ; + int SizeY = a_Area.GetSizeY(); + + // TODO: Improve this by not calling FastSetBlock() and doing the processing here + // so that the heightmap is touched only once for each column. + BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes(); + NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas(); + for (int y = 0; y < SizeY; y++) + { + int ChunkY = a_MinBlockY + y; + int AreaY = y; + for (int z = 0; z < SizeZ; z++) + { + int ChunkZ = OffZ + z; + int AreaZ = BaseZ + z; + for (int x = 0; x < SizeX; x++) + { + int ChunkX = OffX + x; + int AreaX = BaseX + x; + int idx = a_Area.MakeIndex(AreaX, AreaY, AreaZ); + BLOCKTYPE BlockType = AreaBlockTypes[idx]; + NIBBLETYPE BlockMeta = AreaBlockMetas[idx]; + FastSetBlock(ChunkX, ChunkY, ChunkZ, BlockType, BlockMeta); + } // for x + } // for z + } // for y +} + + + + + +/// Returns true if there is a block entity at the coords specified +bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_BlockX) && + ((*itr)->GetPosY() == a_BlockY) && + ((*itr)->GetPosZ() == a_BlockZ) + ) + { + return true; + } + } // for itr - m_BlockEntities[] + return false; +} + + + + + +/// Sets or resets the internal flag that prevents chunk from being unloaded +void cChunk::Stay(bool a_Stay) +{ + m_StayCount += (a_Stay ? 1 : -1); + ASSERT(m_StayCount >= 0); +} + + + + +void cChunk::CollectMobCensus(cMobCensus& toFill) +{ + toFill.CollectSpawnableChunk(*this); + std::list<const Vector3d*> playerPositions; + cPlayer* currentPlayer; + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + currentPlayer = (*itr)->GetPlayer(); + playerPositions.push_back(&(currentPlayer->GetPosition())); + } + + Vector3d currentPosition; + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); + if ((*itr)->IsMob()) + { + cMonster& Monster = (cMonster&)(**itr); + currentPosition = Monster.GetPosition(); + for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++) + { + toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength()); + } + } + } // for itr - m_Entitites[] +} + + + + +void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) +{ + ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); + int Random = m_World->GetTickRandomNumber(0x00ffffff); + a_X = Random % (a_MaxX * 2); + a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); + a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); + a_X /= 2; + a_Y /= 2; + a_Z /= 2; +} + + + + + +void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) +{ + // MG TODO : check if this kind of optimization (only one random call) is still needed + // MG TODO : if so propagate it + + getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); + a_Y++; +} + + + + + +void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + int Center_X,Center_Y,Center_Z; + getRandomBlockCoords(Center_X,Center_Y,Center_Z); + + BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); + if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) + { + a_MobSpawner.NewPack(); + int NumberOfTries = 0; + int NumberOfSuccess = 0; + int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass + while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) + { + const int HorizontalRange = 20; // MG TODO : relocate + const int VerticalRange = 0; // MG TODO : relocate + int Try_X, Try_Y, Try_Z; + getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); + Try_X -= HorizontalRange; + Try_Y -= VerticalRange; + Try_Z -= HorizontalRange; + Try_X += Center_X; + Try_Y += Center_Y; + Try_Z += Center_Z; + + ASSERT(Try_Y > 0); + ASSERT(Try_Y < cChunkDef::Height-1); + + EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); + // MG TODO : + // Moon cycle (for slime) + // check player and playerspawn presence < 24 blocks + // check mobs presence on the block + + // MG TODO : check that "Level" really means Y + + NIBBLETYPE SkyLight = 0; + + NIBBLETYPE BlockLight = 0; + + if (IsLightValid()) + { + cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess); + if (newMob) + { + int WorldX, WorldY, WorldZ; + PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); + double ActualX = WorldX + 0.5; + double ActualZ = WorldZ + 0.5; + newMob->SetPosition(ActualX, WorldY, ActualZ); + LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); + NumberOfSuccess++; + } + } + + NumberOfTries++; + } + } + +} + + + + + +void cChunk::Tick(float a_Dt) +{ + BroadcastPendingBlockChanges(); + + // Unload the chunk from all clients that have queued unloading: + for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr) + { + (*itr)->SendUnloadChunk(m_PosX, m_PosZ); + } + m_UnloadQuery.clear(); + + // Set all blocks that have been queued for setting later: + ProcessQueuedSetBlocks(); + + CheckBlocks(); + + // Tick simulators: + m_World->GetSimulatorManager()->SimulateChunk(a_Dt, m_PosX, m_PosZ, this); + + TickBlocks(); + + // Tick all block entities in this chunk: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty; + } + + // Tick all entities in this chunk (except mobs): + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players) + if (!((*itr)->IsMob())) + { + (*itr)->Tick(a_Dt, *this); + } + } // for itr - m_Entitites[] + + // Remove all entities that were scheduled for removal: + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();) + { + if ((*itr)->IsDestroyed()) + { + LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); + cEntity * ToDelete = *itr; + itr = m_Entities.erase(itr); + delete ToDelete; + continue; + } + itr++; + } // for itr - m_Entitites[] + + // If any entity moved out of the chunk, move it to the neighbor: + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();) + { + if ( + ((*itr)->GetChunkX() != m_PosX) || + ((*itr)->GetChunkZ() != m_PosZ) + ) + { + MoveEntityToNewChunk(*itr); + itr = m_Entities.erase(itr); + } + else + { + ++itr; + } + } + + ApplyWeatherToTop(); +} + + + + + +void cChunk::MoveEntityToNewChunk(cEntity * a_Entity) +{ + cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width); + if (Neighbor == NULL) + { + Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + if (Neighbor == NULL) + { + // TODO: What to do with this? + LOGWARNING("%s: Failed to move entity, destination chunk unreachable. Entity lost", __FUNCTION__); + return; + } + } + + ASSERT(Neighbor != this); // Moving into the same chunk? wtf? + + Neighbor->AddEntity(a_Entity); + + class cMover : + public cClientDiffCallback + { + virtual void Removed(cClientHandle * a_Client) override + { + a_Client->SendDestroyEntity(*m_Entity); + } + + virtual void Added(cClientHandle * a_Client) override + { + m_Entity->SpawnOn(*a_Client); + } + + cEntity * m_Entity; + + public: + cMover(cEntity * a_Entity) : + m_Entity(a_Entity) + {} + } Mover(a_Entity); + + m_ChunkMap->CompareChunkClients(this, Neighbor, Mover); +} + + + + + +void cChunk::ProcessQueuedSetBlocks(void) +{ + Int64 CurrTick = m_World->GetWorldAge(); + for (sSetBlockQueueVector::iterator itr = m_SetBlockQueue.begin(); itr != m_SetBlockQueue.end();) + { + if (itr->m_Tick <= CurrTick) + { + // Current world age is bigger than/equal to target world age - delay time reached + SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); + itr = m_SetBlockQueue.erase(itr); + } + else + { + // Not yet + ++itr; + continue; + } + } // for itr - m_SetBlockQueue[] +} + + + + + +void cChunk::BroadcastPendingBlockChanges(void) +{ + if (m_PendingSendBlocks.empty()) + { + return; + } + + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + } + m_PendingSendBlocks.clear(); +} + + + + + +void cChunk::CheckBlocks(void) +{ + if (m_ToTickBlocks.size() == 0) + { + return; + } + std::vector<unsigned int> ToTickBlocks; + std::swap(m_ToTickBlocks, ToTickBlocks); + + for (std::vector<unsigned int>::const_iterator itr = ToTickBlocks.begin(), end = ToTickBlocks.end(); itr != end; ++itr) + { + unsigned int index = (*itr); + Vector3i BlockPos = IndexToCoordinate(index); + + cBlockHandler * Handler = BlockHandler(GetBlock(index)); + Handler->Check(BlockPos.x, BlockPos.y, BlockPos.z, *this); + } // for itr - ToTickBlocks[] +} + + + + + +void cChunk::TickBlocks(void) +{ + // Tick dem blocks + // _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 ) + int RandomX = m_World->GetTickRandomNumber(0x00ffffff); + int RandomY = m_World->GetTickRandomNumber(0x00ffffff); + int RandomZ = m_World->GetTickRandomNumber(0x00ffffff); + int TickX = m_BlockTickX; + int TickY = m_BlockTickY; + int TickZ = m_BlockTickZ; + + // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it + // This is so that SetNextBlockTick() works + for (int i = 0; i < 50; i++, + + // This weird construct (*2, then /2) is needed, + // otherwise the blocktick distribution is too biased towards even coords! + + TickX = (TickX + RandomX) % (Width * 2), + TickY = (TickY + RandomY) % (Height * 2), + TickZ = (TickZ + RandomZ) % (Width * 2), + m_BlockTickX = TickX / 2, + m_BlockTickY = TickY / 2, + m_BlockTickZ = TickZ / 2 + ) + { + + if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ)) + { + continue; // It's all air up here + } + + unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ); + cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]); + ASSERT(Handler != NULL); // Happenned on server restart, FS #243 + Handler->OnUpdate(m_World, m_BlockTickX + m_PosX * Width, m_BlockTickY, m_BlockTickZ + m_PosZ * Width); + } // for i - tickblocks +} + + + + + +void cChunk::ApplyWeatherToTop() +{ + if ( + (m_World->GetTickRandomNumber(100) != 0) || + ( + (m_World->GetWeather() != eWeather_Rain) && + (m_World->GetWeather() != eWeather_ThunderStorm) + ) + ) + { + // Not the right weather, or not at this tick; bail out + return; + } + + int X = m_World->GetTickRandomNumber(15); + int Z = m_World->GetTickRandomNumber(15); + switch (GetBiomeAt(X, Z)) + { + case biTaiga: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biTaigaHills: + { + // TODO: Check light levels, don't snow over when the BlockLight is higher than (7?) + int Height = GetHeight(X, Z); + BLOCKTYPE TopBlock = GetBlock(X, Height, Z); + NIBBLETYPE TopMeta = GetMeta (X, Height, Z); + if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW)) + { + int MaxSize = 7; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta[4]; + UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]); + UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]); + UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]); + UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]); + for (int i = 0; i < 4; i++) + { + switch (BlockType[i]) + { + case E_BLOCK_AIR: + { + MaxSize = 0; + break; + } + case E_BLOCK_SNOW: + { + MaxSize = std::min(BlockMeta[i] + 1, MaxSize); + break; + } + } + } + if (TopMeta < MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1); + } + else if (TopMeta > MaxSize) + { + FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1); + } + } + else if (g_BlockIsSnowable[TopBlock]) + { + SetBlock(X, Height + 1, Z, E_BLOCK_SNOW, 0); + } + else if ((TopBlock == E_BLOCK_WATER) || (TopBlock == E_BLOCK_STATIONARY_WATER)) + { + SetBlock(X, Height, Z, E_BLOCK_ICE, 0); + } + else if ( + (m_World->IsDeepSnowEnabled()) && + ( + (TopBlock == E_BLOCK_RED_ROSE) || + (TopBlock == E_BLOCK_YELLOW_FLOWER) || + (TopBlock == E_BLOCK_RED_MUSHROOM) || + (TopBlock == E_BLOCK_BROWN_MUSHROOM) + ) + ) + { + SetBlock(X, Height, Z, E_BLOCK_SNOW, 0); + } + break; + } // case (snowy biomes) + + // TODO: Rainy biomes should check for farmland and cauldrons + } // switch (biome) +} + + + + + +void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +{ + // Convert the stem BlockType into produce BlockType + BLOCKTYPE ProduceType; + switch (a_BlockType) + { + case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; + case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; + default: + { + ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); + return; + } + } + + // Check if there's another melon / pumpkin around that stem, if so, abort: + bool IsValid; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta; // unused + IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); + if ( + !IsValid || + (BlockType[0] == ProduceType) || + (BlockType[1] == ProduceType) || + (BlockType[2] == ProduceType) || + (BlockType[3] == ProduceType) + ) + { + // Neighbors not valid or already taken by the same produce + return; + } + + // Pick a direction in which to place the produce: + int x = 0, z = 0; + int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness + switch (CheckType) + { + case 0: x = 1; break; + case 1: x = -1; break; + case 2: z = 1; break; + case 3: z = -1; break; + } + + // Check that the block in that direction is empty: + switch (BlockType[CheckType]) + { + case E_BLOCK_AIR: + case E_BLOCK_SNOW: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + { + break; + } + default: return; + } + + // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok + BLOCKTYPE Soil; + UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta); + switch (Soil) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + // DEBUG: This is here to catch FS #349 - melons growing over other crops. + LOG("Growing melon/pumpkin overwriting %s, growing on %s", + ItemTypeToString(BlockType[CheckType]).c_str(), + ItemTypeToString(Soil).c_str() + ); + // Place a randomly-facing produce: + UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4)); + break; + } + } +} + + + + + +void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +{ + // Check the total height of the sugarcane blocks here: + int Top = a_RelY + 1; + while ( + (Top < cChunkDef::Height) && + (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE) + ) + { + ++Top; + } + int Bottom = a_RelY - 1; + while ( + (Bottom > 0) && + (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE) + ) + { + --Bottom; + } + + // Grow by at most a_NumBlocks, but no more than max height: + int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom)); + for (int i = 0; i < ToGrow; i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) + { + UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0); + } + else + { + break; + } + } // for i +} + + + + + +void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +{ + // Check the total height of the sugarcane blocks here: + int Top = a_RelY + 1; + while ( + (Top < cChunkDef::Height) && + (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS) + ) + { + ++Top; + } + int Bottom = a_RelY - 1; + while ( + (Bottom > 0) && + (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS) + ) + { + --Bottom; + } + + // Grow by at most a_NumBlocks, but no more than max height: + int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom)); + for (int i = 0; i < ToGrow; i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) + { + // TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations) + UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0); + } + else + { + break; + } + } // for i +} + + + + + +bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + int idx = Chunk->MakeIndex(a_RelX, a_RelY, a_RelZ); + a_BlockLight = Chunk->GetBlockLight(idx); + a_SkyLight = Chunk->GetSkyLight(idx); + return true; +} + + + + + +bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); + return false; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + // The chunk is not available, bail out + return false; + } + Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +void cChunk::UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) + { + // Outside of chunkmap + return; + } + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->QueueTickBlock(a_RelX, a_RelY, a_RelZ); + } +} + + + + + +int cChunk::GetHeight(int a_X, int a_Z) +{ + ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); + + if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)) + { + return m_HeightMap[a_X + a_Z * Width]; + } + return 0; +} + + + + + +void cChunk::CreateBlockEntities(void) +{ + for (int x = 0; x < Width; x++) + { + for (int z = 0; z < Width; z++) + { + for (int y = 0; y < Height; y++) + { + BLOCKTYPE BlockType = cChunkDef::GetBlock(m_BlockTypes, x, y, z); + switch (BlockType) + { + case E_BLOCK_CHEST: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + case E_BLOCK_HOPPER: + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_JUKEBOX: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back(cBlockEntity::CreateByBlockType( + BlockType, GetMeta(x, y, z), + x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World + )); + } + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x +} + + + + + +void cChunk::WakeUpSimulators(void) +{ + cSimulator * WaterSimulator = m_World->GetWaterSimulator(); + cSimulator * LavaSimulator = m_World->GetLavaSimulator(); + int BaseX = m_PosX * cChunkDef::Width; + int BaseZ = m_PosZ * cChunkDef::Width; + for (int x = 0; x < Width; x++) + { + int BlockX = x + BaseX; + for (int z = 0; z < Width; z++) + { + int BlockZ = z + BaseZ; + for (int y = GetHeight(x, z); y >= 0; y--) + { + switch (cChunkDef::GetBlock(m_BlockTypes, x, y, z)) + { + case E_BLOCK_WATER: + { + WaterSimulator->AddBlock(BlockX, y, BlockZ, this); + break; + } + case E_BLOCK_LAVA: + { + LavaSimulator->AddBlock(BlockX, y, BlockZ, this); + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x +} + + + + + +void cChunk::CalculateHeightmap() +{ + for (int x = 0; x < Width; x++) + { + for (int z = 0; z < Width; z++) + { + for (int y = Height - 1; y > -1; y--) + { + int index = MakeIndex( x, y, z ); + if (m_BlockTypes[index] != E_BLOCK_AIR) + { + m_HeightMap[x + z * Width] = (unsigned char)y; + break; + } + } // for y + } // for z + } // for x +} + + + + + +void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + + const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + + // Tick this block and its neighbors: + m_ToTickBlocks.push_back(index); + QueueTickBlockNeighbors(a_RelX, a_RelY, a_RelZ); + + // If there was a block entity, remove it: + Vector3i WorldPos = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); + cBlockEntity * BlockEntity = GetBlockEntity(WorldPos); + if (BlockEntity != NULL) + { + BlockEntity->Destroy(); + RemoveBlockEntity(BlockEntity); + delete BlockEntity; + } + + // If the new block is a block entity, create the entity object: + switch (a_BlockType) + { + case E_BLOCK_CHEST: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_FURNACE: + case E_BLOCK_HOPPER: + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_JUKEBOX: + { + AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World)); + break; + } + } // switch (a_BlockType) +} + + + + + +void cChunk::QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) +{ + m_SetBlockQueue.push_back(sSetBlockQueueItem(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta, a_Tick)); +} + + + + + +void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) +{ + ASSERT ( + (a_RelX >= 0) && (a_RelX < Width) && + (a_RelY >= 0) && (a_RelY < Height) && + (a_RelZ >= 0) && (a_RelZ < Width) + ); // Coords need to be valid + + if (!IsValid()) + { + return; + } + + m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)); +} + + + + + +void cChunk::QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ) +{ + struct + { + int x, y, z; + } + Coords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 1, 0}, + { 0, -1, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + UnboundedQueueTickBlock(a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z); + } // for i - Coords[] +} + + + + + +void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) +{ + ASSERT(!((a_RelX < 0) || (a_RelX >= Width) || (a_RelY < 0) || (a_RelY >= Height) || (a_RelZ < 0) || (a_RelZ >= Width))); + + ASSERT(IsValid()); + + const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + const BLOCKTYPE OldBlockType = cChunkDef::GetBlock(m_BlockTypes, index); + const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index); + if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta)) + { + return; + } + + MarkDirty(); + + m_BlockTypes[index] = a_BlockType; + + // The client doesn't need to distinguish between stationary and nonstationary fluids: + if ( + (OldBlockMeta != a_BlockMeta) || // Different meta always gets sent to the client + !( + ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water + ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water + ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary water with water + ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing water with stationary water + ) + ) + { + m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta)); + } + + SetNibble(m_BlockMeta, index, a_BlockMeta); + + // ONLY recalculate lighting if it's necessary! + if( + (g_BlockLightValue[OldBlockType ] != g_BlockLightValue[a_BlockType]) || + (g_BlockSpreadLightFalloff[OldBlockType] != g_BlockSpreadLightFalloff[a_BlockType]) || + (g_BlockTransparent[OldBlockType] != g_BlockTransparent[a_BlockType]) + ) + { + m_IsLightValid = false; + } + + // Update heightmap, if needed: + if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width]) + { + if (a_BlockType != E_BLOCK_AIR) + { + m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)a_RelY; + } + else + { + for (int y = a_RelY - 1; y > 0; --y) + { + if (m_BlockTypes[MakeIndexNoCheck(a_RelX, y, a_RelZ)] != E_BLOCK_AIR) + { + m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y; + break; + } + } // for y - column in m_BlockData + } + } +} + + + + + +void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client) +{ + // The coords must be valid, because the upper level already does chunk lookup. No need to check them again. + // There's an debug-time assert in MakeIndexNoCheck anyway + unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + + if (a_Client == NULL) + { + // Queue the block for all clients in the chunk (will be sent in Tick()) + m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index))); + return; + } + + Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); + a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index)); + + // FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr) + { + if (((*itr)->GetPosX() == wp.x) && ((*itr)->GetPosY() == wp.y) && ((*itr)->GetPosZ() == wp.z)) + { + (*itr)->SendTo(*a_Client); + } + } // for itr - m_BlockEntities +} + + + + + +void cChunk::AddBlockEntity(cBlockEntity * a_BlockEntity) +{ + MarkDirty(); + m_BlockEntities.push_back(a_BlockEntity); +} + + + + + +cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_BlockX) && + ((*itr)->GetPosY() == a_BlockY) && + ((*itr)->GetPosZ() == a_BlockZ) + ) + { + return *itr; + } + } // for itr - m_BlockEntities[] + + return NULL; +} + + + + + +void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) +{ + cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); + if (be != NULL) + { + be->UsedBy(a_Player); + } +} + + + + + +void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) +{ + double PosX = a_Player->GetPosX(); + double PosY = a_Player->GetPosY(); + double PosZ = a_Player->GetPosZ(); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + if ((!(*itr)->IsPickup()) && (!(*itr)->IsProjectile())) + { + continue; // Only pickups and projectiles + } + float DiffX = (float)((*itr)->GetPosX() - PosX ); + float DiffY = (float)((*itr)->GetPosY() - PosY ); + float DiffZ = (float)((*itr)->GetPosZ() - PosZ ); + float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; + if (SqrDist < 1.5f * 1.5f) // 1.5 block + { + /* + LOG("Pickup %d being collected by player \"%s\", distance %f", + (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist + ); + */ + MarkDirty(); + if ((*itr)->IsPickup()) + { + (reinterpret_cast<cPickup *>(*itr))->CollectedBy(a_Player); + } + else + { + (reinterpret_cast<cProjectileEntity *>(*itr))->CollectedBy(a_Player); + } + } + else if (SqrDist < 5 * 5) + { + /* + LOG("Pickup %d close to player \"%s\", but still too far to collect: %f", + (*itr)->GetUniqueID(), a_Player->GetName().c_str(), SqrDist + ); + */ + } + } +} + + + + + +bool cChunk::SetSignLines(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + // Also sends update packets to all clients in the chunk + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_PosX) && + ((*itr)->GetPosY() == a_PosY) && + ((*itr)->GetPosZ() == a_PosZ) && + ( + ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) || + ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST) + ) + ) + { + MarkDirty(); + (reinterpret_cast<cSignEntity *>(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); + m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); + return true; + } + } // for itr - m_BlockEntities[] + return false; +} + + + + + +void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) +{ + MarkDirty(); + m_BlockEntities.remove(a_BlockEntity); +} + + + + + +bool cChunk::AddClient(cClientHandle* a_Client) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if (a_Client == *itr) + { + // Already there, nothing needed + return false; + } + } + m_LoadedByClient.push_back( a_Client ); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) + { + LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str()); + (*itr)->SpawnOn(*a_Client); + } + return true; +} + + + + + +void cChunk::RemoveClient( cClientHandle* a_Client ) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if (*itr != a_Client) + { + continue; + } + + m_LoadedByClient.erase(itr); + + if (!a_Client->IsDestroyed()) + { + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) + { + LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); + a_Client->SendDestroyEntity(*(*itr)); + } + } + return; + } // for itr - m_LoadedByClient[] +} + + + + + +bool cChunk::HasClient( cClientHandle* a_Client ) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if ((*itr) == a_Client) + { + return true; + } + } + return false; +} + + + + + +bool cChunk::HasAnyClients(void) +{ + return !m_LoadedByClient.empty(); +} + + + + + +void cChunk::AddEntity(cEntity * a_Entity) +{ + if (!a_Entity->IsPlayer()) + { + MarkDirty(); + } + + ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already + + m_Entities.push_back(a_Entity); +} + + + + + +void cChunk::RemoveEntity(cEntity * a_Entity) +{ + size_t SizeBefore = m_Entities.size(); + m_Entities.remove(a_Entity); + size_t SizeAfter = m_Entities.size(); + + if (SizeBefore != SizeAfter) + { + // Mark as dirty if it was a server-generated entity: + if (!a_Entity->IsPlayer()) + { + MarkDirty(); + } + } +} + + + + + +bool cChunk::HasEntity(int a_EntityID) +{ + for (cEntityList::const_iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) + { + if ((*itr)->GetUniqueID() == a_EntityID) + { + return true; + } + } // for itr - m_Entities[] + return false; +} + + + + + +bool cChunk::ForEachEntity(cEntityCallback & a_Callback) +{ + // The entity list is locked by the parent chunkmap's CS + for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Entitites[] + return true; +} + + + + + +bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult) +{ + // The entity list is locked by the parent chunkmap's CS + for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr) + { + if ((*itr)->GetUniqueID() == a_EntityID) + { + a_CallbackResult = a_Callback.Item(*itr); + return true; + } + } // for itr - m_Entitites[] + return false; +} + + + + + +bool cChunk::ForEachBlockEntity(cBlockEntityCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachChest(cChestCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + { + continue; + } + if (a_Callback.Item((cChestEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachDispenser(cDispenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) + { + continue; + } + if (a_Callback.Item((cDispenserEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachDropper(cDropperCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_DROPPER) + { + continue; + } + if (a_Callback.Item((cDropperEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachDropSpenser(cDropSpenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER)) + { + continue; + } + if (a_Callback.Item((cDropSpenserEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + { + break; + } + default: + { + continue; + } + } + if (a_Callback.Item((cFurnaceEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + + if (a_Callback.Item(*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cChestEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDispenserEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_DROPPER) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDropperEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if (((*itr)->GetBlockType() != E_BLOCK_DISPENSER) && ((*itr)->GetBlockType() != E_BLOCK_DROPPER)) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDropSpenserEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + { + break; + } + default: + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + } // switch (BlockType) + + // The correct block entity is here, + if (a_Callback.Item((cFurnaceEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_WALLSIGN: + case E_BLOCK_SIGN_POST: + { + a_Line1 = ((cSignEntity *)*itr)->GetLine(0); + a_Line2 = ((cSignEntity *)*itr)->GetLine(1); + a_Line3 = ((cSignEntity *)*itr)->GetLine(2); + a_Line4 = ((cSignEntity *)*itr)->GetLine(3); + return true; + } + } // switch (BlockType) + + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const +{ + if ( + (a_RelX < 0) || (a_RelX >= Width) || + (a_RelY < 0) || (a_RelY >= Height) || + (a_RelZ < 0) || (a_RelZ >= Width) + ) + { + ASSERT(!"GetBlock(x, y, z) out of bounds!"); + return 0; // Clip + } + + return m_BlockTypes[MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)]; +} + + + + + +BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const +{ + if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) + { + ASSERT(!"GetBlock(idx) out of bounds!"); + return 0; + } + + return m_BlockTypes[ a_BlockIdx ]; +} + + + + + +void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ); + a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +{ + int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx); + a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx); + a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx); + a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx); +} + + + + + +cChunk * cChunk::GetNeighborChunk(int a_BlockX, int a_BlockZ) +{ + // Convert coords to relative, then call the relative version: + a_BlockX -= m_PosX * cChunkDef::Width; + a_BlockZ -= m_PosZ * cChunkDef::Width; + return GetRelNeighborChunk(a_BlockX, a_BlockZ); +} + + + + + +cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) +{ + bool ReturnThis = true; + if (a_RelX < 0) + { + if (m_NeighborXM != NULL) + { + cChunk * Candidate = m_NeighborXM->GetRelNeighborChunk(a_RelX + cChunkDef::Width, a_RelZ); + if (Candidate != NULL) + { + return Candidate; + } + } + // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on. + ReturnThis = false; + } + else if (a_RelX >= cChunkDef::Width) + { + if (m_NeighborXP != NULL) + { + cChunk * Candidate = m_NeighborXP->GetRelNeighborChunk(a_RelX - cChunkDef::Width, a_RelZ); + if (Candidate != NULL) + { + return Candidate; + } + } + // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on. + ReturnThis = false; + } + + if (a_RelZ < 0) + { + if (m_NeighborZM != NULL) + { + return m_NeighborZM->GetRelNeighborChunk(a_RelX, a_RelZ + cChunkDef::Width); + // For requests crossing both X and Z, the X-first way has been already tried + } + return NULL; + } + else if (a_RelZ >= cChunkDef::Width) + { + if (m_NeighborZP != NULL) + { + return m_NeighborZP->GetRelNeighborChunk(a_RelX, a_RelZ - cChunkDef::Width); + // For requests crossing both X and Z, the X-first way has been already tried + } + return NULL; + } + + return (ReturnThis ? this : NULL); +} + + + + + +cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const +{ + cChunk * ToReturn = const_cast<cChunk *>(this); + + // The most common case: inside this chunk: + if ( + (a_RelX >= 0) && (a_RelX < Width) && + (a_RelZ >= 0) && (a_RelZ < Width) + ) + { + return ToReturn; + } + + // Request for a different chunk, calculate chunk offset: + int RelX = a_RelX; // Make a local copy of the coords (faster access) + int RelZ = a_RelZ; + while ((RelX >= Width) && (ToReturn != NULL)) + { + RelX -= Width; + ToReturn = ToReturn->m_NeighborXP; + } + while ((RelX < 0) && (ToReturn != NULL)) + { + RelX += Width; + ToReturn = ToReturn->m_NeighborXM; + } + while ((RelZ >= Width) && (ToReturn != NULL)) + { + RelZ -= Width; + ToReturn = ToReturn->m_NeighborZP; + } + while ((RelZ < 0) && (ToReturn != NULL)) + { + RelZ += Width; + ToReturn = ToReturn->m_NeighborZM; + } + if (ToReturn != NULL) + { + a_RelX = RelX; + a_RelZ = RelZ; + return ToReturn; + } + + // The chunk cannot be walked through neighbors, find it through the chunkmap: + int AbsX = a_RelX + m_PosX * Width; + int AbsZ = a_RelZ + m_PosZ * Width; + int DstChunkX, DstChunkZ; + BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ); + ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ); + a_RelX = AbsX - DstChunkX * Width; + a_RelZ = AbsZ - DstChunkZ * Width; + return ToReturn; +} + + + + + +void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + (*itr)->SendAttachEntity(a_Entity, a_Vehicle); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendBlockBreakAnim(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list + cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Entity == NULL) + { + return; + } + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + Entity->SendTo(*(*itr)); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendCollectPickup(a_Pickup, a_Player); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendDestroyEntity(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityHeadLook(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityLook(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityMetadata(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityStatus(a_Entity, a_Status); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityVelocity(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendPlayerAnimation(a_Player, a_Animation); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + a_Entity.SpawnOn(*(*itr)); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + (*itr)->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Entity == NULL) + { + return; + } + Entity->SendTo(a_Client); +} + + + + + +void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ) +{ + a_BlockY = a_RelY; + a_BlockX = m_PosX * Width + a_RelX; + a_BlockZ = m_PosZ * Width + a_RelZ; +} + + + + + +Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ) +{ + return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ); +} + + + + + +NIBBLETYPE cChunk::GetTimeAlteredLight(NIBBLETYPE a_Skylight) const +{ + a_Skylight -= m_World->GetSkyDarkness(); + // Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15 + return (a_Skylight < 16)? a_Skylight : 0; +} + + + + + +#if !C_CHUNK_USE_INLINE +# include "cChunk.inl.h" +#endif + + + + diff --git a/src/Chunk.h b/src/Chunk.h new file mode 100644 index 000000000..895b407a3 --- /dev/null +++ b/src/Chunk.h @@ -0,0 +1,484 @@ + +#pragma once + +#include "Entities/Entity.h" +#include "ChunkDef.h" + +#include "Simulator/FireSimulator.h" +#include "Simulator/SandSimulator.h" +#include "Simulator/RedstoneSimulator.h" + + + + + +#define C_CHUNK_USE_INLINE 1 + +// Do not touch +#if C_CHUNK_USE_INLINE + #define __C_CHUNK_INLINE__ inline +#else + #define __C_CHUNK_INLINE__ +#endif + + + + + +namespace Json +{ + class Value; +}; + + + + + +class cWorld; +class cFurnaceEntity; +class cClientHandle; +class cServer; +class MTRand; +class cPlayer; +class cChunkMap; +class cChestEntity; +class cDispenserEntity; +class cFurnaceEntity; +class cBlockArea; +class cPawn; +class cPickup; +class cChunkDataSerializer; +class cBlockArea; +class cFluidSimulatorData; +class cMobCensus; +class cMobSpawner; + +typedef std::list<cClientHandle *> cClientHandleList; +typedef cItemCallback<cEntity> cEntityCallback; +typedef cItemCallback<cChestEntity> cChestCallback; +typedef cItemCallback<cDispenserEntity> cDispenserCallback; +typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; + + + + +// This class is not to be used directly +// Instead, call actions on cChunkMap (such as cChunkMap::SetBlock() etc.) +class cChunk : + public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef +{ +public: + cChunk( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords + cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks + ); + ~cChunk(); + + bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) + void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight() + void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk + bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved + bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then + bool CanUnload(void); + + bool IsLightValid(void) const {return m_IsLightValid; } + + /* + To save a chunk, the WSSchema must: + 1. Mark the chunk as being saved (MarkSaving() ) + 2. Get the chunk's data using GetAllData() + 3. Mark the chunk as saved (MarkSaved() ) + If anywhere inside this sequence another thread mmodifies the chunk, the chunk will not get marked as saved in MarkSaved() + */ + void MarkSaving(void); // Marks the chunk as being saved. + void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving() + void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid + void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid + + /// Gets all chunk data, calls the a_Callback's methods for each data type + void GetAllData(cChunkDataCallback & a_Callback); + + /// Sets all chunk data + void SetAllData( + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities + ); + + void SetLight( + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + /// Copies m_BlockData into a_BlockTypes, only the block types + void GetBlockTypes(BLOCKTYPE * a_BlockTypes); + + /// Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk! + void WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + + /// Returns true if there is a block entity at the coords specified + bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Sets or resets the internal flag that prevents chunk from being unloaded + void Stay(bool a_Stay = true); + + /// Recence all mobs proximities to players in order to know what to do with them + void CollectMobCensus(cMobCensus& toFill); + + /// Try to Spawn Monsters inside chunk + void SpawnMobs(cMobSpawner& a_MobSpawner); + + void Tick(float a_Dt); + + int GetPosX(void) const { return m_PosX; } + int GetPosY(void) const { return m_PosY; } + int GetPosZ(void) const { return m_PosZ; } + + cWorld * GetWorld(void) const { return m_World; } + + void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); + // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense + void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); } + + /// Queues a block change till the specified world tick + void QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick); + + /// Queues block for ticking (m_ToTickQueue) + void QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ); + + /// Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk + void QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ); + + void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. + BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const; + BLOCKTYPE GetBlock(int a_BlockIdx) const; + void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); + + /** Returns the chunk into which the specified block belongs, by walking the neighbors. + Will return self if appropriate. Returns NULL if not reachable through neighbors. + */ + cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ); + + /** + Returns the chunk into which the relatively-specified block belongs, by walking the neighbors. + Will return self if appropriate. Returns NULL if not reachable through neighbors. + */ + cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ); + + /** + Returns the chunk into which the relatively-specified block belongs. + Also modifies the relative coords from this-relative to return-relative. + Will return self if appropriate. + Will try walking the neighbors first; if that fails, will query the chunkmap + */ + cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const; + + EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } + + void CollectPickupsByPlayer(cPlayer * a_Player); + + /// Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk + bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + int GetHeight( int a_X, int a_Z ); + + void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); + + /// Adds a client to the chunk; returns true if added, false if already there + bool AddClient (cClientHandle* a_Client ); + + void RemoveClient (cClientHandle* a_Client ); + bool HasClient (cClientHandle* a_Client ); + bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise + + void AddEntity(cEntity * a_Entity); + void RemoveEntity(cEntity * a_Entity); + bool HasEntity(int a_EntityID); + + /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. + bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible + + /// Calls the callback for each block entity; returns true if all block entities processed, false if the callback aborted by returning true + bool ForEachBlockEntity(cBlockEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenser(cDispenserCallback & a_Callback); + + /// Calls the callback for each dropper; returns true if all droppers processed, false if the callback aborted by returning true + bool ForEachDropper(cDropperCallback & a_Callback); + + /// Calls the callback for each dropspenser; returns true if all dropspensers processed, false if the callback aborted by returning true + bool ForEachDropSpenser(cDropSpenserCallback & a_Callback); + + /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found + bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found + bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found + bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible + + void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords + + void CalculateLighting(); // Recalculate right now + void CalculateHeightmap(); + + // Broadcast various packets to all clients of this chunk: + // (Please keep these alpha-sorted) + void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); + void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); + void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + + void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + Vector3i PositionToWorldPosition(const Vector3i & a_RelPos) + { + return PositionToWorldPosition(a_RelPos.x, a_RelPos.y, a_RelPos.z); + } + + void PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ); + Vector3i PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ ); + + inline void MarkDirty(void) + { + m_IsDirty = true; + m_IsSaving = false; + } + + /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call + inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ) + { + m_BlockTickX = a_RelX; + m_BlockTickY = a_RelY; + m_BlockTickZ = a_RelZ; + } + + inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetMeta(int a_BlockIdx) const {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } + inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } + inline void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_BlockIdx, a_Meta); } + + inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); } + inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); } + + /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + + /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const; + + /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const; + + /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const; + + /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const; + + /// Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const; + + /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts + void UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ); + + /// Light alterations based on time + NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const; + + + // Simulator data: + cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; } + cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; } + cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } + cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } + cRedstoneSimulatorChunkData & GetRedstoneSimulatorData(void) { return m_RedstoneSimulatorData; } + + cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } + +private: + + friend class cChunkMap; + + struct sSetBlockQueueItem + { + int m_RelX, m_RelY, m_RelZ; + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + Int64 m_Tick; + + sSetBlockQueueItem(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick) : + m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), m_BlockType(a_BlockType), m_BlockMeta(a_BlockMeta), m_Tick(a_Tick) + { + } + } ; + + typedef std::vector<sSetBlockQueueItem> sSetBlockQueueVector; + + + bool m_IsValid; // True if the chunk is loaded / generated + bool m_IsLightValid; // True if the blocklight and skylight are calculated + bool m_IsDirty; // True if the chunk has changed since it was last saved + bool m_IsSaving; // True if the chunk is being saved + bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then + + std::vector<unsigned int> m_ToTickBlocks; + sSetBlockVector m_PendingSendBlocks; ///< Blocks that have changed and need to be sent to all clients + + sSetBlockQueueVector m_SetBlockQueue; ///< Block changes that are queued to a specific tick + + // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers + cClientHandleList m_LoadedByClient; + cClientHandleList m_UnloadQuery; + cEntityList m_Entities; + cBlockEntityList m_BlockEntities; + + /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded + int m_StayCount; + + int m_PosX, m_PosY, m_PosZ; + cWorld * m_World; + cChunkMap * m_ChunkMap; + + // TODO: Make these pointers and don't allocate what isn't needed + BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks]; + NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2]; + NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2]; + NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2]; + + cChunkDef::HeightMap m_HeightMap; + cChunkDef::BiomeMap m_BiomeMap; + + int m_BlockTickX, m_BlockTickY, m_BlockTickZ; + + cChunk * m_NeighborXM; // Neighbor at [X - 1, Z] + cChunk * m_NeighborXP; // Neighbor at [X + 1, Z] + cChunk * m_NeighborZM; // Neighbor at [X, Z - 1] + cChunk * m_NeighborZP; // Neighbor at [X, Z + 1] + + // Per-chunk simulator data: + cFireSimulatorChunkData m_FireSimulatorData; + cFluidSimulatorData * m_WaterSimulatorData; + cFluidSimulatorData * m_LavaSimulatorData; + cSandSimulatorChunkData m_SandSimulatorData; + cRedstoneSimulatorChunkData m_RedstoneSimulatorData; + + + // pick up a random block of this chunk + void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); + void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); + + void RemoveBlockEntity(cBlockEntity * a_BlockEntity); + void AddBlockEntity (cBlockEntity * a_BlockEntity); + + void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); + + /// Creates a block entity for each block that needs a block entity and doesn't have one in the list + void CreateBlockEntities(void); + + /// Wakes up each simulator for its specific blocks; through all the blocks in the chunk + void WakeUpSimulators(void); + + // Makes a copy of the list + cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } + + /// Sends m_PendingSendBlocks to all clients + void BroadcastPendingBlockChanges(void); + + /// Checks the block scheduled for checking in m_ToTickBlocks[] + void CheckBlocks(void); + + /// Ticks several random blocks in the chunk + void TickBlocks(void); + + /// Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes + void ApplyWeatherToTop(void); + + /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) + void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + + /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) + void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); + + /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log) + bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients + void MoveEntityToNewChunk(cEntity * a_Entity); + + /// Processes all blocks that have been scheduled for replacement by the QueueSetBlock() function + void ProcessQueuedSetBlocks(void); +}; + +typedef cChunk * cChunkPtr; + +typedef std::list<cChunkPtr> cChunkPtrList; + + + + + +#if C_CHUNK_USE_INLINE + #include "Chunk.inl.h" +#endif + + + + diff --git a/src/Chunk.inl.h b/src/Chunk.inl.h new file mode 100644 index 000000000..fb9c4dad1 --- /dev/null +++ b/src/Chunk.inl.h @@ -0,0 +1,34 @@ + +#ifndef __C_CHUNK_INL_H__ +#define __C_CHUNK_INL_H__ + +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + + + + + +__C_CHUNK_INLINE__ +void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff) +{ + unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z ); + cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) ); + MarkDirty(); +} + + + + + +#endif + + + + diff --git a/src/ChunkDef.h b/src/ChunkDef.h new file mode 100644 index 000000000..d6630df7e --- /dev/null +++ b/src/ChunkDef.h @@ -0,0 +1,617 @@ + +// ChunkDef.h + +// Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h + + + + + +#pragma once + +#include "Vector3i.h" + + + + + +/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord. +It will help us when the new chunk format comes out and we need to patch everything up for compatibility. +*/ +#define ZERO_CHUNK_Y 0 + +// Used to smoothly convert to new axis ordering. One will be removed when deemed stable. +#define AXIS_ORDER_YZX 1 // Original (1.1-) +#define AXIS_ORDER_XZY 2 // New (1.2+) +#define AXIS_ORDER AXIS_ORDER_XZY + + + + + +// fwd +class cBlockEntity; +class cEntity; +class cClientHandle; +class cBlockEntity; + +typedef std::list<cEntity *> cEntityList; +typedef std::list<cBlockEntity *> cBlockEntityList; + + + + +// tolua_begin + +/// The datatype used by blockdata +typedef unsigned char BLOCKTYPE; + +/// The datatype used by nibbledata (meta, light, skylight) +typedef unsigned char NIBBLETYPE; + +/// The type used by the heightmap +typedef unsigned char HEIGHTTYPE; + +// tolua_end + + + + + + +// tolua_begin +/** Biome IDs +The first batch corresponds to the clientside biomes, used by MineCraft. +BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client +*/ +enum EMCSBiome +{ + biOcean = 0, + biPlains = 1, + biDesert = 2, + biExtremeHills = 3, + biForest = 4, + biTaiga = 5, + biSwampland = 6, + biRiver = 7, + biHell = 8, // same as Nether + biNether = 8, + biSky = 9, // same as biEnd + biEnd = 9, + biFrozenOcean = 10, + biFrozenRiver = 11, + biIcePlains = 12, + biTundra = 12, // same as Ice Plains + biIceMountains = 13, + biMushroomIsland = 14, + biMushroomShore = 15, + biBeach = 16, + biDesertHills = 17, + biForestHills = 18, + biTaigaHills = 19, + biExtremeHillsEdge = 20, + biJungle = 21, + biJungleHills = 22, + + // Release 1.7 biomes: + biJungleEdge = 23, + biDeepOcean = 24, + biStoneBeach = 25, + biColdBeach = 26, + biBirchForest = 27, + biBirchForestHills = 28, + biRoofedForest = 29, + biColdTaiga = 30, + biColdTaigaHills = 31, + biMegaTaiga = 32, + biMegaTaigaHills = 33, + biExtremeHillsPlus = 34, + biSavanna = 35, + biSavannaPlateau = 36, + biMesa = 37, + biMesaPlateauF = 38, + biMesaPlateau = 39, + + // Automatically capture the maximum consecutive biome value into biMaxBiome: + biNumBiomes, // True number of biomes, since they are zero-based + biMaxBiome = biNumBiomes - 1, // The maximum biome value + + // Add this number to the biomes to get the variant + biVariant = 128, + + // Release 1.7 biome variants: + biSunflowerPlains = 129, + biDesertM = 130, + biExtremeHillsM = 131, + biFlowerForest = 132, + biTaigaM = 133, + biSwamplandM = 134, + biIcePlainsSpikes = 140, + biJungleM = 149, + biJungleEdgeM = 151, + biBirchForestM = 155, + biBirchForestHillsM = 156, + biRoofedForestM = 157, + biColdTaigaM = 158, + biMegaSpruceTaiga = 160, + biMegaSpruceTaigaHills = 161, + biExtremeHillsPlusM = 162, + biSavannaM = 163, + biSavannaPlateauM = 164, + biMesaBryce = 165, + biMesaPlateauFM = 166, + biMesaPlateauM = 167, +} ; + +// tolua_end + + + + +/// Constants used throughout the code, useful typedefs and utility functions +class cChunkDef +{ +public: + static const int Width = 16; + static const int Height = 256; + static const int NumBlocks = Width * Height * Width; + static const int BlockDataSize = NumBlocks * 2 + (NumBlocks / 2); // 2.5 * numblocks + + // Offsets to individual components in the joined blockdata array + static const int MetaOffset = NumBlocks; + static const int LightOffset = MetaOffset + NumBlocks / 2; + static const int SkyLightOffset = LightOffset + NumBlocks / 2; + + static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff; + + /// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column + typedef HEIGHTTYPE HeightMap[Width * Width]; + + /** The type used for any biomemap operations and storage inside MCServer, + using MCServer biomes (need not correspond to client representation!) + idx = x + Width * z // Need to verify this with the protocol spec, currently unknown! + */ + typedef EMCSBiome BiomeMap[Width * Width]; + + /// The type used for block type operations and storage, AXIS_ORDER ordering + typedef BLOCKTYPE BlockTypes[NumBlocks]; + + /// The type used for block data in nibble format, AXIS_ORDER ordering + typedef NIBBLETYPE BlockNibbles[NumBlocks / 2]; + + + /// Converts absolute block coords into relative (chunk + block) coords: + inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ ) + { + BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ); + + a_X = a_X - a_ChunkX * Width; + a_Z = a_Z - a_ChunkZ * Width; + } + + + /// Converts absolute block coords to chunk coords: + inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ) + { + a_ChunkX = a_X / Width; + if ((a_X < 0) && (a_X % Width != 0)) + { + a_ChunkX--; + } + a_ChunkZ = a_Z / cChunkDef::Width; + if ((a_Z < 0) && (a_Z % Width != 0)) + { + a_ChunkZ--; + } + } + + + inline static unsigned int MakeIndex(int x, int y, int z ) + { + if ( + (x < Width) && (x > -1) && + (y < Height) && (y > -1) && + (z < Width) && (z > -1) + ) + { + return MakeIndexNoCheck(x, y, z); + } + ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!"); + return INDEX_OUT_OF_RANGE; + } + + + inline static unsigned int MakeIndexNoCheck(int x, int y, int z) + { + #if AXIS_ORDER == AXIS_ORDER_XZY + // For some reason, NOT using the Horner schema is faster. Weird. + return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY + #elif AXIS_ORDER == AXIS_ORDER_YZX + return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX + #endif + } + + + inline static Vector3i IndexToCoordinate( unsigned int index ) + { + #if AXIS_ORDER == AXIS_ORDER_XZY + return Vector3i( // 1.2 + index % cChunkDef::Width, // X + index / (cChunkDef::Width * cChunkDef::Width), // Y + (index / cChunkDef::Width) % cChunkDef::Width // Z + ); + #elif AXIS_ORDER == AXIS_ORDER_YZX + return Vector3i( // 1.1 + index / (cChunkDef::Height * cChunkDef::Width), // X + index % cChunkDef::Height, // Y + (index / cChunkDef::Height) % cChunkDef::Width // Z + ); + #endif + } + + + inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type) + { + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Y >= 0) && (a_Y < Height)); + ASSERT((a_Z >= 0) && (a_Z < Width)); + a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type; + } + + + inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type) + { + ASSERT((a_Index >= 0) && (a_Index <= NumBlocks)); + a_BlockTypes[a_Index] = a_Type; + } + + + inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z) + { + ASSERT((a_X >= 0) && (a_X < Width)); + ASSERT((a_Y >= 0) && (a_Y < Height)); + ASSERT((a_Z >= 0) && (a_Z < Width)); + return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)]; + } + + + inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx) + { + ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks)); + return a_BlockTypes[a_Idx]; + } + + + inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + return a_HeightMap[a_X + Width * a_Z]; + } + + + inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + a_HeightMap[a_X + Width * a_Z] = a_Height; + } + + + inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + return a_BiomeMap[a_X + Width * a_Z]; + } + + + inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) + { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); + a_BiomeMap[a_X + Width * a_Z] = a_Biome; + } + + + static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx) + { + if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks)) + { + return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f; + } + ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!"); + return 0; + } + + + static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z) + { + if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) + { + int Index = MakeIndexNoCheck(x, y, z); + return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f; + } + ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); + return 0; + } + + + static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble) + { + if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) + { + ASSERT(!"cChunkDef::SetNibble(): index out of range!"); + return; + } + a_Buffer[a_BlockIdx / 2] = ( + (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble + ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set + ); + } + + + static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble) + { + if ( + (x >= Width) || (x < 0) || + (y >= Height) || (y < 0) || + (z >= Width) || (z < 0) + ) + { + ASSERT(!"cChunkDef::SetNibble(): index out of range!"); + return; + } + + int Index = MakeIndexNoCheck(x, y, z); + a_Buffer[Index / 2] = ( + (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble + ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set + ); + } + + + inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos ) + { + return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); + } + + + inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value ) + { + SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value ); + } + +} ; + + + + + +/** Interface class used for getting data out of a chunk using the GetAllData() function. +Implementation must use the pointers immediately and NOT store any of them for later use +The virtual methods are called in the same order as they're declared here. +*/ +class cChunkDataCallback abstract +{ +public: + /** Called before any other callbacks to inform of the current coords + (only in processes where multiple chunks can be processed, such as cWorld::ForEachChunkInRect()). + If false is returned, the chunk is skipped. + */ + virtual bool Coords(int a_ChunkX, int a_ChunkZ) { UNUSED(a_ChunkX); UNUSED(a_ChunkZ); return true; }; + + /// Called once to provide heightmap data + virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {UNUSED(a_HeightMap); }; + + /// Called once to provide biome data + virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {UNUSED(a_BiomeMap); }; + + /// Called once to export block types + virtual void BlockTypes (const BLOCKTYPE * a_Type) {UNUSED(a_Type); }; + + /// Called once to export block meta + virtual void BlockMeta (const NIBBLETYPE * a_Meta) {UNUSED(a_Meta); }; + + /// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next (if true) + virtual bool LightIsValid(bool a_IsLightValid) {UNUSED(a_IsLightValid); return true; }; + + /// Called once to export block light + virtual void BlockLight (const NIBBLETYPE * a_BlockLight) {UNUSED(a_BlockLight); }; + + /// Called once to export sky light + virtual void BlockSkyLight(const NIBBLETYPE * a_SkyLight) {UNUSED(a_SkyLight); }; + + /// Called for each entity in the chunk + virtual void Entity(cEntity * a_Entity) {UNUSED(a_Entity); }; + + /// Called for each blockentity in the chunk + virtual void BlockEntity(cBlockEntity * a_Entity) {UNUSED(a_Entity); }; +} ; + + + + + +/** A simple implementation of the cChunkDataCallback interface that collects all block data into a single buffer +*/ +class cChunkDataCollector : + public cChunkDataCallback +{ +public: + + // Must be unsigned char instead of BLOCKTYPE or NIBBLETYPE, because it houses both. + unsigned char m_BlockData[cChunkDef::BlockDataSize]; + +protected: + + virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override + { + memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes)); + } + + + virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override + { + memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2); + } + + + virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override + { + memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2); + } + + + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override + { + memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2); + } +} ; + + + + + +/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers +*/ +class cChunkDataSeparateCollector : + public cChunkDataCallback +{ +public: + + cChunkDef::BlockTypes m_BlockTypes; + cChunkDef::BlockNibbles m_BlockMetas; + cChunkDef::BlockNibbles m_BlockLight; + cChunkDef::BlockNibbles m_BlockSkyLight; + +protected: + + virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override + { + memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); + } + + + virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override + { + memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas)); + } + + + virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override + { + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + } + + + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override + { + memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); + } +} ; + + + + + +/** Interface class used for comparing clients of two chunks. +Used primarily for entity moving while both chunks are locked. +*/ +class cClientDiffCallback +{ +public: + /// Called for clients that are in Chunk1 and not in Chunk2, + virtual void Removed(cClientHandle * a_Client) = 0; + + /// Called for clients that are in Chunk2 and not in Chunk1. + virtual void Added(cClientHandle * a_Client) = 0; +} ; + + + + + +struct sSetBlock +{ + int x, y, z; + int ChunkX, ChunkZ; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + + sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); // absolute block position + sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + x(a_X), y(a_Y), z(a_Z), + ChunkX(a_ChunkX), ChunkZ(a_ChunkZ), + BlockType(a_BlockType), + BlockMeta(a_BlockMeta) + {} +}; + +typedef std::list<sSetBlock> sSetBlockList; +typedef std::vector<sSetBlock> sSetBlockVector; + + + + + +class cChunkCoords +{ +public: + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + + cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {} + + bool operator == (const cChunkCoords & a_Other) const + { + return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ)); + } +} ; + +typedef std::list<cChunkCoords> cChunkCoordsList; + + + + + +/// Interface class used as a callback for operations that involve chunk coords +class cChunkCoordCallback +{ +public: + virtual void Call(int a_ChunkX, int a_ChunkZ) = 0; +} ; + + + + + +/// Generic template that can store any kind of data together with a triplet of 3 coords: +template <typename X> class cCoordWithData +{ +public: + int x; + int y; + int z; + X Data; + + cCoordWithData(int a_X, int a_Y, int a_Z) : + x(a_X), y(a_Y), z(a_Z) + { + } + + cCoordWithData(int a_X, int a_Y, int a_Z, const X & a_Data) : + x(a_X), y(a_Y), z(a_Z), Data(a_Data) + { + } +} ; + +// Illegal in C++03: typedef std::list< cCoordWithData<X> > cCoordWithDataList<X>; +typedef cCoordWithData<int> cCoordWithInt; +typedef std::list<cCoordWithInt> cCoordWithIntList; +typedef std::vector<cCoordWithInt> cCoordWithIntVector; + + + + diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp new file mode 100644 index 000000000..5937ee2e4 --- /dev/null +++ b/src/ChunkMap.cpp @@ -0,0 +1,2701 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChunkMap.h" +#include "World.h" +#include "Root.h" +#include "Entities/Player.h" +#include "Item.h" +#include "Entities/Pickup.h" +#include "Chunk.h" +#include "Generating/Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination +#include "BlockArea.h" +#include "PluginManager.h" +#include "Entities/TNTEntity.h" +#include "Blocks/BlockHandler.h" +#include "MobCensus.h" +#include "MobSpawner.h" + +#ifndef _WIN32 + #include <cstdlib> // abs +#endif + +#include "lib/zlib/zlib.h" +#include "lib/jsoncpp/include/json/json.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap: + +cChunkMap::cChunkMap(cWorld * a_World ) + : m_World( a_World ) +{ +} + + + + + +cChunkMap::~cChunkMap() +{ + cCSLock Lock(m_CSLayers); + while (!m_Layers.empty()) + { + delete m_Layers.back(); + m_Layers.pop_back(); // Must pop, because further chunk deletions query the chunkmap for entities and that would touch deleted data + } +} + + + + + +void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) +{ + cCSLock Lock(m_CSLayers); + m_Layers.remove(a_Layer); +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) + { + return *itr; + } + } + + // Not found, create new: + cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); + if (Layer == NULL) + { + LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); + return NULL; + } + m_Layers.push_back(Layer); + return Layer; +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::FindLayerForChunk(int a_ChunkX, int a_ChunkZ) +{ + const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE); + const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE); + return FindLayer(LayerX, LayerZ); +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::FindLayer(int a_LayerX, int a_LayerZ) +{ + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) + { + return *itr; + } + } // for itr - m_Layers[] + + // Not found + return NULL; +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ) +{ + const int LayerX = FAST_FLOOR_DIV(a_ChunkX, LAYER_SIZE); + const int LayerZ = FAST_FLOOR_DIV(a_ChunkZ, LAYER_SIZE); + return GetLayer(LayerX, LayerZ); +} + + + + + +cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return NULL; + } + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true); + } + return Chunk; +} + + + + + +cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return NULL; + } + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false); + } + + return Chunk; +} + + + + + +cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockType = Chunk->GetBlock(Index); + a_BlockMeta = Chunk->GetMeta(Index); + return true; +} + + + + + +bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockType = Chunk->GetBlock(Index); + return true; +} + + + + + +bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockMeta = Chunk->GetMeta(Index); + return true; +} + + + + + +bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_CSLayers.IsLockedByCurrentThread()); + + cChunkLayer * Layer = FindLayerForChunk(a_ChunkX, a_ChunkZ); + if (Layer == NULL) + { + return NULL; + } + return Layer->FindChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int x, y, z, ChunkX, ChunkZ; + x = a_BlockX; + y = a_BlockY; + z = a_BlockZ; + cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); +} + + + + + +void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastBlockBreakAnimation(a_entityID, a_blockX, a_blockY, a_blockZ, a_stage, a_Exclude); +} + + + + + +void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastChunkData(a_Serializer, a_Exclude); +} + + + + + +void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), ZERO_CHUNK_Y, a_Pickup.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); +} + + + + + +void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastDestroyEntity(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityHeadLook(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityLook(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityMetadata(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityVelocity(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Player.GetChunkX(), ZERO_CHUNK_Y, a_Player.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSpawnEntity(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); +} + + + + + +void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // a_Player rclked block entity at the coords specified, handle it + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return false; + } + return a_Callback.Item(Chunk); +} + + + + + +void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk); +} + + + + + +/// Wakes up the simulators for the specified area of blocks +void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) +{ + cSimulatorManager * SimMgr = m_World->GetSimulatorManager(); + int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ; + cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ); + cChunkDef::BlockToChunk(a_MaxBlockX, a_MaxBlockZ, MaxChunkX, MaxChunkZ); + for (int z = MinChunkZ; z <= MaxChunkZ; z++) + { + int MinZ = std::max(a_MinBlockZ, z * cChunkDef::Width); + int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1); + for (int x = MinChunkX; x <= MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoGen(x, 0, z); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + int MinX = std::max(a_MinBlockX, x * cChunkDef::Width); + int MaxX = std::min(a_MaxBlockX, x * cChunkDef::Width + cChunkDef::Width - 1); + for (int BlockY = a_MinBlockY; BlockY <= a_MaxBlockY; BlockY++) + { + for (int BlockZ = MinZ; BlockZ <= MaxZ; BlockZ++) + { + for (int BlockX = MinX; BlockX <= MaxX; BlockX++) + { + SimMgr->WakeUp(BlockX, BlockY, BlockZ, Chunk); + } // for BlockX + } // for BlockZ + } // for BlockY + } // for x - chunks + } // for z = chunks +} + + + + + +void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkDirty(); +} + + + + + +void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkSaving(); +} + + + + + +void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkSaved(); +} + + + + + +void cChunkMap::SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty +) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities); + + if (a_MarkDirty) + { + Chunk->MarkDirty(); + } + + // Notify plugins of the chunk becoming available + cPluginManager::Get()->CallHookChunkAvailable(m_World, a_ChunkX, a_ChunkZ); +} + + + + + +void cChunkMap::ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->SetLight(a_BlockLight, a_SkyLight); + Chunk->MarkDirty(); +} + + + + + +bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetAllData(a_Callback); + return true; +} + + + + + +bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetBlockTypes(a_BlockTypes); + return true; +} + + + + + +bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + return (Chunk != NULL) && Chunk->IsValid(); +} + + + + + +bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + return (Chunk != NULL) && Chunk->HasAnyClients(); +} + + + + + +int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) +{ + while (true) + { + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return 0; + } + + if (Chunk->IsValid()) + { + return Chunk->GetHeight(a_BlockX, a_BlockZ); + } + + // The chunk is not valid, wait for it to become valid: + cCSUnlock Unlock(Lock); + m_evtChunkValid.Wait(); + } // while (true) +} + + + + + +bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) +{ + // Returns false if chunk not loaded / generated + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + a_Height = Chunk->GetHeight(a_BlockX, a_BlockZ); + return true; +} + + + + + +void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) +{ + sSetBlockList Failed; + + // Process all items from a_BlockList, either successfully or by placing into Failed + while (!a_BlockList.empty()) + { + int ChunkX = a_BlockList.front().ChunkX; + int ChunkZ = a_BlockList.front().ChunkZ; + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) + { + if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + itr = a_BlockList.erase(itr); + } + else + { + ++itr; + } + } // for itr - a_BlockList[] + } + else + { + // The chunk is not valid, move all blocks within this chunk to Failed + for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) + { + if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + Failed.push_back(*itr); + itr = a_BlockList.erase(itr); + } + else + { + ++itr; + } + } // for itr - a_BlockList[] + } + } + + // Return the failed: + std::swap(Failed, a_BlockList); +} + + + + + +void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) +{ + int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway + int BlockY = (int)(a_Player->GetPosY()); + int BlockZ = (int)(a_Player->GetPosZ()); + int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); + int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); + + // We suppose that each player keeps their chunks in memory, therefore it makes little sense to try to re-load or even generate them. + // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment + + cCSLock Lock(m_CSLayers); + GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); + + // Check the neighboring chunks as well: + GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); +} + + + + + +BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ); + } + return 0; +} + + + + + +void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + // a_BlockXYZ now contains relative coords! + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); + Chunk->MarkDirty(); + Chunk->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, NULL); + } +} + + + + + +void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); + m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk); + } +} + + + + + +void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->QueueSetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_Tick); + } +} + + + + + +bool cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta); + return true; + } + return false; +} + + + + + +bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); + return true; + } + return false; +} + + + + + +void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) +{ + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + } + } +} + + + + + +void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) +{ + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) + { + CASE_TREE_OVERWRITTEN_BLOCKS: + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + break; + } + case E_BLOCK_LEAVES: + { + if (itr->BlockType == E_BLOCK_LOG) + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + } + break; + } + } + } // for itr - a_Blocks[] +} + + + + + +EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + return Chunk->GetBiomeAt(X, Z); + } + else + { + return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ); + } +} + + + + + +bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) +{ + bool res = true; + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + if (!a_ContinueOnFailure) + { + return false; + } + res = false; + continue; + } + int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z); + itr->BlockType = Chunk->GetBlock(idx); + itr->BlockMeta = Chunk->GetMeta(idx); + } + return res; +} + + + + + +bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) +{ + int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; + + cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ ); + + { + cCSLock Lock(m_CSLayers); + cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((DestChunk == NULL) || !DestChunk->IsValid()) + { + return false; + } + + DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); + m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk); + } + + return true; +} + + + + + +void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk->IsValid()) + { + Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); + } +} + + + + + +void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, ZERO_CHUNK_Y, a_ChunkZ1); + if (Chunk1 == NULL) + { + return; + } + cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, ZERO_CHUNK_Y, a_ChunkZ2); + if (Chunk2 == NULL) + { + return; + } + + CompareChunkClients(Chunk1, Chunk2, a_Callback); +} + + + + + +void cChunkMap::CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback) +{ + cClientHandleList Clients1(a_Chunk1->GetAllClients()); + cClientHandleList Clients2(a_Chunk2->GetAllClients()); + + // Find "removed" clients: + for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) + { + bool Found = false; + for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) + { + if (*itr1 == *itr2) + { + Found = true; + break; + } + } // for itr2 - Clients2[] + if (!Found) + { + a_Callback.Removed(*itr1); + } + } // for itr1 - Clients1[] + + // Find "added" clients: + for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) + { + bool Found = false; + for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) + { + if (*itr1 == *itr2) + { + Found = true; + break; + } + } // for itr1 - Clients1[] + if (!Found) + { + a_Callback.Added(*itr2); + } + } // for itr2 - Clients2[] +} + + + + + +bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return false; + } + return Chunk->AddClient(a_Client); +} + + + + + +void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->RemoveClient(a_Client); +} + + + + + +void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->RemoveClient(a_Client); + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::AddEntity(cEntity * a_Entity) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.", + a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID() + ); + return; + } + Chunk->AddEntity(a_Entity); +} + + + + + +bool cChunkMap::HasEntity(int a_UniqueID) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if ((*itr)->HasEntity(a_UniqueID)) + { + return true; + } + } + return false; +} + + + + + +void cChunkMap::RemoveEntity(cEntity * a_Entity) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return; + } + Chunk->RemoveEntity(a_Entity); +} + + + + + +bool cChunkMap::ForEachEntity(cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (!(*itr)->ForEachEntity(a_Callback)) + { + return false; + } + } + return true; +} + + + + + +bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachEntity(a_Callback); +} + + + + + +void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected) +{ + // Don't explode if outside of Y range (prevents the following test running into unallocated memory): + if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height - 1)) + { + return; + } + + // Don't explode if the explosion center is inside a liquid block: + switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ))) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + return; + } + } + + cBlockArea area; + int bx = (int)floor(a_BlockX); + int by = (int)floor(a_BlockY); + int bz = (int)floor(a_BlockZ); + int ExplosionSizeInt = (int) ceil(a_ExplosionSize); + int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt; + a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt); + int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0); + int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1); + area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt)); + for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++) + { + for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++) + { + if ((by + y >= cChunkDef::Height) || (by + y < 0)) + { + // Outside of the world + continue; + } + for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++) + { + if ((x * x + y * y + z * z) > ExplosionSizeSq) + { + // Too far away + continue; + } + + BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z); + switch (Block) + { + case E_BLOCK_TNT: + { + // Activate the TNT, with a random fuse between 10 to 30 game ticks + double FuseTime = (double)(10 + m_World->GetTickRandomNumber(20)) / 20; + m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime); + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); + a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); + break; + } + case E_BLOCK_OBSIDIAN: + case E_BLOCK_BEDROCK: + case E_BLOCK_WATER: + case E_BLOCK_LAVA: + { + // These blocks are not affected by explosions + break; + } + + case E_BLOCK_STATIONARY_WATER: + { + // Turn into simulated water: + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER); + break; + } + + case E_BLOCK_STATIONARY_LAVA: + { + // Turn into simulated lava: + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA); + break; + } + + case E_BLOCK_AIR: + { + // No pickups for air + break; + } + + default: + { + if (m_World->GetTickRandomNumber(10) == 5) + { + cItems Drops; + cBlockHandler * Handler = BlockHandler(Block); + + Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); + m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z); + } + area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR); + a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); + } + } // switch (BlockType) + } // for z + } // for y + } // for x + area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt); + + // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391): + WakeUpSimulatorsInArea( + bx - ExplosionSizeInt, bx + ExplosionSizeInt + 1, + MinY, MaxY, + bz - ExplosionSizeInt, bz + ExplosionSizeInt + 1 + ); +} + + + + + +bool cChunkMap::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + bool res = false; + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if ((*itr)->DoWithEntityByID(a_UniqueID, a_Callback, res)) + { + return res; + } + } + return false; +} + + + + + +bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachBlockEntity(a_Callback); +} + + + + + +bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachChest(a_Callback); +} + + + + + +bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDispenser(a_Callback); +} + + + + + +bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDropper(a_Callback); +} + + + + + +bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDropSpenser(a_Callback); +} + + + + + +bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachFurnace(a_Callback); +} + + + + + +bool cChunkMap::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) +bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + // Internal error + return false; + } + if (Chunk->IsValid()) + { + // Already loaded + return true; + } + if (Chunk->HasLoadFailed()) + { + // Already tried loading and it failed + return false; + } + } + return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() +void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks) +{ + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) + { + LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + } // for itr - a_Chunks[] +} + + + + + +void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->MarkLoadFailed(); +} + + + + + +bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + return Chunk->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) +{ + cCSLock Lock(m_CSLayers); + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) + { + cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + if (Chunk == NULL) + { + continue; + } + Chunk->Stay(a_Stay); + } +} + + + + + +void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + // Not present + return; + } + Chunk->MarkRegenerating(); +} + + + + + +bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + // Not present + return false; + } + return Chunk->IsLightValid(); +} + + + + + +bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) +{ + bool Result = true; + cCSLock Lock(m_CSLayers); + for (int z = a_MinChunkZ; z <= a_MaxChunkZ; z++) + { + for (int x = a_MinChunkX; x <= a_MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + if ((Chunk == NULL) || (!Chunk->IsValid())) + { + // Not present / not valid + Result = false; + continue; + } + if (!a_Callback.Coords(x, z)) + { + continue; + } + Chunk->GetAllData(a_Callback); + } + } + return Result; +} + + + + + +bool cChunkMap::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + // Convert block coords to chunks coords: + int MinChunkX, MaxChunkX; + int MinChunkZ, MaxChunkZ; + int MinBlockX = a_MinBlockX; + int MinBlockY = a_MinBlockY; + int MinBlockZ = a_MinBlockZ; + int MaxBlockX = a_MinBlockX + a_Area.GetSizeX(); + int MaxBlockY = a_MinBlockY + a_Area.GetSizeY(); + int MaxBlockZ = a_MinBlockZ + a_Area.GetSizeZ(); + cChunkDef::AbsoluteToRelative(MinBlockX, MinBlockY, MinBlockZ, MinChunkX, MinChunkZ); + cChunkDef::AbsoluteToRelative(MaxBlockX, MaxBlockY, MaxBlockZ, MaxChunkX, MaxChunkZ); + + // Iterate over chunks, write data into each: + bool Result = true; + cCSLock Lock(m_CSLayers); + for (int z = MinChunkZ; z <= MaxChunkZ; z++) + { + for (int x = MinChunkX; x <= MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + if ((Chunk == NULL) || (!Chunk->IsValid())) + { + // Not present / not valid + Result = false; + continue; + } + Chunk->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); + } // for x + } // for z + return Result; +} + + + + + +void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) +{ + a_NumChunksValid = 0; + a_NumChunksDirty = 0; + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + int NumValid = 0, NumDirty = 0; + (*itr)->GetChunkStats(NumValid, NumDirty); + a_NumChunksValid += NumValid; + a_NumChunksDirty += NumDirty; + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); + } +} + + + + + +void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + } +} + + + + + +void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + } +} + + + + + +void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + +void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->CollectMobCensus(a_ToFill); + } // for itr - m_Layers +} + + + + + + +void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->SpawnMobs(a_MobSpawner); + } // for itr - m_Layers +} + + + + + +void cChunkMap::Tick(float a_Dt) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->Tick(a_Dt); + } // for itr - m_Layers +} + + + + + +void cChunkMap::UnloadUnusedChunks() +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->UnloadUnusedChunks(); + } // for itr - m_Layers +} + + + + + +void cChunkMap::SaveAllChunks(void) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->Save(); + } // for itr - m_Layers[] +} + + + + + +int cChunkMap::GetNumChunks(void) +{ + cCSLock Lock(m_CSLayers); + int NumChunks = 0; + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + NumChunks += (*itr)->GetNumChunksLoaded(); + } + return NumChunks; +} + + + + + +void cChunkMap::ChunkValidated(void) +{ + m_evtChunkValid.Set(); +} + + + + + +void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + // a_BlockXYZ now contains relative coords! + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap::cChunkLayer: + +cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) + : m_LayerX( a_LayerX ) + , m_LayerZ( a_LayerZ ) + , m_Parent( a_Parent ) + , m_NumChunksLoaded( 0 ) +{ + memset(m_Chunks, 0, sizeof(m_Chunks)); +} + + + + + +cChunkMap::cChunkLayer::~cChunkLayer() +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) + { + delete m_Chunks[i]; + m_Chunks[i] = NULL; // // Must zero out, because further chunk deletions query the chunkmap for entities and that would touch deleted data + } // for i - m_Chunks[] +} + + + + + +cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + + const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; + const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; + + if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) + { + ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); + return NULL; + } + + int Index = LocalX + LocalZ * LAYER_SIZE; + if (m_Chunks[Index] == NULL) + { + cChunk * neixm = (LocalX > 0) ? m_Chunks[Index - 1] : m_Parent->FindChunk(a_ChunkX - 1, a_ChunkZ); + cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ); + cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1); + cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1); + m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp); + } + return m_Chunks[Index]; +} + + + + + +cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) +{ + const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; + const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; + + if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) + { + ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); + return NULL; + } + + int Index = LocalX + LocalZ * LAYER_SIZE; + return m_Chunks[Index]; +} + + + + +void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client + // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn + // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->CollectMobCensus(a_ToFill); + } + } // for i - m_Chunks[] +} + + + + + + +void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We only spawn close to players + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->SpawnMobs(a_MobSpawner); + } + } // for i - m_Chunks[] +} + + + +void cChunkMap::cChunkLayer::Tick(float a_Dt) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // Only tick chunks that are valid and have clients: + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->Tick(a_Dt); + } + } // for i - m_Chunks[] +} + + + + + +void cChunkMap::cChunkLayer::RemoveClient(cClientHandle * a_Client) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if (m_Chunks[i] != NULL) + { + m_Chunks[i]->RemoveClient(a_Client); + } + } // for i - m_Chunks[] +} + + + + + +bool cChunkMap::cChunkLayer::ForEachEntity(cEntityCallback & a_Callback) +{ + // Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + if (!m_Chunks[i]->ForEachEntity(a_Callback)) + { + return false; + } + } + } + return true; +} + + + + + +bool cChunkMap::cChunkLayer::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn) +{ + // Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + if (m_Chunks[i]->DoWithEntityByID(a_EntityID, a_Callback, a_CallbackReturn)) + { + return true; + } + } + } + return false; +} + + + + + +bool cChunkMap::cChunkLayer::HasEntity(int a_EntityID) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + if (m_Chunks[i]->HasEntity(a_EntityID)) + { + return true; + } + } + } + return false; +} + + + + + +int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const +{ + int NumChunks = 0; + for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) + { + if (m_Chunks[i] != NULL) + { + NumChunks++; + } + } // for i - m_Chunks[] + return NumChunks; +} + + + + + +void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const +{ + int NumValid = 0; + int NumDirty = 0; + for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) + { + if (m_Chunks[i] == NULL) + { + continue; + } + NumValid++; + if (m_Chunks[i]->IsDirty()) + { + NumDirty++; + } + } // for i - m_Chunks[] + a_NumChunksValid = NumValid; + a_NumChunksDirty = NumDirty; +} + + + + + +void cChunkMap::cChunkLayer::Save(void) +{ + cWorld * World = m_Parent->GetWorld(); + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) + { + World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); + } + } // for i - m_Chunks[] +} + + + + + +void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ( + (m_Chunks[i] != NULL) && // Is valid + (m_Chunks[i]->CanUnload()) && // Can unload + !cPluginManager::Get()->CallHookChunkUnloading(m_Parent->GetWorld(), m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosZ()) // Plugins agree + ) + { + // The cChunk destructor calls our GetChunk() while removing its entities + // so we still need to be able to return the chunk. Therefore we first delete, then NULLify + // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355 + delete m_Chunks[i]; + m_Chunks[i] = NULL; + } + } // for i - m_Chunks[] +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkStay: + +cChunkStay::cChunkStay(cWorld * a_World) : + m_World(a_World), + m_IsEnabled(false) +{ +} + + + + + +cChunkStay::~cChunkStay() +{ + Clear(); +} + + + + + +void cChunkStay::Clear(void) +{ + if (m_IsEnabled) + { + Disable(); + } + m_Chunks.clear(); +} + + + + + +void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + ASSERT(!m_IsEnabled); + + for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Already present + return; + } + } // for itr - Chunks[] + m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); +} + + + + + +void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + ASSERT(!m_IsEnabled); + + for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Found, un-"stay" + m_Chunks.erase(itr); + return; + } + } // for itr - m_Chunks[] +} + + + + + +void cChunkStay::Enable(void) +{ + ASSERT(!m_IsEnabled); + + m_World->ChunksStay(*this, true); + m_IsEnabled = true; +} + + + + + +void cChunkStay::Load(void) +{ + for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + } // for itr - m_Chunks[] +} + + + + + +void cChunkStay::Disable(void) +{ + ASSERT(m_IsEnabled); + + m_World->ChunksStay(*this, false); + m_IsEnabled = false; +} + + + + diff --git a/src/ChunkMap.h b/src/ChunkMap.h new file mode 100644 index 000000000..2a1d78ff8 --- /dev/null +++ b/src/ChunkMap.h @@ -0,0 +1,439 @@ + +// cChunkMap.h + +// Interfaces to the cChunkMap class representing the chunk storage for a single world + +#pragma once + +#include "ChunkDef.h" + + + + + +class cWorld; +class cItem; +class MTRand; +class cChunkStay; +class cChunk; +class cPlayer; +class cChestEntity; +class cDispenserEntity; +class cDropperEntity; +class cDropSpenserEntity; +class cFurnaceEntity; +class cPawn; +class cPickup; +class cChunkDataSerializer; +class cBlockArea; +class cMobCensus; +class cMobSpawner; + +typedef std::list<cClientHandle *> cClientHandleList; +typedef cChunk * cChunkPtr; +typedef cItemCallback<cEntity> cEntityCallback; +typedef cItemCallback<cBlockEntity> cBlockEntityCallback; +typedef cItemCallback<cChestEntity> cChestCallback; +typedef cItemCallback<cDispenserEntity> cDispenserCallback; +typedef cItemCallback<cDropperEntity> cDropperCallback; +typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback; +typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; +typedef cItemCallback<cChunk> cChunkCallback; + + + + + +class cChunkMap +{ +public: + + static const int LAYER_SIZE = 32; + + cChunkMap(cWorld* a_World ); + ~cChunkMap(); + + // Broadcast respective packets to all clients of the chunk where the event is taking place + // (Please keep these alpha-sorted) + void BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle); + void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); + void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); + void BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + + /// Sends the block entity, if it is at the coords specified, to a_Client + void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + /// a_Player rclked block entity at the coords specified, handle it + void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); + + /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback + bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); + + /// Wakes up simulators for the specified block + void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Wakes up the simulators for the specified area of blocks + void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); + + void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); + void MarkChunkSaving (int a_ChunkX, int a_ChunkZ); + void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); + + /** Sets the chunk data as either loaded from the storage or generated. + a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. + a_BiomeMap is optional, if not present, biomes will be calculated by the generator + a_HeightMap is optional, if not present, will be calculated. + If a_MarkDirty is set, the chunk is set as dirty (used after generating) + */ + void SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty + ); + + void ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback); + + /// Copies the chunk's blocktypes into a_Blocks; returns true if successful + bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks); + + bool IsChunkValid (int a_ChunkX, int a_ChunkZ); + bool HasChunkAnyClients (int a_ChunkX, int a_ChunkZ); + int GetHeight (int a_BlockX, int a_BlockZ); // Waits for the chunk to get loaded / generated + bool TryGetHeight (int a_BlockX, int a_BlockZ, int & a_Height); // Returns false if chunk not loaded / generated + void FastSetBlocks (sSetBlockList & a_BlockList); + void CollectPickupsByPlayer(cPlayer * a_Player); + + BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockMeta); + void SetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta); + void QueueSetBlock (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick); + bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); + + /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType + void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); + + /// Special function used for growing trees, replaces only blocks that tree may overwrite + void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); + + EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ); + + /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. + bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); + + bool DigBlock (int a_X, int a_Y, int a_Z); + void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player); + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback); + + /// Adds client to a chunk, if not already present; returns true if added, false if present + bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from the chunk + void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from all chunks it is present in + void RemoveClientFromChunks(cClientHandle * a_Client); + + /// Adds the entity to its appropriate chunk, takes ownership of the entity pointer + void AddEntity(cEntity * a_Entity); + + /// Returns true if the entity with specified ID is present in the chunks + bool HasEntity(int a_EntityID); + + /// Removes the entity from its appropriate chunk + void RemoveEntity(cEntity * a_Entity); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible + + /// Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates + void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected); + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. + bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true + bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); + + /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true + bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback); + + /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true + bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback); + + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found + bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found + bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found + bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible + + /// Touches the chunk, causing it to be loaded or generated + void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() + void LoadChunks(const cChunkCoordsList & a_Chunks); + + /// Marks the chunk as failed-to-load + void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Sets the sign text. Returns true if sign text changed. + bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay! + void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); + + /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() ) + void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ); + + bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); + + /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully + bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); + + /// Writes the block area into the specified coords. Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead. + bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + + /// Returns the number of valid chunks and the number of dirty chunks + void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); + + /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player + void CollectMobCensus(cMobCensus& a_ToFill); + + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + + void Tick(float a_Dt); + + void UnloadUnusedChunks(void); + void SaveAllChunks(void); + + cWorld * GetWorld(void) { return m_World; } + + int GetNumChunks(void); + + void ChunkValidated(void); // Called by chunks that have become valid + + /// Queues the specified block for ticking (block update) + void QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! + cCriticalSection & GetCS(void) { return m_CSLayers; } + +private: + + friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock() + + class cChunkLayer + { + public: + cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); + ~cChunkLayer(); + + /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); + + /// Returns the specified chunk, or NULL if not created yet + cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); + + int GetX(void) const {return m_LayerX; } + int GetZ(void) const {return m_LayerZ; } + + int GetNumChunksLoaded(void) const ; + + void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const; + + void Save(void); + void UnloadUnusedChunks(void); + + /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player + void CollectMobCensus(cMobCensus& a_ToFill); + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + + void Tick(float a_Dt); + + void RemoveClient(cClientHandle * a_Client); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. + bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn); // Lua-accessible + + /// Returns true if there is an entity with the specified ID within this layer's chunks + bool HasEntity(int a_EntityID); + + protected: + + cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE]; + int m_LayerX; + int m_LayerZ; + cChunkMap * m_Parent; + int m_NumChunksLoaded; + }; + + typedef std::list<cChunkLayer *> cChunkLayerList; + + /// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked. + cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ); + + /// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked. + cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ); + + /// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found. + cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ); + + /// Returns the specified cChunkLayer object; creates it if not found. + cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ); + + void RemoveLayer(cChunkLayer * a_Layer); + + cCriticalSection m_CSLayers; + cChunkLayerList m_Layers; + cEvent m_evtChunkValid; // Set whenever any chunk becomes valid, via ChunkValidated() + + cWorld * m_World; + + cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid + cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate + cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate + + /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Gets a block type in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType); + + /// Gets a block meta in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta); + + /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap. + cChunk * FindChunk(int a_ChunkX, int a_ChunkZ); +}; + + + + + +/** Makes chunks stay loaded until this object is cleared or destroyed +Works by setting internal flags in the cChunk that it should not be unloaded. +To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled +The object itself is not made thread-safe, it's supposed to be used from a single thread only. +*/ +class cChunkStay +{ +public: + cChunkStay(cWorld * a_World); + ~cChunkStay(); + + void Clear(void); + + void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + void Enable(void); + void Disable(void); + + /// Queues each chunk in m_Chunks[] for loading / generating + void Load(void); + + // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList & + operator const cChunkCoordsList(void) const {return m_Chunks; } + +protected: + + cWorld * m_World; + + bool m_IsEnabled; + + cChunkCoordsList m_Chunks; +} ; + + + + diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp new file mode 100644 index 000000000..005cfe29d --- /dev/null +++ b/src/ChunkSender.cpp @@ -0,0 +1,295 @@ + +// ChunkSender.cpp + +// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients + + + + + +#include "Globals.h" +#include "ChunkSender.h" +#include "World.h" +#include "BlockEntities/BlockEntity.h" +#include "Protocol/ChunkDataSerializer.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNotifyChunkSender: + +void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkSender: + +cChunkSender::cChunkSender(void) : + super("ChunkSender"), + m_World(NULL), + m_Notify(NULL), + m_RemoveCount(0) +{ + m_Notify.SetChunkSender(this); +} + + + + + +cChunkSender::~cChunkSender() +{ + Stop(); +} + + + + + +bool cChunkSender::Start(cWorld * a_World) +{ + m_ShouldTerminate = false; + m_World = a_World; + return super::Start(); +} + + + + + +void cChunkSender::Stop(void) +{ + m_ShouldTerminate = true; + m_evtQueue.Set(); + Wait(); +} + + + + + +void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ) +{ + // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check + { + cCSLock Lock(m_CS); + m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } + m_evtQueue.Set(); +} + + + + + +void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(a_Client != NULL); + { + cCSLock Lock(m_CS); + if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end()) + { + // Already queued, bail out + return; + } + m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)); + } + m_evtQueue.Set(); +} + + + + + +void cChunkSender::RemoveClient(cClientHandle * a_Client) +{ + { + cCSLock Lock(m_CS); + for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();) + { + if (itr->m_Client == a_Client) + { + itr = m_SendChunks.erase(itr); + continue; + } + ++itr; + } // for itr - m_SendChunks[] + m_RemoveCount++; + } + m_evtQueue.Set(); + m_evtRemoved.Wait(); // Wait for removal confirmation +} + + + + + +void cChunkSender::Execute(void) +{ + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_ChunksReady.empty() && m_SendChunks.empty()) + { + int RemoveCount = m_RemoveCount; + m_RemoveCount = 0; + cCSUnlock Unlock(Lock); + for (int i = 0; i < RemoveCount; i++) + { + m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted + } + m_evtQueue.Wait(); + if (m_ShouldTerminate) + { + return; + } + } // while (empty) + + if (!m_ChunksReady.empty()) + { + // Take one from the queue: + cChunkCoords Coords(m_ChunksReady.front()); + m_ChunksReady.pop_front(); + Lock.Unlock(); + + SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL); + } + else + { + // Take one from the queue: + sSendChunk Chunk(m_SendChunks.front()); + m_SendChunks.pop_front(); + Lock.Unlock(); + + SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client); + } + Lock.Lock(); + int RemoveCount = m_RemoveCount; + m_RemoveCount = 0; + Lock.Unlock(); + for (int i = 0; i < RemoveCount; i++) + { + m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted + } + } // while (!mShouldTerminate) +} + + + + + +void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(m_World != NULL); + + // Ask the client if it still wants the chunk: + if (a_Client != NULL) + { + if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ)) + { + return; + } + } + + // If the chunk has no clients, no need to packetize it: + if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) + { + return; + } + + // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating + if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) + { + return; + } + + // If the chunk is not lighted, queue it for relighting and get notified when it's ready: + if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ)) + { + m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify); + return; + } + + // Query and prepare chunk data: + if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this)) + { + return; + } + cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap); + + // Send: + if (a_Client == NULL) + { + m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data); + } + else + { + a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data); + } + + // Send block-entity packets: + for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if (a_Client == NULL) + { + m_World->BroadcastBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ); + } + else + { + m_World->SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client); + } + } // for itr - m_Packets[] + m_BlockEntities.clear(); + + // TODO: Send entity spawn packets +} + + + + + +void cChunkSender::BlockEntity(cBlockEntity * a_Entity) +{ + m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ())); +} + + + + +void cChunkSender::Entity(cEntity * a_Entity) +{ + // Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;) +} + + + + + +void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++) + { + if ((*a_BiomeMap)[i] < 255) + { + // Normal MC biome, copy as-is: + m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]); + } + else + { + // TODO: MCS-specific biome, need to map to some basic MC biome: + ASSERT(!"Unimplemented MCS-specific biome"); + } + } // for i - m_BiomeMap[] +} + + + + diff --git a/src/ChunkSender.h b/src/ChunkSender.h new file mode 100644 index 000000000..a26f764a7 --- /dev/null +++ b/src/ChunkSender.h @@ -0,0 +1,169 @@ + +// ChunkSender.h + +// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients + +/* +The whole thing is a thread that runs in a loop, waiting for either: + "finished chunks" (ChunkReady()), or + "chunks to send" (QueueSendChunkTo() ) +to come to a queue. +And once they do, it requests the chunk data and sends it all away, either + broadcasting (ChunkReady), or + sends to a specific client (QueueSendChunkTo) +Chunk data is queried using the cChunkDataCallback interface. +It is cached inside the ChunkSender object during the query and then processed after the query ends. +Note that the data needs to be compressed only *after* the query finishes, +because the query callbacks run with ChunkMap's CS locked. + +A client may remove itself from all direct requests(QueueSendChunkTo()) by calling RemoveClient(); +this ensures that the client's Send() won't be called anymore by ChunkSender. +Note that it may be called by world's BroadcastToChunk() if the client is still in the chunk. +*/ + + + +#pragma once + +#include "OSSupport/IsThread.h" +#include "ChunkDef.h" + + + + + +class cWorld; +class cClientHandle; + + + + + +// fwd: +class cChunkSender; + + + + + +/// Callback that can be used to notify chunk sender upon another chunkcoord notification +class cNotifyChunkSender : + public cChunkCoordCallback +{ + virtual void Call(int a_ChunkX, int a_ChunkZ) override; + + cChunkSender * m_ChunkSender; +public: + cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {} + + void SetChunkSender(cChunkSender * a_ChunkSender) + { + m_ChunkSender = a_ChunkSender; + } +} ; + + + + + +class cChunkSender: + public cIsThread, + public cChunkDataSeparateCollector +{ + typedef cIsThread super; +public: + cChunkSender(void); + ~cChunkSender(); + + bool Start(cWorld * a_World); + + void Stop(void); + + /// Notifies that a chunk has become ready and it should be sent to all its clients + void ChunkReady(int a_ChunkX, int a_ChunkZ); + + /// Queues a chunk to be sent to a specific client + void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the a_Client from all waiting chunk send operations + void RemoveClient(cClientHandle * a_Client); + +protected: + + /// Used for sending chunks to specific clients + struct sSendChunk + { + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + cClientHandle * m_Client; + + sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) : + m_ChunkX(a_ChunkX), + m_ChunkY(a_ChunkY), + m_ChunkZ(a_ChunkZ), + m_Client(a_Client) + { + } + + bool operator ==(const sSendChunk & a_Other) + { + return ( + (a_Other.m_ChunkX == m_ChunkX) && + (a_Other.m_ChunkY == m_ChunkY) && + (a_Other.m_ChunkZ == m_ChunkZ) && + (a_Other.m_Client == m_Client) + ); + } + } ; + typedef std::list<sSendChunk> sSendChunkList; + + struct sBlockCoord + { + int m_BlockX; + int m_BlockY; + int m_BlockZ; + + sBlockCoord(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ) + { + } + } ; + + typedef std::vector<sBlockCoord> sBlockCoords; + + cWorld * m_World; + + cCriticalSection m_CS; + cChunkCoordsList m_ChunksReady; + sSendChunkList m_SendChunks; + cEvent m_evtQueue; // Set when anything is added to m_ChunksReady + cEvent m_evtRemoved; // Set when removed clients are safe to be deleted + int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times) + + cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc + + // Data about the chunk that is being sent: + // NOTE that m_BlockData[] is inherited from the cChunkDataCollector + unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width]; + sBlockCoords m_BlockEntities; // Coords of the block entities to send + // TODO: sEntityIDs m_Entities; // Entity-IDs of the entities to send + + // cIsThread override: + virtual void Execute(void) override; + + // cChunkDataCollector overrides: + // (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!) + virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override; + virtual void Entity (cEntity * a_Entity) override; + virtual void BlockEntity (cBlockEntity * a_Entity) override; + + /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL + void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); +} ; + + + + diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp new file mode 100644 index 000000000..b3e12ce77 --- /dev/null +++ b/src/ClientHandle.cpp @@ -0,0 +1,2219 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ClientHandle.h" +#include "Server.h" +#include "World.h" +#include "Entities/Pickup.h" +#include "PluginManager.h" +#include "Entities/Player.h" +#include "Inventory.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/SignEntity.h" +#include "UI/Window.h" +#include "Item.h" +#include "Piston.h" +#include "Mobs/Monster.h" +#include "ChatColor.h" +#include "OSSupport/Socket.h" +#include "OSSupport/Timer.h" +#include "Items/ItemHandler.h" +#include "Blocks/BlockHandler.h" +#include "Blocks/BlockSlab.h" + +#include "Vector3f.h" +#include "Vector3d.h" + +#include "Root.h" + +#include "Authenticator.h" +#include "MersenneTwister.h" + +#include "Protocol/ProtocolRecognizer.h" + + + + + +#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ + case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ + case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } + + + + + +/// If the number of queued outgoing packets reaches this, the client will be kicked +#define MAX_OUTGOING_PACKETS 2000 + +/// How many explosions per single game tick are allowed +static const int MAX_EXPLOSIONS_PER_TICK = 100; + +/// How many explosions in the recent history are allowed +static const int MAX_RUNNING_SUM_EXPLOSIONS = cClientHandle::NUM_CHECK_EXPLOSIONS_TICKS * MAX_EXPLOSIONS_PER_TICK / 8; + +/// How many ticks before the socket is closed after the client is destroyed (#31) +static const int TICKS_BEFORE_CLOSE = 20; + + + + + +#define RECI_RAND_MAX (1.f/RAND_MAX) +inline int fRadRand(MTRand & r1, int a_BlockCoord) +{ + return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8); +} + + + + + +int cClientHandle::s_ClientCount = 0; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cClientHandle: + +cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) + : m_ViewDistance(a_ViewDistance) + , m_IPString(a_Socket->GetIPString()) + , m_OutgoingData(64 KiB) + , m_Player(NULL) + , m_HasSentDC(false) + , m_TimeSinceLastPacket(0) + , m_bKeepThreadGoing(true) + , m_Ping(1000) + , m_PingID(1) + , m_TicksSinceDestruction(0) + , m_State(csConnected) + , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login + , m_LastStreamedChunkZ(0x7fffffff) + , m_ShouldCheckDownloaded(false) + , m_UniqueID(0) + , m_BlockDigAnimStage(-1) + , m_HasStartedDigging(false) + , m_CurrentExplosionTick(0) + , m_RunningSumExplosions(0) + , m_HasSentPlayerChunk(false) +{ + m_Protocol = new cProtocolRecognizer(this); + + s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread + m_UniqueID = s_ClientCount; + + cTimer t1; + m_LastPingTime = t1.GetNowTime(); + + LOGD("New ClientHandle created at %p", this); +} + + + + + +cClientHandle::~cClientHandle() +{ + ASSERT(m_State >= csDestroyedWaiting); // Has Destroy() been called? + + LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this); + + // Remove from cSocketThreads, we're not to be called anymore: + cRoot::Get()->GetServer()->ClientDestroying(this); + + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); + } + + if (m_Player != NULL) + { + cWorld * World = m_Player->GetWorld(); + if (!m_Username.empty() && (World != NULL)) + { + // Send the Offline PlayerList packet: + World->BroadcastPlayerListItem(*m_Player, false, this); + } + if (World != NULL) + { + World->RemovePlayer(m_Player); + m_Player->Destroy(); + } + delete m_Player; + m_Player = NULL; + } + + if (!m_HasSentDC) + { + SendDisconnect("Server shut down? Kthnxbai"); + } + + // Queue all remaining outgoing packets to cSocketThreads: + { + cCSLock Lock(m_CSOutgoingData); + AString Data; + m_OutgoingData.ReadAll(Data); + m_OutgoingData.CommitRead(); + cRoot::Get()->GetServer()->WriteToClient(this, Data); + } + + // Queue the socket to close as soon as it sends all outgoing data: + cRoot::Get()->GetServer()->QueueClientClose(this); + cRoot::Get()->GetServer()->RemoveClient(this); + + delete m_Protocol; + m_Protocol = NULL; + + LOGD("ClientHandle at %p deleted", this); +} + + + + + +void cClientHandle::Destroy(void) +{ + { + cCSLock Lock(m_CSDestroyingState); + if (m_State >= csDestroying) + { + // Already called + return; + } + m_State = csDestroying; + } + + // DEBUG: + LOGD("%s: client %p, \"%s\"", __FUNCTION__, this, m_Username.c_str()); + + if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) + { + RemoveFromAllChunks(); + m_Player->GetWorld()->RemoveClientFromChunkSender(this); + } + m_State = csDestroyedWaiting; +} + + + + + +void cClientHandle::Kick(const AString & a_Reason) +{ + if (m_State >= csAuthenticating) // Don't log pings + { + LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str()); + } + SendDisconnect(a_Reason); +} + + + + + +void cClientHandle::Authenticate(void) +{ + if (m_State != csAuthenticating) + { + return; + } + + ASSERT( m_Player == NULL ); + + // Spawn player (only serversided, so data is loaded) + m_Player = new cPlayer(this, GetUsername()); + + cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); + if (World == NULL) + { + World = cRoot::Get()->GetDefaultWorld(); + } + + if (m_Player->GetGameMode() == eGameMode_NotSet) + { + m_Player->LoginSetGameMode(World->GetGameMode()); + } + + m_Player->SetIP (m_IPString); + + cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player); + + m_ConfirmPosition = m_Player->GetPosition(); + + // Return a server login packet + m_Protocol->SendLogin(*m_Player, *World); + + // Send Weather if raining: + if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) + { + m_Protocol->SendWeather(World->GetWeather()); + } + + // Send time + m_Protocol->SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay()); + + // Send contents of the inventory window + m_Protocol->SendWholeInventory(*m_Player->GetWindow()); + + // Send health + m_Player->SendHealth(); + + // Send experience + m_Player->SendExperience(); + + // Send gamemode (1.6.1 movementSpeed): + SendGameMode(m_Player->GetGameMode()); + + m_Player->Initialize(World); + m_State = csAuthenticated; + + // Broadcast this player's spawning to all other players in the same chunk + m_Player->GetWorld()->BroadcastSpawnEntity(*m_Player, this); + + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); +} + + + + + +void cClientHandle::StreamChunks(void) +{ + if ((m_State < csAuthenticated) || (m_State >= csDestroying)) + { + return; + } + + ASSERT(m_Player != NULL); + + int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width); + int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width); + if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ)) + { + // Already streamed for this position + return; + } + m_LastStreamedChunkX = ChunkPosX; + m_LastStreamedChunkZ = ChunkPosZ; + + LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance); + + cWorld * World = m_Player->GetWorld(); + ASSERT(World != NULL); + + // Remove all loaded chunks that are no longer in range; deferred to out-of-CS: + cChunkCoordsList RemoveChunks; + { + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) + { + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) + { + RemoveChunks.push_back(*itr); + itr = m_LoadedChunks.erase(itr); + } + else + { + ++itr; + } + } // for itr - m_LoadedChunks[] + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) + { + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) + { + itr = m_ChunksToSend.erase(itr); + } + else + { + ++itr; + } + } // for itr - m_ChunksToSend[] + } + for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); + m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); + } // for itr - RemoveChunks[] + + // Add all chunks that are in range and not yet in m_LoadedChunks: + // Queue these smartly - from the center out to the edge + for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance add chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + StreamChunk(ChunkPosX + d, ChunkPosZ + i); + StreamChunk(ChunkPosX - d, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + StreamChunk(ChunkPosX + i, ChunkPosZ + d); + StreamChunk(ChunkPosX + i, ChunkPosZ - d); + } // for i + } // for d + + // Touch chunks GENERATEDISTANCE ahead to let them generate: + for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance touch chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); + World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); + World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); + } // for i + } // for d +} + + + + +void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ) +{ + if (m_State >= csDestroying) + { + // Don't stream chunks to clients that are being destroyed + return; + } + + cWorld * World = m_Player->GetWorld(); + ASSERT(World != NULL); + + if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this)) + { + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } + World->SendChunkTo(a_ChunkX, a_ChunkZ, this); + } +} + + + + + +// Removes the client from all chunks. Used when switching worlds or destroying the player +void cClientHandle::RemoveFromAllChunks() +{ + cWorld * World = m_Player->GetWorld(); + if (World != NULL) + { + World->RemoveClientFromChunks(this); + } + + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); + + // Also reset the LastStreamedChunk coords to bogus coords, + // so that all chunks are streamed in subsequent StreamChunks() call (FS #407) + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; + } +} + + + + + +void cClientHandle::HandlePing(void) +{ + // Somebody tries to retrieve information about the server + AString Reply; + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetServer()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetMaxPlayers() + ); + Kick(Reply.c_str()); +} + + + + + +bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username) +{ + LOGD("LOGIN %s", a_Username.c_str()); + m_Username = a_Username; + + if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username)) + { + Destroy(); + return false; + } + + // Schedule for authentication; until then, let them wait (but do not block) + m_State = csAuthenticating; + cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID()); + return true; +} + + + + + +void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem) +{ + // This is for creative Inventory changes + if (!m_Player->IsGameModeCreative()) + { + LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str()); + return; + } + if (m_Player->GetWindow()->GetWindowType() != cWindow::wtInventory) + { + LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str()); + return; + } + + m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, (a_SlotNum >= 0) ? caLeftClick : caLeftClickOutside, a_HeldItem); +} + + + + + +void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround) +{ + if ((m_Player == NULL) || (m_State != csPlaying)) + { + // The client hasn't been spawned yet and sends nonsense, we know better + return; + } + + /* + // TODO: Invalid stance check + if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) + { + LOGD("Invalid stance"); + SendPlayerMoveLook(); + return; + } + */ + + // If the player has moved too far, "repair" them: + Vector3d Pos(a_PosX, a_PosY, a_PosZ); + if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) + { + LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length()); + SendPlayerMoveLook(); + return; + } + + // If a jump just started, process food exhaustion: + if ((a_PosY > m_Player->GetPosY()) && !a_IsOnGround && m_Player->IsOnGround()) + { + // we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion + + if (!m_Player->IsSwimming()) + { + m_Player->AddFoodExhaustion(m_Player->IsSprinting() ? 0.8 : 0.2); + } + } + + m_Player->MoveTo(Pos); + m_Player->SetStance(a_Stance); + m_Player->SetTouchGround(a_IsOnGround); +} + + + + + +void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status + ); + + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) + { + // A plugin doesn't agree with the action, replace the block on the client and quit: + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + if (!CheckBlockInteractionsRate()) + { + // Too many interactions per second, simply ignore. Probably a hacked client, so don't even send bak the block + return; + } + + switch (a_Status) + { + case DIG_STATUS_DROP_HELD: // Drop held item + { + if (PlgMgr->CallHookPlayerTossingItem(*m_Player)) + { + // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch) + return; + } + m_Player->TossItem(false); + return; + } + + case DIG_STATUS_SHOOT_EAT: + { + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + if (ItemHandler->IsFood()) + { + m_Player->AbortEating(); + return; + } + else + { + if (PlgMgr->CallHookPlayerShooting(*m_Player)) + { + // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch) + return; + } + ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + } + return; + } + + case DIG_STATUS_STARTED: + { + BLOCKTYPE OldBlock; + NIBBLETYPE OldMeta; + m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); + HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); + return; + } + + case DIG_STATUS_FINISHED: + { + BLOCKTYPE OldBlock; + NIBBLETYPE OldMeta; + m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); + HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta); + return; + } + + case DIG_STATUS_CANCELLED: + { + // Block breaking cancelled by player + return; + } + + default: + { + ASSERT(!"Unhandled DIG_STATUS"); + return; + } + } // switch (a_Status) +} + + + + + +void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + if ( + m_HasStartedDigging && + (a_BlockX == m_LastDigBlockX) && + (a_BlockY == m_LastDigBlockY) && + (a_BlockZ == m_LastDigBlockZ) + ) + { + // It is a duplicate packet, drop it right away + return; + } + + if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) + { + // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows: + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: + m_HasStartedDigging = true; + m_LastDigBlockX = a_BlockX; + m_LastDigBlockY = a_BlockY; + m_LastDigBlockZ = a_BlockZ; + + if ( + (m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately + g_BlockOneHitDig[a_OldBlock] // One-hit blocks get destroyed immediately, too + ) + { + HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta); + return; + } + + // Start dig animation + // TODO: calculate real animation speed + // TODO: Send animation packets even without receiving any other packets + m_BlockDigAnimSpeed = 10; + m_BlockDigAnimX = a_BlockX; + m_BlockDigAnimY = a_BlockY; + m_BlockDigAnimZ = a_BlockZ; + m_BlockDigAnimStage = 0; + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 0, this); + + cWorld * World = m_Player->GetWorld(); + + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(a_OldBlock); + Handler->OnDigging(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // Check for clickthrough-blocks: + if (a_BlockFace != BLOCK_FACE_NONE) + { + int pX = a_BlockX; + int pY = a_BlockY; + int pZ = a_BlockZ; + AddFaceDirection(pX, pY, pZ, a_BlockFace); + + Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ)); + + // 2013_01_05 _X: This looks weird + // Why do we ask the block "behind" the one being clicked if it is clicked through? Shouldn't we ask the primary block instead? + if (Handler->IsClickedThrough()) + { + Handler->OnDigging(World, m_Player, pX, pY, pZ); + } + } +} + + + + + +void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + if ( + !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet + (m_LastDigBlockX != a_BlockX) || // DIG_STARTED has had different pos + (m_LastDigBlockY != a_BlockY) || + (m_LastDigBlockZ != a_BlockZ) + ) + { + LOGD("Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s)", + a_BlockX, a_BlockY, a_BlockZ, + m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, + m_HasStartedDigging + ); + return; + } + + m_HasStartedDigging = false; + if (m_BlockDigAnimStage != -1) + { + // End dig animation + m_BlockDigAnimStage = -1; + // It seems that 10 ends block animation + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this); + } + + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + + if (a_OldBlock == E_BLOCK_AIR) + { + LOGD("Dug air - what the function?"); + return; + } + + 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, a_BlockY, a_BlockZ, a_OldBlock, this); + World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + + cRoot::Get()->GetPluginManager()->CallHookPlayerBrokenBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta); +} + + + + + +void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem) +{ + LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str() + ); + + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // A plugin doesn't agree with the action, replace the block on the client and quit: + if (a_BlockFace > -1) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + } + return; + } + + if (!CheckBlockInteractionsRate()) + { + LOGD("Too many block interactions, aborting placement"); + return; + } + + const cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); + + if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) + { + // Only compare ItemType, not meta (torches have different metas) + // The -1 check is there because sometimes the client sends -1 instead of the held item + // ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 ) + LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", + m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType + ); + + // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block + if (a_BlockFace > -1) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + } + return; + } + + cWorld * World = m_Player->GetWorld(); + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType); + + if (BlockHandler->IsUseable() && !m_Player->IsCrouched()) + { + if (PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // A plugin doesn't agree with using the block, abort + return; + } + BlockHandler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); + return; + } + + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); + + if (ItemHandler->IsPlaceable()) + { + HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + } + else if (ItemHandler->IsFood()) + { + if (m_Player->IsSatiated()) + { + // The player is satiated, they cannot eat + return; + } + m_Player->StartEating(); + if (PlgMgr->CallHookPlayerEating(*m_Player)) + { + // A plugin won't let us eat, abort (send the proper packets to the client, too): + m_Player->AbortEating(); + return; + } + return; + } + else + { + if (PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // A plugin doesn't agree with using the item, abort + return; + } + ItemHandler->OnItemUse(World, m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + } +} + + + + + +void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler) +{ + if (a_BlockFace < 0) + { + // Clicked in air + return; + } + + cWorld * World = m_Player->GetWorld(); + + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedBlockMeta; + BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType); + NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return; + } + + World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); + + // Special slab handling - placing a slab onto another slab produces a dblslab instead: + if ( + cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already? + cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab? + ((ClickedBlockMeta & 0x07) == (EquippedBlockDamage & 0x07)) && // Is it the same slab type? + ( + (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab + (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab + ) + ) + { + // Coordinates at CLICKED block, don't move them anywhere + } + else + { + // Check if the block ignores build collision (water, grass etc.): + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(ClickedBlock); + if (Handler->DoesIgnoreBuildCollision()) + { + Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + } + + BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return; + } + + BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. + // No need to do combinability (dblslab) checks, client will do that here. + if (cBlockSlabHandler::IsAnySlabType(PlaceBlock)) + { + // It's a slab, don't do checks and proceed to double-slabbing + } + else + { + if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision()) + { + // Tried to place a block *into* another? + // Happens when you place a block aiming at side of block like torch or stem + return; + } + } + } + } + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // Handler refused the placement, send that information back to the client: + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockY, m_Player); + return; + } + + cBlockHandler * NewBlock = BlockHandler(BlockType); + + if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // A plugin doesn't agree with placing the block, revert the block on the client: + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + // The actual block placement: + World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + if (m_Player->GetGameMode() != gmCreative) + { + m_Player->GetInventory().RemoveOneEquippedItem(); + } + NewBlock->OnPlacedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); + + // Step sound with 0.8f pitch is used as block placement sound + World->BroadcastSoundEffect(NewBlock->GetStepSound(), a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f); + cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); +} + + + + + +void cClientHandle::HandleChat(const AString & a_Message) +{ + // We no longer need to postpone message processing, because the messages already arrive in the Tick thread + + // If a command, perform it: + AString Message(a_Message); + if (cRoot::Get()->GetServer()->Command(*this, Message)) + { + return; + } + + // Not a command, broadcast as a simple message: + AString Msg; + Printf(Msg, "<%s%s%s> %s", + m_Player->GetColor().c_str(), + m_Player->GetName().c_str(), + cChatColor::White.c_str(), + Message.c_str() + ); + m_Player->GetWorld()->BroadcastChat(Msg); +} + + + + + +void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround) +{ + if ((m_Player == NULL) || (m_State != csPlaying)) + { + return; + } + + m_Player->SetRotation (a_Rotation); + m_Player->SetHeadYaw (a_Rotation); + m_Player->SetPitch (a_Pitch); + m_Player->SetTouchGround(a_IsOnGround); +} + + + + + +void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) +{ + if ((m_Player == NULL) || (m_State != csPlaying)) + { + // The client hasn't been spawned yet and sends nonsense, we know better + return; + } + + /* + // TODO: Invalid stance check + if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) + { + LOGD("Invalid stance"); + SendPlayerMoveLook(); + return; + } + */ + + m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); + m_Player->SetStance (a_Stance); + m_Player->SetTouchGround(a_IsOnGround); + m_Player->SetHeadYaw (a_Rotation); + m_Player->SetRotation (a_Rotation); + m_Player->SetPitch (a_Pitch); +} + + + + + +void cClientHandle::HandleAnimation(char a_Animation) +{ + if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation)) + { + // Plugin disagrees, bail out + return; + } + + m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, a_Animation, this); +} + + + + + +void cClientHandle::HandleSlotSelected(short a_SlotNum) +{ + m_Player->GetInventory().SetEquippedSlotNum(a_SlotNum); + m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); +} + + + + + +void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways) +{ + m_Player->SteerVehicle(a_Forward, a_Sideways); +} + + + + + +void cClientHandle::HandleWindowClose(char a_WindowID) +{ + m_Player->CloseWindowIfID(a_WindowID); +} + + + + + +void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem) +{ + LOGD("WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d", + a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction), + ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount + ); + + cWindow * Window = m_Player->GetWindow(); + if (Window == NULL) + { + LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); + return; + } + + Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem); +} + + + + + +void cClientHandle::HandleUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, + const AString & a_Line3, const AString & a_Line4 +) +{ + cWorld * World = m_Player->GetWorld(); + World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player); +} + + + + + +void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick) +{ + // TODO: Let plugins interfere via a hook + + // If it is a right click, call the entity's OnRightClicked() handler: + if (!a_IsLeftClick) + { + class cRclkEntity : public cEntityCallback + { + cPlayer & m_Player; + virtual bool Item(cEntity * a_Entity) override + { + if (cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity)) + { + return false; + } + a_Entity->OnRightClicked(m_Player); + return false; + } + public: + cRclkEntity(cPlayer & a_Player) : m_Player(a_Player) {} + } Callback (*m_Player); + + cWorld * World = m_Player->GetWorld(); + World->DoWithEntityByID(a_TargetEntityID, Callback); + return; + } + + // If it is a left click, attack the entity: + class cDamageEntity : public cEntityCallback + { + virtual bool Item(cEntity * a_Entity) override + { + if (!a_Entity->GetWorld()->IsPVPEnabled()) + { + // PVP is disabled, disallow players hurting other players: + if (a_Entity->IsPlayer()) + { + // Player is hurting another player which is not allowed when PVP is disabled so ignore it + return true; + } + } + a_Entity->TakeDamage(*m_Attacker); + return false; + } + public: + cPawn * m_Attacker; + } Callback; + + Callback.m_Attacker = m_Player; + + cWorld * World = m_Player->GetWorld(); + if (World->DoWithEntityByID(a_TargetEntityID, Callback)) + { + // Any kind of an attack implies food exhaustion + m_Player->AddFoodExhaustion(0.3); + } +} + + + + + +void cClientHandle::HandleRespawn(void) +{ + if (m_Player == NULL) + { + Destroy(); + return; + } + m_Player->Respawn(); + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); +} + + + + + +void cClientHandle::HandleDisconnect(const AString & a_Reason) +{ + LOGD("Received d/c packet from \"%s\" with reason \"%s\"", m_Username.c_str(), a_Reason.c_str()); + if (!cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason)) + { + AString DisconnectMessage; + Printf(DisconnectMessage, "%s disconnected: %s", m_Username.c_str(), a_Reason.c_str()); + m_Player->GetWorld()->BroadcastChat(DisconnectMessage, this); + } + m_HasSentDC = true; + Destroy(); +} + + + + + +void cClientHandle::HandleKeepAlive(int a_KeepAliveID) +{ + if (a_KeepAliveID == m_PingID) + { + cTimer t1; + m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2); + } +} + + + + + +bool cClientHandle::HandleHandshake(const AString & a_Username) +{ + if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username)) + { + if (cRoot::Get()->GetServer()->GetNumPlayers() >= cRoot::Get()->GetServer()->GetMaxPlayers()) + { + Kick("The server is currently full :(-- Try again later"); + return false; + } + } + return true; +} + + + + + +void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID) +{ + if (a_EntityID != m_Player->GetUniqueID()) + { + // We should only receive entity actions from the entity that is performing the action + return; + } + + switch (a_ActionID) + { + case 1: // crouch + { + m_Player->SetCrouch(true); + break; + } + case 2: // uncrouch + { + m_Player->SetCrouch(false); + break; + } + case 3: // Leave bed + { + m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, 3); + break; + } + case 4: // Start sprinting + { + m_Player->SetSprint(true); + break; + } + case 5: // Stop sprinting + { + m_Player->SetSprint(false); + SendPlayerMaxSpeed(); + break; + } + } +} + + + + + +void cClientHandle::HandleUnmount(void) +{ + if (m_Player == NULL) + { + return; + } + m_Player->Detach(); +} + + + + + +void cClientHandle::HandleTabCompletion(const AString & a_Text) +{ + AStringVector Results; + m_Player->GetWorld()->TabCompleteUserName(a_Text, Results); + cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player); + if (Results.empty()) + { + return; + } + std::sort(Results.begin(), Results.end()); + SendTabCompletionResults(Results); +} + + + + + +void cClientHandle::SendData(const char * a_Data, int a_Size) +{ + if (m_HasSentDC) + { + // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31) + return; + } + + { + cCSLock Lock(m_CSOutgoingData); + + // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks + if (m_OutgoingDataOverflow.empty()) + { + // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer: + int CanFit = m_OutgoingData.GetFreeSpace(); + if (CanFit > a_Size) + { + CanFit = a_Size; + } + if (CanFit > 0) + { + m_OutgoingData.Write(a_Data, CanFit); + } + if (a_Size > CanFit) + { + m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit); + } + } + else + { + // There is a queued overflow. Append to it, then send as much from its front as possible + m_OutgoingDataOverflow.append(a_Data, a_Size); + int CanFit = m_OutgoingData.GetFreeSpace(); + if (CanFit > 128) + { + // No point in moving the data over if it's not large enough - too much effort for too little an effect + m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit); + m_OutgoingDataOverflow.erase(0, CanFit); + } + } + } // Lock(m_CSOutgoingData) + + // Notify SocketThreads that we have something to write: + cRoot::Get()->GetServer()->NotifyClientWrite(this); +} + + + + + +void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket) +{ + ASSERT(m_Player != NULL); + + if (a_SendRespawnPacket) + { + SendRespawn(); + } + + cWorld * World = m_Player->GetWorld(); + + // Remove all associated chunks: + cChunkCoordsList Chunks; + { + cCSLock Lock(m_CSChunkLists); + std::swap(Chunks, m_LoadedChunks); + m_ChunksToSend.clear(); + } + for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); + m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); + } // for itr - Chunks[] + + // Do NOT stream new chunks, the new world runs its own tick thread and may deadlock + // Instead, the chunks will be streamed when the client is moved to the new world's Tick list, + // by setting state to csAuthenticated + m_State = csAuthenticated; + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; + m_HasSentPlayerChunk = false; +} + + + + + +bool cClientHandle::CheckBlockInteractionsRate(void) +{ + ASSERT(m_Player != NULL); + ASSERT(m_Player->GetWorld() != NULL); + /* + // TODO: _X 2012_11_01: This needs a total re-thinking and rewriting + int LastActionCnt = m_Player->GetLastBlockActionCnt(); + if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1) + { + // Limit the number of block interactions per tick + m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time. + m_Player->SetLastBlockActionCnt(LastActionCnt + 1); + if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS) + { + // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick + LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str()); + Kick("You're a baaaaaad boy!"); + return false; + } + } + else + { + m_Player->SetLastBlockActionCnt(0); // Reset count + m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time. + } + */ + return true; +} + + + + + +void cClientHandle::Tick(float a_Dt) +{ + // Handle clients that are waiting for final close while destroyed: + if (m_State == csDestroyedWaiting) + { + m_TicksSinceDestruction += 1; // This field is misused for the timeout counting + if (m_TicksSinceDestruction > TICKS_BEFORE_CLOSE) + { + m_State = csDestroyed; + } + return; + } + + // Process received network data: + AString IncomingData; + { + cCSLock Lock(m_CSIncomingData); + std::swap(IncomingData, m_IncomingData); + } + m_Protocol->DataReceived(IncomingData.data(), IncomingData.size()); + + if (m_State == csAuthenticated) + { + StreamChunks(); + m_State = csDownloadingWorld; + } + + m_TimeSinceLastPacket += a_Dt; + if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out + { + SendDisconnect("Nooooo!! You timed out! D: Come back!"); + Destroy(); + } + + if (m_Player == NULL) + { + return; + } + + // If the chunk the player's in was just sent, spawn the player: + if (m_HasSentPlayerChunk && (m_State != csPlaying) && !IsDestroying()) + { + if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player)) + { + // Broadcast that this player has joined the game! Yay~ + m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this); + } + m_Protocol->SendPlayerMoveLook(); + m_State = csPlaying; + } + + // Send a ping packet: + cTimer t1; + if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime())) + { + m_PingID++; + m_PingStartTime = t1.GetNowTime(); + m_Protocol->SendKeepAlive(m_PingID); + m_LastPingTime = m_PingStartTime; + } + + // Handle block break animation: + if (m_BlockDigAnimStage > -1) + { + int lastAnimVal = m_BlockDigAnimStage; + m_BlockDigAnimStage += (int)(m_BlockDigAnimSpeed * a_Dt); + if (m_BlockDigAnimStage > 9000) + { + m_BlockDigAnimStage = 9000; + } + if (m_BlockDigAnimStage / 1000 != lastAnimVal / 1000) + { + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, (char)(m_BlockDigAnimStage / 1000), this); + } + } + + // Update the explosion statistics: + m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick); + m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick]; + m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0; +} + + + + + +void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cClientHandle::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) +{ + m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage); +} + + + + + +void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets! + + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cClientHandle::SendChat(const AString & a_Message) +{ + m_Protocol->SendChat(a_Message); +} + + + + + +void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_Player != NULL); + + // Check chunks being sent, erase them from m_ChunksToSend: + bool Found = false; + { + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) + { + m_ChunksToSend.erase(itr); + Found = true; + break; + } + } // for itr - m_ChunksToSend[] + } + if (!Found) + { + // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it + // It's not a big issue anyway, just means that some chunks may be compressed several times + // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ()); + return; + } + + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); + + // If it is the chunk the player's in, make them spawn (in the tick thread): + if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld)) + { + if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ())) + { + m_HasSentPlayerChunk = true; + } + } +} + + + + + +void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cClientHandle::SendDestroyEntity(const cEntity & a_Entity) +{ + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cClientHandle::SendDisconnect(const AString & a_Reason) +{ + if (!m_HasSentDC) + { + LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str()); + m_Protocol->SendDisconnect(a_Reason); + m_HasSentDC = true; + } +} + + + + + +void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityHeadLook(a_Entity); +} + + + + + +void cClientHandle::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityLook(a_Entity); +} + + + + + +void cClientHandle::SendEntityMetadata(const cEntity & a_Entity) +{ + m_Protocol->SendEntityMetadata(a_Entity); +} + + + + + +void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cClientHandle::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntityVelocity(a_Entity); +} + + + + + +void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + if ( + (m_NumExplosionsPerTick[m_CurrentExplosionTick] > MAX_EXPLOSIONS_PER_TICK) || // Too many explosions in this tick + (m_RunningSumExplosions > MAX_RUNNING_SUM_EXPLOSIONS) // Too many explosions in the recent history + ) + { + LOGD("Dropped %u explosions", a_BlocksAffected.size()); + return; + } + + // Update the statistics: + m_NumExplosionsPerTick[m_CurrentExplosionTick] += a_BlocksAffected.size(); + m_RunningSumExplosions += a_BlocksAffected.size(); + + m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); +} + + + + + +void cClientHandle::SendGameMode(eGameMode a_GameMode) +{ + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cClientHandle::SendHealth(void) +{ + m_Protocol->SendHealth(); +} + + + + + +void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) +{ + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cClientHandle::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cClientHandle::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cClientHandle::SendPlayerMaxSpeed(void) +{ + m_Protocol->SendPlayerMaxSpeed(); +} + + + + + +void cClientHandle::SendPlayerMoveLook(void) +{ + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cClientHandle::SendPlayerPosition(void) +{ + m_Protocol->SendPlayerPosition(); +} + + + + + +void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) +{ + if (a_Player.GetUniqueID() == m_Player->GetUniqueID()) + { + // Do NOT send this packet to myself + return; + } + + LOGD("Spawning player \"%s\" on client \"%s\" @ %s", + a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() + ); + + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cClientHandle::SendRespawn(void) +{ + m_Protocol->SendRespawn(); +} + + + + + +void cClientHandle::SendExperience(void) +{ + m_Protocol->SendExperience(); +} + + + + + +void cClientHandle::SendExperienceOrb(const cExpOrb & a_ExpOrb) +{ + m_Protocol->SendExperienceOrb(a_ExpOrb); +} + + + + + +void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cClientHandle::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); +} + + + + + +void cClientHandle::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + m_Protocol->SendSpawnFallingBlock(a_FallingBlock); +} + + + + + +void cClientHandle::SendSpawnMob(const cMonster & a_Mob) +{ + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); +} + + + + + +void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts +{ + m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); +} + + + + + +void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results) +{ + m_Protocol->SendTabCompletionResults(a_Results); +} + + + + + +void cClientHandle::SendTeleportEntity(const cEntity & a_Entity) +{ + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); +} + + + + + +void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cClientHandle::SendUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 +) +{ + m_Protocol->SendUpdateSign( + a_BlockX, a_BlockY, a_BlockZ, + a_Line1, a_Line2, a_Line3, a_Line4 + ); +} + + + + + +void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + +void cClientHandle::SendWeather(eWeather a_Weather) +{ + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cClientHandle::SendWholeInventory(const cWindow & a_Window) +{ + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cClientHandle::SendWindowClose(const cWindow & a_Window) +{ + m_Protocol->SendWindowClose(a_Window); +} + + + + + +void cClientHandle::SendWindowOpen(const cWindow & a_Window) +{ + m_Protocol->SendWindowOpen(a_Window); +} + + + + + +void cClientHandle::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value) +{ + m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); +} + + + + + +const AString & cClientHandle::GetUsername(void) const +{ + return m_Username; +} + + + + + +void cClientHandle::SetUsername( const AString & a_Username ) +{ + m_Username = a_Username; +} + + + + + +void cClientHandle::SetViewDistance(int a_ViewDistance) +{ + if (a_ViewDistance < MIN_VIEW_DISTANCE) + { + a_ViewDistance = MIN_VIEW_DISTANCE; + } + if (a_ViewDistance > MAX_VIEW_DISTANCE) + { + a_ViewDistance = MAX_VIEW_DISTANCE; + } + m_ViewDistance = a_ViewDistance; + + // Need to re-stream chunks for the change to become apparent: + StreamChunks(); +} + + + + + +bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + if (m_State >= csDestroying) + { + return false; + } + + cCSLock Lock(m_CSChunkLists); + return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end()); +} + + + + + +void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) +{ + if (m_State >= csDestroying) + { + return; + } + + LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this); + cCSLock Lock(m_CSChunkLists); + if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end()) + { + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } +} + + + + + +void cClientHandle::PacketBufferFull(void) +{ + // Too much data in the incoming queue, the server is probably too busy, kick the client: + LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_IPString.c_str()); + SendDisconnect("Server busy"); + Destroy(); +} + + + + + +void cClientHandle::PacketUnknown(unsigned char a_PacketType) +{ + LOGERROR("Unknown packet type 0x%02x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_IPString.c_str()); + + AString Reason; + Printf(Reason, "Unknown [C->S] PacketType: 0x%02x", a_PacketType); + SendDisconnect(Reason); + Destroy(); +} + + + + + +void cClientHandle::PacketError(unsigned char a_PacketType) +{ + LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); + SendDisconnect("Protocol error"); + Destroy(); +} + + + + + +void cClientHandle::DataReceived(const char * a_Data, int a_Size) +{ + // Data is received from the client, store it in the buffer to be processed by the Tick thread: + m_TimeSinceLastPacket = 0; + cCSLock Lock(m_CSIncomingData); + m_IncomingData.append(a_Data, a_Size); +} + + + + + +void cClientHandle::GetOutgoingData(AString & a_Data) +{ + // Data can be sent to client + { + cCSLock Lock(m_CSOutgoingData); + m_OutgoingData.ReadAll(a_Data); + m_OutgoingData.CommitRead(); + a_Data.append(m_OutgoingDataOverflow); + m_OutgoingDataOverflow.clear(); + } + + // Disconnect player after all packets have been sent + if (m_HasSentDC && a_Data.empty()) + { + Destroy(); + } +} + + + + + +void cClientHandle::SocketClosed(void) +{ + // The socket has been closed for any reason + + LOGD("Client \"%s\" @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); + Destroy(); +} + + + + + + diff --git a/src/ClientHandle.h b/src/ClientHandle.h new file mode 100644 index 000000000..b3550110d --- /dev/null +++ b/src/ClientHandle.h @@ -0,0 +1,334 @@ + +// cClientHandle.h + +// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet + + + + + +#pragma once +#ifndef CCLIENTHANDLE_H_INCLUDED +#define CCLIENTHANDLE_H_INCLUDED + +#include "Defines.h" +#include "Vector3d.h" +#include "OSSupport/SocketThreads.h" +#include "ChunkDef.h" +#include "ByteBuffer.h" + + + + + +class cChunkDataSerializer; +class cInventory; +class cMonster; +class cPawn; +class cExpOrb; +class cPickup; +class cPlayer; +class cProtocol; +class cRedstone; +class cWindow; +class cFallingBlock; +class cItemHandler; +class cWorld; + + + + + +class cClientHandle : // tolua_export + public cSocketThreads::cCallback +{ // tolua_export +public: + enum ENUM_PRIORITY + { + E_PRIORITY_LOW, + E_PRIORITY_NORMAL + }; + + static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick + +#if defined(ANDROID_NDK) + static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini) +#else + static const int DEFAULT_VIEW_DISTANCE = 10; +#endif + static const int MAX_VIEW_DISTANCE = 15; + static const int MIN_VIEW_DISTANCE = 3; + + /// How many ticks should be checked for a running average of explosions, for limiting purposes + static const int NUM_CHECK_EXPLOSIONS_TICKS = 20; + + cClientHandle(const cSocket * a_Socket, int a_ViewDistance); + virtual ~cClientHandle(); + + const AString & GetIPString(void) const { return m_IPString; } + + cPlayer* GetPlayer() { return m_Player; } // tolua_export + + void Kick(const AString & a_Reason); // tolua_export + void Authenticate(void); // Called by cAuthenticator when the user passes authentication + + void StreamChunks(void); + + // Removes the client from all chunks. Used when switching worlds or destroying the player + void RemoveFromAllChunks(void); + + inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); } + + void Tick(float a_Dt); + + void Destroy(void); + + bool IsPlaying (void) const { return (m_State == csPlaying); } + bool IsDestroyed (void) const { return (m_State == csDestroyed); } + bool IsDestroying(void) const { return (m_State == csDestroying); } + + // The following functions send the various packets: + // (Please keep these alpha-sorted) + void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); + void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType); + void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage); + void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export + void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); + void SendChat (const AString & a_Message); + void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); + void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); + void SendDestroyEntity (const cEntity & a_Entity); + void SendDisconnect (const AString & a_Reason); + void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ); + void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item); + void SendEntityHeadLook (const cEntity & a_Entity); + void SendEntityLook (const cEntity & a_Entity); + void SendEntityMetadata (const cEntity & a_Entity); + void SendEntityProperties (const cEntity & a_Entity); + void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); + void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); + void SendEntityStatus (const cEntity & a_Entity, char a_Status); + void SendEntityVelocity (const cEntity & a_Entity); + void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion); + void SendGameMode (eGameMode a_GameMode); + void SendHealth (void); + void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); + void SendPickupSpawn (const cPickup & a_Pickup); + void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation); + void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline); + void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+) + void SendPlayerMoveLook (void); + void SendPlayerPosition (void); + void SendPlayerSpawn (const cPlayer & a_Player); + void SendRespawn (void); + void SendExperience (void); + void SendExperienceOrb (const cExpOrb & a_ExpOrb); + void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 + void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); + void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); + void SendSpawnMob (const cMonster & a_Mob); + void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch); + void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0); + void SendTabCompletionResults(const AStringVector & a_Results); + void SendTeleportEntity (const cEntity & a_Entity); + void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); + void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay); + void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); + void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + void SendWeather (eWeather a_Weather); + void SendWholeInventory (const cWindow & a_Window); + void SendWindowClose (const cWindow & a_Window); + void SendWindowOpen (const cWindow & a_Window); + void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value); + + const AString & GetUsername(void) const; // tolua_export + void SetUsername( const AString & a_Username ); // tolua_export + + inline short GetPing(void) const { return m_Ping; } // tolua_export + + void SetViewDistance(int a_ViewDistance); // tolua_export + int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export + + int GetUniqueID() const { return m_UniqueID; } // tolua_export + + /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) + bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) + void AddWantedChunk(int a_ChunkX, int a_ChunkZ); + + // Calls that cProtocol descendants use to report state: + void PacketBufferFull(void); + void PacketUnknown(unsigned char a_PacketType); + void PacketError(unsigned char a_PacketType); + + // Calls that cProtocol descendants use for handling packets: + void HandleAnimation (char a_Animation); + void HandleChat (const AString & a_Message); + void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); + void HandleDisconnect (const AString & a_Reason); + void HandleEntityAction (int a_EntityID, char a_ActionID); + bool HandleHandshake (const AString & a_Username); + void HandleKeepAlive (int a_KeepAliveID); + void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); + void HandlePing (void); + void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); + void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) + void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); + void HandleRespawn (void); + void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem); + void HandleSlotSelected (short a_SlotNum); + void HandleSteerVehicle (float Forward, float Sideways); + void HandleTabCompletion (const AString & a_Text); + void HandleUpdateSign ( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, + const AString & a_Line3, const AString & a_Line4 + ); + void HandleUnmount (void); + void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick); + void HandleWindowClick (char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem); + void HandleWindowClose (char a_WindowID); + + /** Called when the protocol has finished logging the user in. + Return true to allow the user in; false to kick them. + */ + bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); + + void SendData(const char * a_Data, int a_Size); + + /// Called when the player moves into a different world; queues sreaming the new chunks + void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket); + + /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) + void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); + +private: + + int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) + + static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation + + AString m_IPString; + + int m_ProtocolVersion; + AString m_Username; + AString m_Password; + + cCriticalSection m_CSChunkLists; + cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to + cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) + + cProtocol * m_Protocol; + + cCriticalSection m_CSIncomingData; + AString m_IncomingData; + + cCriticalSection m_CSOutgoingData; + cByteBuffer m_OutgoingData; + AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily + + Vector3d m_ConfirmPosition; + + cPlayer * m_Player; + + bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction + + // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk + int m_LastStreamedChunkX; + int m_LastStreamedChunkZ; + + /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) + float m_TimeSinceLastPacket; + + short m_Ping; + int m_PingID; + long long m_PingStartTime; + long long m_LastPingTime; + static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms) + + // Values required for block dig animation + int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging + int m_BlockDigAnimSpeed; // Current speed of the animation (units ???) + int m_BlockDigAnimX; + int m_BlockDigAnimY; + int m_BlockDigAnimZ; + + // To avoid dig/aim bug in the client, store the last position given in a DIG_START packet and compare to that when processing the DIG_FINISH packet: + bool m_HasStartedDigging; + int m_LastDigBlockX; + int m_LastDigBlockY; + int m_LastDigBlockZ; + + /// Used while csDestroyedWaiting for counting the ticks until the connection is closed + int m_TicksSinceDestruction; + + enum eState + { + csConnected, ///< The client has just connected, waiting for their handshake / login + csAuthenticating, ///< The client has logged in, waiting for external authentication + csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick + csDownloadingWorld, ///< The client is waiting for chunks, we're waiting for the loader to provide and send them + csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back + csPlaying, ///< Normal gameplay + csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks + csDestroyedWaiting, ///< The client has been destroyed, but is still kept so that the Kick packet is delivered (#31) + csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread + + // TODO: Add Kicking here as well + } ; + + eState m_State; + + /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads + cCriticalSection m_CSDestroyingState; + + bool m_bKeepThreadGoing; + + /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() + bool m_ShouldCheckDownloaded; + + /// Stores the recent history of the number of explosions per tick + int m_NumExplosionsPerTick[NUM_CHECK_EXPLOSIONS_TICKS]; + + /// Points to the current tick in the m_NumExplosionsPerTick[] array + int m_CurrentExplosionTick; + + /// Running sum of m_NumExplosionsPerTick[] + int m_RunningSumExplosions; + + static int s_ClientCount; + int m_UniqueID; + + /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player + bool m_HasSentPlayerChunk; + + + + /// Returns true if the rate block interactions is within a reasonable limit (bot protection) + bool CheckBlockInteractionsRate(void); + + /// Adds a single chunk to be streamed to the client; used by StreamChunks() + void StreamChunk(int a_ChunkX, int a_ChunkZ); + + /// Handles the DIG_STARTED dig packet: + void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); + + /// Handles the DIG_FINISHED dig packet: + void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); + + // 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 +}; // tolua_export + + + + +#endif // CCLIENTHANDLE_H_INCLUDED + + + + diff --git a/src/CommandOutput.cpp b/src/CommandOutput.cpp new file mode 100644 index 000000000..c221682a1 --- /dev/null +++ b/src/CommandOutput.cpp @@ -0,0 +1,71 @@ + +// CommandOutput.cpp + +// Implements the various classes that process command output + +#include "Globals.h" +#include "CommandOutput.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCommandOutputCallback: + +void cCommandOutputCallback::Out(const char * a_Fmt, ...) +{ + AString Output; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(Output, a_Fmt, args); + va_end(args); + Output.append("\n"); + Out(Output); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLogCommandOutputCallback: + +void cLogCommandOutputCallback::Out(const AString & a_Text) +{ + m_Buffer.append(a_Text); +} + + + + + +void cLogCommandOutputCallback::Finished(void) +{ + // Log each line separately: + size_t len = m_Buffer.length(); + size_t last = 0; + for (size_t i = 0; i < len; i++) + { + switch (m_Buffer[i]) + { + case '\n': + { + LOG(m_Buffer.substr(last, i - last).c_str()); + last = i + 1; + break; + } + } + } // for i - m_Buffer[] + if (last < len) + { + LOG(m_Buffer.substr(last).c_str()); + } + + // Clear the buffer for the next command output: + m_Buffer.clear(); +} + + + + diff --git a/src/CommandOutput.h b/src/CommandOutput.h new file mode 100644 index 000000000..bdf675238 --- /dev/null +++ b/src/CommandOutput.h @@ -0,0 +1,82 @@ + +// CommandOutput.h + +// Declares various classes that process command output + + + + + +/** Interface for a callback that receives command output +The Out() function is called for any output the command has produced. +Descendants override that function to provide specific processing of the output. +*/ +class cCommandOutputCallback +{ +public: + virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses + + /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n" + void Out(const char * a_Fmt, ...); + + /// Called when the command wants to output anything; may be called multiple times + virtual void Out(const AString & a_Text) = 0; + + /// Called when the command processing has been finished + virtual void Finished(void) {}; +} ; + + + + + +/// Class that discards all command output +class cNullCommandOutputCallback : + public cCommandOutputCallback +{ + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + // Do nothing + } +} ; + + + + + + +/// Sends all command output to a log, line by line, when the command finishes processing +class cLogCommandOutputCallback : + public cCommandOutputCallback +{ +public: + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override; + virtual void Finished(void) override; + +protected: + /// Output is stored here until the command finishes processing + AString m_Buffer; +} ; + + + + + +/// Sends all command output to a log, line by line; deletes self when command finishes processing +class cLogCommandDeleteSelfOutputCallback : + public cLogCommandOutputCallback +{ + typedef cLogCommandOutputCallback super; + + virtual void Finished(void) override + { + super::Finished(); + delete this; + } +} ; + + + + diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp new file mode 100644 index 000000000..9dc471781 --- /dev/null +++ b/src/CraftingRecipes.cpp @@ -0,0 +1,770 @@ + +// CraftingRecipes.cpp + +// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes + +#include "Globals.h" +#include "CraftingRecipes.h" +#include "Root.h" +#include "PluginManager.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingGrid: + +cCraftingGrid::cCraftingGrid(int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_Items(new cItem[a_Width * a_Height]) +{ +} + + + + + +cCraftingGrid::cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_Items(new cItem[a_Width * a_Height]) +{ + for (int i = a_Width * a_Height - 1; i >= 0; i--) + { + m_Items[i] = a_Items[i]; + } +} + + + + + +cCraftingGrid::cCraftingGrid(const cCraftingGrid & a_Original) : + m_Width(a_Original.m_Width), + m_Height(a_Original.m_Height), + m_Items(new cItem[a_Original.m_Width * a_Original.m_Height]) +{ + for (int i = m_Width * m_Height - 1; i >= 0; i--) + { + m_Items[i] = a_Original.m_Items[i]; + } +} + + + + + +cCraftingGrid::~cCraftingGrid() +{ + delete[] m_Items; +} + + + + + +cItem & cCraftingGrid::GetItem(int x, int y) const +{ + // Accessible through scripting, must verify parameters: + if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) + { + LOGERROR("Attempted to get an invalid item from a crafting grid: (%d, %d), grid dimensions: (%d, %d).", + x, y, m_Width, m_Height + ); + return m_Items[0]; + } + return m_Items[x + m_Width * y]; +} + + + + + +void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) +{ + // Accessible through scripting, must verify parameters: + if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) + { + LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).", + x, y, m_Width, m_Height + ); + return; + } + + m_Items[x + m_Width * y] = cItem(a_ItemType, a_ItemCount, a_ItemHealth); +} + + + + + +void cCraftingGrid::SetItem(int x, int y, const cItem & a_Item) +{ + // Accessible through scripting, must verify parameters: + if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height)) + { + LOGERROR("Attempted to set an invalid item in a crafting grid: (%d, %d), grid dimensions: (%d, %d).", + x, y, m_Width, m_Height + ); + return; + } + + m_Items[x + m_Width * y] = a_Item; +} + + + + + +void cCraftingGrid::Clear(void) +{ + for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++) + { + m_Items[x + m_Width * y].Empty(); + } +} + + + + + +void cCraftingGrid::ConsumeGrid(const cCraftingGrid & a_Grid) +{ + if ((a_Grid.m_Width != m_Width) || (a_Grid.m_Height != m_Height)) + { + LOGWARNING("Consuming a grid of different dimensions: (%d, %d) vs (%d, %d)", + a_Grid.m_Width, a_Grid.m_Height, m_Width, m_Height + ); + } + int MinX = std::min(a_Grid.m_Width, m_Width); + int MinY = std::min(a_Grid.m_Height, m_Height); + for (int y = 0; y < MinY; y++) for (int x = 0; x < MinX; x++) + { + int ThatIdx = x + a_Grid.m_Width * y; + if (a_Grid.m_Items[ThatIdx].IsEmpty()) + { + continue; + } + int ThisIdx = x + m_Width * y; + if (a_Grid.m_Items[ThatIdx].m_ItemType != m_Items[ThisIdx].m_ItemType) + { + LOGWARNING("Consuming incompatible grids: item at (%d, %d) is %d in grid and %d in ingredients. Item not consumed.", + x, y, m_Items[ThisIdx].m_ItemType, a_Grid.m_Items[ThatIdx].m_ItemType + ); + continue; + } + char NumWantedItems = a_Grid.m_Items[ThatIdx].m_ItemCount; + if (NumWantedItems > m_Items[ThisIdx].m_ItemCount) + { + LOGWARNING("Consuming more items than there actually are in slot (%d, %d), item %d (want %d, have %d). Item zeroed out.", + x, y, m_Items[ThisIdx].m_ItemType, + NumWantedItems, m_Items[ThisIdx].m_ItemCount + ); + NumWantedItems = m_Items[ThisIdx].m_ItemCount; + } + m_Items[ThisIdx].m_ItemCount -= NumWantedItems; + if (m_Items[ThisIdx].m_ItemCount == 0) + { + m_Items[ThisIdx].Clear(); + } + } // for x, for y +} + + + + + +void cCraftingGrid::CopyToItems(cItem * a_Items) const +{ + for (int i = m_Height * m_Width - 1; i >= 0; i--) + { + a_Items[i] = m_Items[i]; + } // for x, for y +} + + + + + +void cCraftingGrid::Dump(void) +{ + for (int y = 0; y < m_Height; y++) for (int x = 0; x < m_Width; x++) + { + int idx = x + m_Width * y; + LOGD("Slot (%d, %d): Type %d, health %d, count %d", + x, y, m_Items[idx].m_ItemType, m_Items[idx].m_ItemDamage, m_Items[idx].m_ItemCount + ); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingRecipe: + +cCraftingRecipe::cCraftingRecipe(const cCraftingGrid & a_CraftingGrid) : + m_Ingredients(a_CraftingGrid) +{ +} + + + + + +void cCraftingRecipe::Clear(void) +{ + m_Ingredients.Clear(); + m_Result.Clear(); +} + + + + + +void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) +{ + m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth); +} + + + + + +void cCraftingRecipe::ConsumeIngredients(cCraftingGrid & a_CraftingGrid) +{ + a_CraftingGrid.ConsumeGrid(m_Ingredients); +} + + + + + +void cCraftingRecipe::Dump(void) +{ + LOGD("Recipe ingredients:"); + m_Ingredients.Dump(); + LOGD("Result: Type %d, health %d, count %d", + m_Result.m_ItemType, m_Result.m_ItemDamage, m_Result.m_ItemCount + ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingRecipes: + +cCraftingRecipes::cCraftingRecipes(void) +{ + LoadRecipes(); +} + + + + + +cCraftingRecipes::~cCraftingRecipes() +{ + ClearRecipes(); +} + + + + + +void cCraftingRecipes::GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe) +{ + // Allow plugins to intercept recipes using a pre-craft hook: + if (cRoot::Get()->GetPluginManager()->CallHookPreCrafting(a_Player, &a_CraftingGrid, &a_Recipe)) + { + return; + } + + // Built-in recipes: + std::auto_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight())); + a_Recipe.Clear(); + if (Recipe.get() == NULL) + { + // Allow plugins to intercept a no-recipe-found situation: + cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, &a_CraftingGrid, &a_Recipe); + return; + } + for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr) + { + a_Recipe.SetIngredient(itr->x, itr->y, itr->m_Item); + } // for itr + a_Recipe.SetResult(Recipe->m_Result); + + // Allow plugins to intercept recipes after they are processed: + cRoot::Get()->GetPluginManager()->CallHookPostCrafting(a_Player, &a_CraftingGrid, &a_Recipe); +} + + + + + +void cCraftingRecipes::LoadRecipes(void) +{ + LOGD("Loading crafting recipes from crafting.txt..."); + ClearRecipes(); + + // Load the crafting.txt file: + cFile f; + if (!f.Open("crafting.txt", cFile::fmRead)) + { + LOGWARNING("Cannot open file \"crafting.txt\", no crafting recipes will be available!"); + return; + } + AString Everything; + f.ReadRestOfFile(Everything); + f.Close(); + + // Split it into lines, then process each line as a single recipe: + AStringVector Split = StringSplit(Everything, "\n"); + int LineNum = 1; + for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr, ++LineNum) + { + // Remove anything after a '#' sign and trim away the whitespace: + AString Recipe = TrimString(itr->substr(0, itr->find('#'))); + if (Recipe.empty()) + { + // Empty recipe + continue; + } + AddRecipeLine(LineNum, Recipe); + } // for itr - Split[] + LOG("Loaded %d crafting recipes", m_Recipes.size()); +} + + + + +void cCraftingRecipes::ClearRecipes(void) +{ + for (cRecipes::iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) + { + delete *itr; + } + m_Recipes.clear(); +} + + + + + +void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine) +{ + AStringVector Sides = StringSplit(a_RecipeLine, "="); + if (Sides.size() != 2) + { + LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + + std::auto_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe); + + // Parse the result: + AStringVector ResultSplit = StringSplit(Sides[0], ","); + if (ResultSplit.empty()) + { + LOGWARNING("crafting.txt: line %d: Result is empty, ignoring the recipe.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + if (!ParseItem(ResultSplit[0], Recipe->m_Result)) + { + LOGWARNING("crafting.txt: line %d: Cannot parse result item, ignoring the recipe.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + if (ResultSplit.size() > 1) + { + Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str()); + if (Recipe->m_Result.m_ItemCount == 0) + { + LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + } + else + { + Recipe->m_Result.m_ItemCount = 1; + } + + // Parse each ingredient: + AStringVector Ingredients = StringSplit(Sides[1], "|"); + int Num = 1; + for (AStringVector::const_iterator itr = Ingredients.begin(); itr != Ingredients.end(); ++itr, ++Num) + { + if (!ParseIngredient(*itr, Recipe.get())) + { + LOGWARNING("crafting.txt: line %d: Cannot parse ingredient #%d, ignoring the recipe.", a_LineNum, Num); + LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str()); + return; + } + } // for itr - Ingredients[] + + NormalizeIngredients(Recipe.get()); + + m_Recipes.push_back(Recipe.release()); +} + + + + + +bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item) +{ + // The caller provides error logging + + AStringVector Split = StringSplit(a_String, "^"); + if (Split.empty()) + { + return false; + } + + if (!StringToItem(Split[0], a_Item)) + { + return false; + } + + if (Split.size() > 1) + { + AString Damage = TrimString(Split[1]); + a_Item.m_ItemDamage = atoi(Damage.c_str()); + if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0)) + { + // Parsing the number failed + return false; + } + } + + // Success + return true; +} + + + + + +bool cCraftingRecipes::ParseIngredient(const AString & a_String, cRecipe * a_Recipe) +{ + // a_String is in this format: "ItemType^damage, X:Y, X:Y, X:Y..." + AStringVector Split = StringSplit(a_String, ","); + if (Split.size() < 2) + { + // Not enough split items + return false; + } + cItem Item; + if (!ParseItem(Split[0], Item)) + { + return false; + } + Item.m_ItemCount = 1; + + cCraftingRecipes::cRecipeSlots TempSlots; + for (AStringVector::const_iterator itr = Split.begin() + 1; itr != Split.end(); ++itr) + { + // Parse the coords in the split item: + AStringVector Coords = StringSplit(*itr, ":"); + if ((Coords.size() == 1) && (TrimString(Coords[0]) == "*")) + { + cCraftingRecipes::cRecipeSlot Slot; + Slot.m_Item = Item; + Slot.x = -1; + Slot.y = -1; + TempSlots.push_back(Slot); + continue; + } + if (Coords.size() != 2) + { + return false; + } + Coords[0] = TrimString(Coords[0]); + Coords[1] = TrimString(Coords[1]); + if (Coords[0].empty() || Coords[1].empty()) + { + return false; + } + cCraftingRecipes::cRecipeSlot Slot; + Slot.m_Item = Item; + switch (Coords[0][0]) + { + case '1': Slot.x = 0; break; + case '2': Slot.x = 1; break; + case '3': Slot.x = 2; break; + case '*': Slot.x = -1; break; + default: + { + return false; + } + } + switch (Coords[1][0]) + { + case '1': Slot.y = 0; break; + case '2': Slot.y = 1; break; + case '3': Slot.y = 2; break; + case '*': Slot.y = -1; break; + default: + { + return false; + } + } + TempSlots.push_back(Slot); + } // for itr - Split[] + + // Append the ingredients: + a_Recipe->m_Ingredients.insert(a_Recipe->m_Ingredients.end(), TempSlots.begin(), TempSlots.end()); + return true; +} + + + + + +void cCraftingRecipes::NormalizeIngredients(cCraftingRecipes::cRecipe * a_Recipe) +{ + // Calculate the minimum coords for ingredients, excluding the "anywhere" items: + int MinX = MAX_GRID_WIDTH, MaxX = 0; + int MinY = MAX_GRID_HEIGHT, MaxY = 0; + for (cRecipeSlots::const_iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) + { + if (itr->x >= 0) + { + MinX = std::min(itr->x, MinX); + MaxX = std::max(itr->x, MaxX); + } + if (itr->y >= 0) + { + MinY = std::min(itr->y, MinY); + MaxY = std::max(itr->y, MaxY); + } + } // for itr - a_Recipe->m_Ingredients[] + + // Move ingredients so that the minimum coords are 0:0 + for (cRecipeSlots::iterator itr = a_Recipe->m_Ingredients.begin(); itr != a_Recipe->m_Ingredients.end(); ++itr) + { + if (itr->x >= 0) + { + itr->x -= MinX; + } + if (itr->y >= 0) + { + itr->y -= MinY; + } + } // for itr - a_Recipe->m_Ingredients[] + a_Recipe->m_Width = std::max(MaxX - MinX + 1, 1); + a_Recipe->m_Height = std::max(MaxY - MinY + 1, 1); + + // TODO: Compress two same ingredients with the same coords into a single ingredient with increased item count +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight) +{ + ASSERT(a_GridWidth <= MAX_GRID_WIDTH); + ASSERT(a_GridHeight <= MAX_GRID_HEIGHT); + + // Get the real bounds of the crafting grid: + int GridLeft = MAX_GRID_WIDTH, GridTop = MAX_GRID_HEIGHT; + int GridRight = 0, GridBottom = 0; + for (int y = 0; y < a_GridHeight; y++ ) for(int x = 0; x < a_GridWidth; x++) + { + if (!a_CraftingGrid[x + y * a_GridWidth].IsEmpty()) + { + GridRight = std::max(x, GridRight); + GridBottom = std::max(y, GridBottom); + GridLeft = std::min(x, GridLeft); + GridTop = std::min(y, GridTop); + } + } + int GridWidth = GridRight - GridLeft + 1; + int GridHeight = GridBottom - GridTop + 1; + + // Search in the possibly minimized grid, but keep the stride: + const cItem * Grid = a_CraftingGrid + GridLeft + (a_GridWidth * GridTop); + cRecipe * Recipe = FindRecipeCropped(Grid, GridWidth, GridHeight, a_GridWidth); + if (Recipe == NULL) + { + return NULL; + } + + // A recipe has been found, move it to correspond to the original crafting grid: + for (cRecipeSlots::iterator itrS = Recipe->m_Ingredients.begin(); itrS != Recipe->m_Ingredients.end(); ++itrS) + { + itrS->x += GridLeft; + itrS->y += GridTop; + } // for itrS - Recipe->m_Ingredients[] + + return Recipe; +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride) +{ + for (cRecipes::const_iterator itr = m_Recipes.begin(); itr != m_Recipes.end(); ++itr) + { + // Both the crafting grid and the recipes are normalized. The only variable possible is the "anywhere" items. + // This still means that the "anywhere" item may be the one that is offsetting the grid contents to the right or downwards, so we need to check all possible positions. + // E. g. recipe "A, * | B, 1:1 | ..." still needs to check grid for B at 2:2 (in case A was in grid's 1:1) + // Calculate the maximum offsets for this recipe relative to the grid size, and iterate through all combinations of offsets. + // Also, this calculation automatically filters out recipes that are too large for the current grid - the loop won't be entered at all. + + int MaxOfsX = a_GridWidth - (*itr)->m_Width; + int MaxOfsY = a_GridHeight - (*itr)->m_Height; + for (int x = 0; x <= MaxOfsX; x++) for (int y = 0; y <= MaxOfsY; y++) + { + cRecipe * Recipe = MatchRecipe(a_CraftingGrid, a_GridWidth, a_GridHeight, a_GridStride, *itr, x, y); + if (Recipe != NULL) + { + return Recipe; + } + } // for y, for x + } // for itr - m_Recipes[] + + // No matching recipe found + return NULL; +} + + + + + +cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY) +{ + // Check the regular items first: + bool HasMatched[MAX_GRID_WIDTH][MAX_GRID_HEIGHT]; + memset(HasMatched, 0, sizeof(HasMatched)); + for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) + { + if ((itrS->x < 0) || (itrS->y < 0)) + { + // "Anywhere" item, process later + continue; + } + ASSERT(itrS->x + a_OffsetX < a_GridWidth); + ASSERT(itrS->y + a_OffsetY < a_GridHeight); + int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY); + if ( + (itrS->x >= a_GridWidth) || + (itrS->y >= a_GridHeight) || + (itrS->m_Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type? + (itrS->m_Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items + ( + (itrS->m_Item.m_ItemDamage > 0) && // should compare damage values? + (itrS->m_Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage) + ) + ) + { + // Doesn't match + return NULL; + } + HasMatched[itrS->x + a_OffsetX][itrS->y + a_OffsetY] = true; + } // for itrS - Recipe->m_Ingredients[] + + // Process the "Anywhere" items now, and only in the cells that haven't matched yet + // The "anywhere" items are processed on a first-come-first-served basis. + // Do not use a recipe with one horizontal and one vertical "anywhere" ("*:1, 1:*") as it may not match properly! + cRecipeSlots MatchedSlots; // Stores the slots of "anywhere" items that have matched, with the match coords + for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) + { + if ((itrS->x >= 0) && (itrS->y >= 0)) + { + // Regular item, already processed + continue; + } + int StartX = 0, EndX = a_GridWidth - 1; + int StartY = 0, EndY = a_GridHeight - 1; + if (itrS->x >= 0) + { + StartX = itrS->x; + EndX = itrS->x; + } + else if (itrS->y >= 0) + { + StartY = itrS->y; + EndY = itrS->y; + } + bool Found = false; + for (int x = StartX; x <= EndX; x++) + { + for (int y = StartY; y <= EndY; y++) + { + if (HasMatched[x][y]) + { + // Already matched some other item + continue; + } + int GridIdx = x + a_GridStride * y; + if ( + (a_CraftingGrid[GridIdx].m_ItemType == itrS->m_Item.m_ItemType) && + ( + (itrS->m_Item.m_ItemDamage < 0) || // doesn't want damage comparison + (itrS->m_Item.m_ItemDamage == a_CraftingGrid[GridIdx].m_ItemDamage) // the damage matches + ) + ) + { + HasMatched[x][y] = true; + Found = true; + MatchedSlots.push_back(*itrS); + MatchedSlots.back().x = x; + MatchedSlots.back().y = y; + break; + } + } // for y + if (Found) + { + break; + } + } // for x + if (!Found) + { + return NULL; + } + } // for itrS - a_Recipe->m_Ingredients[] + + // Check if the whole grid has matched: + for (int x = 0; x < a_GridWidth; x++) for (int y = 0; y < a_GridHeight; y++) + { + if (!HasMatched[x][y] && !a_CraftingGrid[x + a_GridStride * y].IsEmpty()) + { + // There's an unmatched item in the grid + return NULL; + } + } // for y, for x + + // The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid: + std::auto_ptr<cRecipe> Recipe(new cRecipe); + Recipe->m_Result = a_Recipe->m_Result; + Recipe->m_Width = a_Recipe->m_Width; + Recipe->m_Height = a_Recipe->m_Height; + for (cRecipeSlots::const_iterator itrS = a_Recipe->m_Ingredients.begin(); itrS != a_Recipe->m_Ingredients.end(); ++itrS) + { + if ((itrS->x < 0) || (itrS->y < 0)) + { + // "Anywhere" item, process later + continue; + } + Recipe->m_Ingredients.push_back(*itrS); + } + Recipe->m_Ingredients.insert(Recipe->m_Ingredients.end(), MatchedSlots.begin(), MatchedSlots.end()); + return Recipe.release(); +} + + + + diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h new file mode 100644 index 000000000..9d92cbfab --- /dev/null +++ b/src/CraftingRecipes.h @@ -0,0 +1,172 @@ + +// CraftingRecipes.h + +// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes + + + + +#pragma once + +#include "Item.h" + + + + + +// fwd: cPlayer.h +class cPlayer; + + + + + +class cCraftingGrid // tolua_export +{ // tolua_export +public: + cCraftingGrid(const cCraftingGrid & a_Original); + cCraftingGrid(int a_Width, int a_Height); // tolua_export + cCraftingGrid(const cItem * a_Items, int a_Width, int a_Height); + ~cCraftingGrid(); + + // tolua_begin + int GetWidth (void) const {return m_Width; } + int GetHeight(void) const {return m_Height; } + cItem & GetItem (int x, int y) const; + void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); + void SetItem (int x, int y, const cItem & a_Item); + void Clear (void); + + /// Removes items in a_Grid from m_Items[] (used by cCraftingRecipe::ConsumeIngredients()) + void ConsumeGrid(const cCraftingGrid & a_Grid); + + /// Dumps the entire crafting grid using LOGD() + void Dump(void); + + // tolua_end + + cItem * GetItems(void) const {return m_Items; } + + /// Copies internal contents into the item array specified. Assumes that the array has the same dimensions as self + void CopyToItems(cItem * a_Items) const; + +protected: + + int m_Width; + int m_Height; + cItem * m_Items; +} ; // tolua_export + + + + + +class cCraftingRecipe // tolua_export +{ // tolua_export +public: + cCraftingRecipe(const cCraftingGrid & a_CraftingGrid); + + // tolua_begin + void Clear (void); + int GetIngredientsWidth (void) const {return m_Ingredients.GetWidth(); } + int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); } + cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); } + const cItem & GetResult (void) const {return m_Result; } + void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth); + void SetResult (const cItem & a_Item) + { + m_Result = a_Item; + } + + void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth) + { + m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth); + } + + void SetIngredient (int x, int y, const cItem & a_Item) + { + m_Ingredients.SetItem(x, y, a_Item); + } + + /// Consumes ingredients from the crafting grid specified + void ConsumeIngredients(cCraftingGrid & a_CraftingGrid); + + /// Dumps the entire recipe using LOGD() + void Dump(void); + // tolua_end + +protected: + + cCraftingGrid m_Ingredients; // Adjusted to correspond to the input crafting grid! + cItem m_Result; +} ; // tolua_export + + + + + +class cCraftingRecipes +{ +public: + static const int MAX_GRID_WIDTH = 3; + static const int MAX_GRID_HEIGHT = 3; + + cCraftingRecipes(void); + ~cCraftingRecipes(); + + /// Returns the recipe for current crafting grid. Doesn't modify the grid. Clears a_Recipe if no recipe found. + void GetRecipe(const cPlayer * a_Player, const cCraftingGrid & a_CraftingGrid, cCraftingRecipe & a_Recipe); + +protected: + + struct cRecipeSlot + { + cItem m_Item; + int x, y; // 1..3, or -1 for "any" + } ; + typedef std::vector<cRecipeSlot> cRecipeSlots; + + /** A single recipe, stored. Each recipe is normalized right after parsing (NormalizeIngredients()) + A normalized recipe starts at (0,0) + */ + struct cRecipe + { + cRecipeSlots m_Ingredients; + cItem m_Result; + + // Size of the regular items in the recipe; "anywhere" items are excluded: + int m_Width; + int m_Height; + } ; + typedef std::vector<cRecipe *> cRecipes; + + cRecipes m_Recipes; + + void LoadRecipes(void); + void ClearRecipes(void); + + /// Parses the recipe line and adds it into m_Recipes. a_LineNum is used for diagnostic warnings only + void AddRecipeLine(int a_LineNum, const AString & a_RecipeLine); + + /// Parses an item string in the format "<ItemType>[^<Damage>]", returns true if successful. + bool ParseItem(const AString & a_String, cItem & a_Item); + + /// Parses one ingredient and adds it to the specified recipe. Returns true if successful. + bool ParseIngredient(const AString & a_String, cRecipe * a_Recipe); + + /// Moves the recipe to top-left corner, sets its MinWidth / MinHeight + void NormalizeIngredients(cRecipe * a_Recipe); + + /// Finds a recipe matching the crafting grid. Returns a newly allocated recipe (with all its coords set) or NULL if not found. Caller must delete return value! + cRecipe * FindRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight); + + /// Same as FindRecipe, but the grid is guaranteed to be of minimal dimensions needed + cRecipe * FindRecipeCropped(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride); + + /// Checks if the grid matches the specified recipe, offset by the specified offsets. Returns a matched cRecipe * if so, or NULL if not matching. Caller must delete the return value! + cRecipe * MatchRecipe(const cItem * a_CraftingGrid, int a_GridWidth, int a_GridHeight, int a_GridStride, const cRecipe * a_Recipe, int a_OffsetX, int a_OffsetY); +} ; + + + + diff --git a/src/Cuboid.cpp b/src/Cuboid.cpp new file mode 100644 index 000000000..ea6f7c453 --- /dev/null +++ b/src/Cuboid.cpp @@ -0,0 +1,117 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cuboid.h" + + + + + +/// Returns true if the two specified intervals have a non-empty union +static bool DoIntervalsIntersect(int a_Min1, int a_Max1, int a_Min2, int a_Max2) +{ + return ( + ((a_Min1 >= a_Min2) && (a_Min1 <= a_Max2)) || // Start of first interval is within the second interval + ((a_Max1 >= a_Min2) && (a_Max1 <= a_Max2)) || // End of first interval is within the second interval + ((a_Min2 >= a_Min1) && (a_Min2 <= a_Max1)) // Start of second interval is within the first interval + ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCuboid: + +void cCuboid::Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) +{ + p1.x = a_X1; + p1.y = a_Y1; + p1.z = a_Z1; + p2.x = a_X2; + p2.y = a_Y2; + p2.z = a_Z2; +} + + + + + +void cCuboid::Sort(void) +{ + if (p1.x > p2.x) + { + std::swap(p1.x, p2.x); + } + if (p1.y > p2.y) + { + std::swap(p1.y, p2.y); + } + if (p1.z > p2.z) + { + std::swap(p1.z, p2.z); + } +} + + + + + +bool cCuboid::DoesIntersect(const cCuboid & a_Other) const +{ + // In order for cuboids to intersect, each of their coord intervals need to intersect + return ( + DoIntervalsIntersect(p1.x, p2.x, a_Other.p1.x, a_Other.p2.x) && + DoIntervalsIntersect(p1.y, p2.y, a_Other.p1.y, a_Other.p2.y) && + DoIntervalsIntersect(p1.z, p2.z, a_Other.p1.z, a_Other.p2.z) + ); +} + + + + + +bool cCuboid::IsCompletelyInside(const cCuboid & a_Outer) const +{ + return ( + (p1.x >= a_Outer.p1.x) && + (p2.x <= a_Outer.p2.x) && + (p1.y >= a_Outer.p1.y) && + (p2.y <= a_Outer.p2.y) && + (p1.z >= a_Outer.p1.z) && + (p2.z <= a_Outer.p2.z) + ); +} + + + + + +void cCuboid::Move(int a_OfsX, int a_OfsY, int a_OfsZ) +{ + p1.x += a_OfsX; + p1.y += a_OfsY; + p1.z += a_OfsZ; + p2.x += a_OfsX; + p2.y += a_OfsY; + p2.z += a_OfsZ; +} + + + + + + +bool cCuboid::IsSorted(void) const +{ + return ( + (p1.x <= p2.x) && + (p1.y <= p2.y) && + (p1.z <= p2.z) + ); +} + + + + diff --git a/src/Cuboid.h b/src/Cuboid.h new file mode 100644 index 000000000..44db7b98e --- /dev/null +++ b/src/Cuboid.h @@ -0,0 +1,75 @@ + +#pragma once + +#include "Vector3i.h" +#include "Vector3d.h" + + + + + +// tolua_begin +class cCuboid +{ +public: + // p1 is expected to have the smaller of the coords; Sort() swaps coords to match this + Vector3i p1, p2; + + cCuboid(void) {} + cCuboid(const cCuboid & a_Cuboid ) : p1(a_Cuboid.p1), p2(a_Cuboid.p2) {} + cCuboid(const Vector3i & a_p1, const Vector3i & a_p2) : p1(a_p1), p2(a_p2) {} + cCuboid(int a_X1, int a_Y1, int a_Z1) : p1(a_X1, a_Y1, a_Z1), p2(a_X1, a_Y1, a_Z1) {} + cCuboid(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2) : p1(a_X1, a_Y1, a_Z1), p2(a_X2, a_Y2, a_Z2) {} + + void Assign(int a_X1, int a_Y1, int a_Z1, int a_X2, int a_Y2, int a_Z2); + + void Sort(void); + + int DifX(void) const { return p2.x - p1.x; } + int DifY(void) const { return p2.y - p1.y; } + int DifZ(void) const { return p2.z - p1.z; } + + /// Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. + bool DoesIntersect(const cCuboid & a_Other) const; + + bool IsInside(const Vector3i & v) const + { + return ( + (v.x >= p1.x) && (v.x <= p2.x) && + (v.y >= p1.y) && (v.y <= p2.y) && + (v.z >= p1.z) && (v.z <= p2.z) + ); + } + + bool IsInside(int a_X, int a_Y, int a_Z) const + { + return ( + (a_X >= p1.x) && (a_X <= p2.x) && + (a_Y >= p1.y) && (a_Y <= p2.y) && + (a_Z >= p1.z) && (a_Z <= p2.z) + ); + } + + bool IsInside( const Vector3d & v ) const + { + return ( + (v.x >= p1.x) && (v.x <= p2.x) && + (v.y >= p1.y) && (v.y <= p2.y) && + (v.z >= p1.z) && (v.z <= p2.z) + ); + } + + /// Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) + bool IsCompletelyInside(const cCuboid & a_Outer) const; + + /// Moves the cuboid by the specified offsets in each direction + void Move(int a_OfsX, int a_OfsY, int a_OfsZ); + + /// Returns true if the coords are properly sorted (lesser in p1, greater in p2) + bool IsSorted(void) const; +} ; +// tolua_end + + + + diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp new file mode 100644 index 000000000..c774c9dce --- /dev/null +++ b/src/DeadlockDetect.cpp @@ -0,0 +1,147 @@ + +// DeadlockDetect.cpp + +// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one + +#include "Globals.h" +#include "DeadlockDetect.h" +#include "Root.h" +#include "World.h" + + + + + +/// Number of milliseconds per cycle +const int CYCLE_MILLISECONDS = 100; + +/// When the number of cycles for the same world age hits this value, it is considered a deadlock +const int NUM_CYCLES_LIMIT = 200; // 200 = twenty seconds + + + + + +cDeadlockDetect::cDeadlockDetect(void) : + super("DeadlockDetect") +{ +} + + + + + +bool cDeadlockDetect::Start(void) +{ + // Read the initial world data: + class cFillIn : + public cWorldListCallback + { + public: + cFillIn(cDeadlockDetect * a_Detect) : + m_Detect(a_Detect) + { + } + + virtual bool Item(cWorld * a_World) override + { + m_Detect->SetWorldAge(a_World->GetName(), a_World->GetWorldAge()); + return false; + } + + protected: + cDeadlockDetect * m_Detect; + } FillIn(this); + cRoot::Get()->ForEachWorld(FillIn); + return super::Start(); +} + + + + + +void cDeadlockDetect::Execute(void) +{ + // Loop until the signal to terminate: + while (!m_ShouldTerminate) + { + // Check the world ages: + class cChecker : + public cWorldListCallback + { + public: + cChecker(cDeadlockDetect * a_Detect) : + m_Detect(a_Detect) + { + } + + protected: + cDeadlockDetect * m_Detect; + + virtual bool Item(cWorld * a_World) override + { + m_Detect->CheckWorldAge(a_World->GetName(), a_World->GetWorldAge()); + return false; + } + } Checker(this); + cRoot::Get()->ForEachWorld(Checker); + + cSleep::MilliSleep(CYCLE_MILLISECONDS); + } // while (should run) +} + + + + + +void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age) +{ + m_WorldAges[a_WorldName].m_Age = a_Age; + m_WorldAges[a_WorldName].m_NumCyclesSame = 0; +} + + + + + +void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age) +{ + WorldAges::iterator itr = m_WorldAges.find(a_WorldName); + if (itr == m_WorldAges.end()) + { + ASSERT(!"Unknown world in cDeadlockDetect"); + return; + } + if (itr->second.m_Age == a_Age) + { + itr->second.m_NumCyclesSame += 1; + if (itr->second.m_NumCyclesSame > NUM_CYCLES_LIMIT) + { + DeadlockDetected(); + return; + } + } + else + { + itr->second.m_Age = a_Age; + itr->second.m_NumCyclesSame = 0; + } +} + + + + + +void cDeadlockDetect::DeadlockDetected(void) +{ + ASSERT(!"Deadlock detected"); + + // TODO: Make a crashdump / coredump + + // Crash the server intentionally: + *((volatile int *)0) = 0; +} + + + + diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h new file mode 100644 index 000000000..2559c3fff --- /dev/null +++ b/src/DeadlockDetect.h @@ -0,0 +1,65 @@ + +// DeadlockDetect.h + +// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one + +/* +This class simply monitors each world's m_WorldAge, which is expected to grow on each tick. +If the world age doesn't grow for several seconds, it's either because the server is super-overloaded, +or because the world tick thread hangs in a deadlock. We presume the latter and therefore kill the server. +Once we learn to write crashdumps programmatically, we should do so just before killing, to enable debugging. +*/ + + + +#pragma once + +#include "OSSupport/IsThread.h" + + + + + +class cDeadlockDetect : + public cIsThread +{ + typedef cIsThread super; + +public: + cDeadlockDetect(void); + + /// Starts the detection. Hides cIsThread's Start, because we need some initialization + bool Start(void); + +protected: + struct sWorldAge + { + /// Last m_WorldAge that has been detected in this world + Int64 m_Age; + + /// Number of cycles for which the age has been the same + int m_NumCyclesSame; + } ; + + /// Maps world name -> sWorldAge + typedef std::map<AString, sWorldAge> WorldAges; + + WorldAges m_WorldAges; + + + // cIsThread overrides: + virtual void Execute(void) override; + + /// Sets the initial world age + void SetWorldAge(const AString & a_WorldName, Int64 a_Age); + + /// Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected + void CheckWorldAge(const AString & a_WorldName, Int64 a_Age); + + /// Called when a deadlock is detected. Aborts the server. + void DeadlockDetected(void); +} ; + + + + diff --git a/src/Defines.h b/src/Defines.h new file mode 100644 index 000000000..06410cab4 --- /dev/null +++ b/src/Defines.h @@ -0,0 +1,562 @@ + +#pragma once + + + + + +typedef unsigned char Byte; + +/// List of slot numbers, used for inventory-painting +typedef std::vector<int> cSlotNums; + + + + + + +// tolua_begin + +/// How much light do the blocks emit on their own? +extern unsigned char g_BlockLightValue[]; + +/// How much light do the block consume? +extern unsigned char g_BlockSpreadLightFalloff[]; + +/// Is a block completely transparent? (light doesn't get decreased(?)) +extern bool g_BlockTransparent[]; + +/// Is a block destroyed after a single hit? +extern bool g_BlockOneHitDig[]; + +/// Can a piston break this block? +extern bool g_BlockPistonBreakable[256]; + +/// Can this block hold snow atop? +extern bool g_BlockIsSnowable[256]; + +/// Does this block require a tool to drop? +extern bool g_BlockRequiresSpecialTool[256]; + +/// Is this block solid (player cannot walk through)? +extern bool g_BlockIsSolid[256]; + +/// Can torches be placed on this block? +extern bool g_BlockIsTorchPlaceable[256]; + +/// Experience Orb setup +enum +{ + //open to suggestion on naming convention here :) + MAX_EXPERIENCE_ORB_SIZE = 2000 +} ; + + + + + +/// Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc +enum eBlockFace +{ + BLOCK_FACE_NONE = -1, // Interacting with no block face - swinging the item in the air + BLOCK_FACE_XM = 4, // Interacting with the X- face of the block + BLOCK_FACE_XP = 5, // Interacting with the X+ face of the block + BLOCK_FACE_YM = 0, // Interacting with the Y- face of the block + BLOCK_FACE_YP = 1, // Interacting with the Y+ face of the block + BLOCK_FACE_ZM = 2, // Interacting with the Z- face of the block + BLOCK_FACE_ZP = 3, // Interacting with the Z+ face of the block + + // Synonyms using the (deprecated) world directions: + BLOCK_FACE_BOTTOM = BLOCK_FACE_YM, // Interacting with the bottom face of the block + BLOCK_FACE_TOP = BLOCK_FACE_YP, // Interacting with the top face of the block + BLOCK_FACE_NORTH = BLOCK_FACE_ZM, // Interacting with the northern face of the block + BLOCK_FACE_SOUTH = BLOCK_FACE_ZP, // Interacting with the southern face of the block + BLOCK_FACE_WEST = BLOCK_FACE_XM, // Interacting with the western face of the block + BLOCK_FACE_EAST = BLOCK_FACE_XP, // Interacting with the eastern face of the block +} ; + + + + + +/// PlayerDigging status constants +enum +{ + DIG_STATUS_STARTED = 0, + DIG_STATUS_CANCELLED = 1, + DIG_STATUS_FINISHED = 2, + DIG_STATUS_DROP_HELD = 4, + DIG_STATUS_SHOOT_EAT = 5, +} ; + + + + + +/// Individual actions sent in the WindowClick packet +enum eClickAction +{ + // Sorted by occurrence in the 1.5 protocol + caLeftClick, + caRightClick, + caShiftLeftClick, + caShiftRightClick, + caNumber1, + caNumber2, + caNumber3, + caNumber4, + caNumber5, + caNumber6, + caNumber7, + caNumber8, + caNumber9, + caMiddleClick, + caDropKey, + caCtrlDropKey, + caLeftClickOutside, + caRightClickOutside, + caLeftClickOutsideHoldNothing, + caRightClickOutsideHoldNothing, + caLeftPaintBegin, + caRightPaintBegin, + caLeftPaintProgress, + caRightPaintProgress, + caLeftPaintEnd, + caRightPaintEnd, + caDblClick, + // Add new actions here + caUnknown = 255, + + // Keep this list in sync with ClickActionToString() function below! +} ; + + + + + +enum eGameMode +{ + eGameMode_NotSet = -1, + eGameMode_Survival = 0, + eGameMode_Creative = 1, + eGameMode_Adventure = 2, + + // Easier-to-use synonyms: + gmNotSet = eGameMode_NotSet, + gmSurvival = eGameMode_Survival, + gmCreative = eGameMode_Creative, + gmAdventure = eGameMode_Adventure, + + // These two are used to check GameMode for validity when converting from integers. + gmMax, // Gets automatically assigned + gmMin = 0, +} ; + + + + + +enum eWeather +{ + eWeather_Sunny = 0, + eWeather_Rain = 1, + eWeather_ThunderStorm = 2, + + // Easier-to-use synonyms: + wSunny = eWeather_Sunny, + wRain = eWeather_Rain, + wThunderstorm = eWeather_ThunderStorm, + wStorm = wThunderstorm, +} ; + + + + + +inline const char * ClickActionToString(eClickAction a_ClickAction) +{ + switch (a_ClickAction) + { + case caLeftClick: return "caLeftClick"; + case caRightClick: return "caRightClick"; + case caShiftLeftClick: return "caShiftLeftClick"; + case caShiftRightClick: return "caShiftRightClick"; + case caNumber1: return "caNumber1"; + case caNumber2: return "caNumber2"; + case caNumber3: return "caNumber3"; + case caNumber4: return "caNumber4"; + case caNumber5: return "caNumber5"; + case caNumber6: return "caNumber6"; + case caNumber7: return "caNumber7"; + case caNumber8: return "caNumber8"; + case caNumber9: return "caNumber9"; + case caMiddleClick: return "caMiddleClick"; + case caDropKey: return "caDropKey"; + case caCtrlDropKey: return "caCtrlDropKey"; + case caLeftClickOutside: return "caLeftClickOutside"; + case caRightClickOutside: return "caRightClickOutside"; + case caLeftClickOutsideHoldNothing: return "caLeftClickOutsideHoldNothing"; + case caRightClickOutsideHoldNothing: return "caRightClickOutsideHoldNothing"; + case caLeftPaintBegin: return "caLeftPaintBegin"; + case caRightPaintBegin: return "caRightPaintBegin"; + case caLeftPaintProgress: return "caLeftPaintProgress"; + case caRightPaintProgress: return "caRightPaintProgress"; + case caLeftPaintEnd: return "caLeftPaintEnd"; + case caRightPaintEnd: return "caRightPaintEnd"; + case caDblClick: return "caDblClick"; + + case caUnknown: return "caUnknown"; + } + ASSERT(!"Unknown click action"); + return "caUnknown"; +} + + + + + +inline bool IsValidBlock(int a_BlockType) +{ + if ( + (a_BlockType > -1) && + (a_BlockType <= E_BLOCK_MAX_TYPE_ID) + ) + { + return true; + } + return false; +} + + + + + +inline bool IsValidItem(int a_ItemType) +{ + if ( + ((a_ItemType >= E_ITEM_FIRST) && (a_ItemType <= E_ITEM_MAX_CONSECUTIVE_TYPE_ID)) || // Basic items range + ((a_ItemType >= E_ITEM_FIRST_DISC) && (a_ItemType <= E_ITEM_LAST_DISC)) // Music discs' special range + ) + { + return true; + } + + if (a_ItemType == 0) + { + return false; + } + + return IsValidBlock(a_ItemType); +} + +// tolua_end + + + + + +inline bool IsBlockWater(BLOCKTYPE a_BlockType) +{ + return ((a_BlockType == E_BLOCK_WATER) || (a_BlockType == E_BLOCK_STATIONARY_WATER)); +} + + + + + +inline bool IsBlockLava(BLOCKTYPE a_BlockType) +{ + return ((a_BlockType == E_BLOCK_LAVA) || (a_BlockType == E_BLOCK_STATIONARY_LAVA)); +} + + + + + +inline bool IsBlockLiquid(BLOCKTYPE a_BlockType) +{ + return IsBlockWater(a_BlockType) || IsBlockLava(a_BlockType); +} + + + + + +inline bool IsBlockTypeOfDirt(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + return true; + } + } + return false; +} + + + + +inline void AddFaceDirection(int & a_BlockX, int & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) // tolua_export +{ // tolua_export + if (!a_bInverse) + { + switch (a_BlockFace) + { + case BLOCK_FACE_YP: a_BlockY++; break; + case BLOCK_FACE_YM: a_BlockY--; break; + case BLOCK_FACE_ZM: a_BlockZ--; break; + case BLOCK_FACE_ZP: a_BlockZ++; break; + case BLOCK_FACE_XP: a_BlockX++; break; + case BLOCK_FACE_XM: a_BlockX--; break; + default: + { + LOGWARNING("%s: Unknown face: %d", __FUNCTION__, a_BlockFace); + ASSERT(!"AddFaceDirection(): Unknown face"); + break; + } + } + } + else + { + switch (a_BlockFace) + { + case BLOCK_FACE_YP: a_BlockY--; break; + case BLOCK_FACE_YM: a_BlockY++; break; + case BLOCK_FACE_ZM: a_BlockZ++; break; + case BLOCK_FACE_ZP: a_BlockZ--; break; + case BLOCK_FACE_XP: a_BlockX--; break; + case BLOCK_FACE_XM: a_BlockX++; break; + default: + { + LOGWARNING("%s: Unknown inv face: %d", __FUNCTION__, a_BlockFace); + ASSERT(!"AddFaceDirection(): Unknown face"); + break; + } + } + } +} // tolua_export + + + + + +inline void AddFaceDirection(int & a_BlockX, unsigned char & a_BlockY, int & a_BlockZ, char a_BlockFace, bool a_bInverse = false) +{ + int Y = a_BlockY; + AddFaceDirection(a_BlockX, Y, a_BlockZ, a_BlockFace, a_bInverse); + if (Y < 0) + { + a_BlockY = 0; + } + else if (Y > 255) + { + a_BlockY = 255; + } + else + { + a_BlockY = (unsigned char)Y; + } +} + + + + + +#define PI 3.14159265358979323846264338327950288419716939937510582097494459072381640628620899862803482534211706798f + +inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a_Y, double & a_Z) +{ + // a_X = sinf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI ); + // a_Y = -sinf ( a_Pitch / 180 * PI ); + // a_Z = -cosf ( a_Pan / 180 * PI ) * cosf ( a_Pitch / 180 * PI ); + a_X = cos(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI); + a_Y = sin(a_Pan / 180 * PI) * cos(a_Pitch / 180 * PI); + a_Z = sin(a_Pitch / 180 * PI); +} + + + + + +inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch) +{ + if (a_X != 0) + { + a_Pan = atan2(a_Z, a_X) * 180 / PI - 90; + } + else + { + a_Pan = 0; + } + a_Pitch = atan2(a_Y, sqrt((a_X * a_X) + (a_Z * a_Z))) * 180 / PI; +} + + + + + +inline float GetSignf(float a_Val) +{ + return (a_Val < 0.f) ? -1.f : 1.f; +} + + + + + +inline float GetSpecialSignf( float a_Val ) +{ + return (a_Val <= 0.f) ? -1.f : 1.f; +} + + + + +// tolua_begin +namespace ItemCategory +{ + inline bool IsPickaxe(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_PICKAXE) + || (a_ItemID == E_ITEM_STONE_PICKAXE) + || (a_ItemID == E_ITEM_IRON_PICKAXE) + || (a_ItemID == E_ITEM_GOLD_PICKAXE) + || (a_ItemID == E_ITEM_DIAMOND_PICKAXE); + } + + + + inline bool IsAxe(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_AXE) + || (a_ItemID == E_ITEM_STONE_AXE) + || (a_ItemID == E_ITEM_IRON_AXE) + || (a_ItemID == E_ITEM_GOLD_AXE) + || (a_ItemID == E_ITEM_DIAMOND_AXE); + } + + + + inline bool IsSword(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_SWORD) + || (a_ItemID == E_ITEM_STONE_SWORD) + || (a_ItemID == E_ITEM_IRON_SWORD) + || (a_ItemID == E_ITEM_GOLD_SWORD) + || (a_ItemID == E_ITEM_DIAMOND_SWORD); + } + + + + inline bool IsHoe(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_HOE) + || (a_ItemID == E_ITEM_STONE_HOE) + || (a_ItemID == E_ITEM_IRON_HOE) + || (a_ItemID == E_ITEM_GOLD_HOE) + || (a_ItemID == E_ITEM_DIAMOND_HOE); + } + + + + inline bool IsShovel(short a_ItemID) + { + return (a_ItemID == E_ITEM_WOODEN_SHOVEL) + || (a_ItemID == E_ITEM_STONE_SHOVEL) + || (a_ItemID == E_ITEM_IRON_SHOVEL) + || (a_ItemID == E_ITEM_GOLD_SHOVEL) + || (a_ItemID == E_ITEM_DIAMOND_SHOVEL); + } + + + + inline bool IsTool(short a_ItemID) + { + return IsPickaxe( a_ItemID ) + || IsAxe ( a_ItemID ) + || IsSword ( a_ItemID ) + || IsHoe ( a_ItemID ) + || IsShovel ( a_ItemID ); + } + + + + inline bool IsHelmet(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_CAP) || + (a_ItemType == E_ITEM_GOLD_HELMET) || + (a_ItemType == E_ITEM_CHAIN_HELMET) || + (a_ItemType == E_ITEM_IRON_HELMET) || + (a_ItemType == E_ITEM_DIAMOND_HELMET) + ); + } + + + + inline bool IsChestPlate(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_TUNIC) || + (a_ItemType == E_ITEM_GOLD_CHESTPLATE) || + (a_ItemType == E_ITEM_CHAIN_CHESTPLATE) || + (a_ItemType == E_ITEM_IRON_CHESTPLATE) || + (a_ItemType == E_ITEM_DIAMOND_CHESTPLATE) + ); + } + + + + inline bool IsLeggings(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_PANTS) || + (a_ItemType == E_ITEM_GOLD_LEGGINGS) || + (a_ItemType == E_ITEM_CHAIN_LEGGINGS) || + (a_ItemType == E_ITEM_IRON_LEGGINGS) || + (a_ItemType == E_ITEM_DIAMOND_LEGGINGS) + ); + } + + + + inline bool IsBoots(short a_ItemType) + { + return ( + (a_ItemType == E_ITEM_LEATHER_BOOTS) || + (a_ItemType == E_ITEM_GOLD_BOOTS) || + (a_ItemType == E_ITEM_CHAIN_BOOTS) || + (a_ItemType == E_ITEM_IRON_BOOTS) || + (a_ItemType == E_ITEM_DIAMOND_BOOTS) + ); + } + + + + inline bool IsArmor(short a_ItemType) + { + return ( + IsHelmet(a_ItemType) || + IsChestPlate(a_ItemType) || + IsLeggings(a_ItemType) || + IsBoots(a_ItemType) + ); + } +} +// tolua_end + + +inline bool BlockRequiresSpecialTool(BLOCKTYPE a_BlockType) +{ + if(!IsValidBlock(a_BlockType)) return false; + return g_BlockRequiresSpecialTool[a_BlockType]; +} + + + + + + diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp new file mode 100644 index 000000000..6b53d0b52 --- /dev/null +++ b/src/Enchantments.cpp @@ -0,0 +1,299 @@ +// Enchantments.cpp + +// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments + +#include "Globals.h" +#include "Enchantments.h" +#include "WorldStorage/FastNBT.h" + + + + + +cEnchantments::cEnchantments(void) +{ + // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable +} + + + + + +cEnchantments::cEnchantments(const AString & a_StringSpec) +{ + AddFromString(a_StringSpec); +} + + + + + +void cEnchantments::AddFromString(const AString & a_StringSpec) +{ + // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it + + // Split the StringSpec into separate declarations, each in the form "id=lvl": + AStringVector Decls = StringSplit(a_StringSpec, ";"); + for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr) + { + // Split each declaration into the id and lvl part: + if (itr->empty()) + { + // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently + continue; + } + AStringVector Split = StringSplitAndTrim(*itr, "="); + if (Split.size() != 2) + { + // Malformed decl + LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str()); + continue; + } + int id = atoi(Split[0].c_str()); + if ((id == 0) && (Split[0] != "0")) + { + id = StringToEnchantmentID(Split[0]); + } + int lvl = atoi(Split[1].c_str()); + if ( + ((id <= 0) && (Split[0] != "0")) || + ((lvl == 0) && (Split[1] != "0")) + ) + { + // Numbers failed to parse + LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.", + __FUNCTION__, Split[0].c_str(), Split[1].c_str() + ); + continue; + } + SetLevel(id, lvl); + } // for itr - Decls[] +} + + + + + +AString cEnchantments::ToString(void) const +{ + // Serialize all the enchantments into a string + AString res; + for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) + { + AppendPrintf(res, "%d=%d;", itr->first, itr->second); + } // for itr - m_Enchantments[] + return res; +} + + + + + +int cEnchantments::GetLevel(int a_EnchantmentID) const +{ + // Return the level for the specified enchantment; 0 if not stored + cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID); + if (itr != m_Enchantments.end()) + { + return itr->second; + } + + // Not stored, return zero + return 0; +} + + + + + +void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level) +{ + // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 + if (a_Level == 0) + { + // Delete enchantment, if present: + cMap::iterator itr = m_Enchantments.find(a_EnchantmentID); + if (itr != m_Enchantments.end()) + { + m_Enchantments.erase(itr); + } + } + else + { + // Add / overwrite enchantment + m_Enchantments[a_EnchantmentID] = a_Level; + } +} + + + + + + +void cEnchantments::Clear(void) +{ + m_Enchantments.clear(); +} + + + + + +bool cEnchantments::IsEmpty(void) const +{ + return m_Enchantments.empty(); +} + + + + + +int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) +{ + struct + { + int m_Value; + const char * m_Name; + } EnchantmentNames[] = + { + { enchProtection, "Protection"}, + { enchFireProtection, "FireProtection"}, + { enchFeatherFalling, "FeatherFalling"}, + { enchBlastProtection, "BlastProtection"}, + { enchProjectileProtection, "ProjectileProtection"}, + { enchRespiration, "Respiration"}, + { enchAquaAffinity, "AquaAffinity"}, + { enchThorns, "Thorns"}, + { enchSharpness, "Sharpness"}, + { enchSmite, "Smite"}, + { enchBaneOfArthropods, "BaneOfArthropods"}, + { enchKnockback, "Knockback"}, + { enchFireAspect, "FireAspect"}, + { enchLooting, "Looting"}, + { enchEfficiency, "Efficiency"}, + { enchSilkTouch, "SilkTouch"}, + { enchUnbreaking, "Unbreaking"}, + { enchFortune, "Fortune"}, + { enchPower, "Power"}, + { enchPunch, "Punch"}, + { enchFlame, "Flame"}, + { enchInfinity, "Infinity"}, + { enchLuckOfTheSea, "LuckOfTheSea"}, + { enchLure, "Lure"}, + } ; + for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++) + { + if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0) + { + return EnchantmentNames[i].m_Value; + } + } // for i - EnchantmentNames[] + return -1; +} + + + + + +bool cEnchantments::operator ==(const cEnchantments & a_Other) const +{ + return m_Enchantments == a_Other.m_Enchantments; +} + + + + + +bool cEnchantments::operator !=(const cEnchantments & a_Other) const +{ + return m_Enchantments != a_Other.m_Enchantments; +} + + + + + +void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const +{ + // Write the enchantments into the specified NBT writer + // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments") + + a_Writer.BeginList(a_ListTagName, TAG_Compound); + for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) + { + a_Writer.BeginCompound(""); + a_Writer.AddShort("id", itr->first); + a_Writer.AddShort("lvl", itr->second); + a_Writer.EndCompound(); + } // for itr - m_Enchantments[] + a_Writer.EndList(); +} + + + + + +void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx) +{ + // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments) + + // Verify that the tag is a list: + if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List) + { + LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed", + __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx) + ); + ASSERT(!"Bad EnchListTag type"); + return; + } + + // Verify that the list is of Compounds: + if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound) + { + LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed", + __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx) + ); + ASSERT(!"Bad EnchListTag children type"); + return; + } + + Clear(); + + // Iterate over all the compound children, parse an enchantment from each: + for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag)) + { + // tag is the compound inside the "ench" list tag + ASSERT(a_NBT.GetType(tag) == TAG_Compound); + + // Search for the id and lvl tags' values: + int id = -1, lvl = -1; + for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) + { + if (a_NBT.GetType(ch) != TAG_Short) + { + continue; + } + if (a_NBT.GetName(ch) == "id") + { + id = a_NBT.GetShort(ch); + } + else if (a_NBT.GetName(ch) == "lvl") + { + lvl = a_NBT.GetShort(ch); + } + } // for ch - children of the compound tag + + if ((id == -1) || (lvl <= 0)) + { + // Failed to parse either the id or the lvl, skip this compound + continue; + } + + // Store the enchantment: + m_Enchantments[id] = lvl; + } // for tag - children of the ench list tag +} + + + + diff --git a/src/Enchantments.h b/src/Enchantments.h new file mode 100644 index 000000000..7581b87b5 --- /dev/null +++ b/src/Enchantments.h @@ -0,0 +1,115 @@ +// Enchantments.h + +// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments + + + + + +#pragma once + + + + + +// fwd: WorldStorage/FastNBT.h +class cFastNBTWriter; +class cParsedNBT; + + + + + +// tolua_begin + +/** Class that stores item enchantments or stored-enchantments +The enchantments may be serialized to a stringspec and read back from such stringspec. +The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end, +mapping each enchantment's id onto its level. ID may be either a number or the enchantment name. +Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments. +Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs. +*/ +class cEnchantments +{ +public: + /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs ) + enum + { + enchProtection = 0, + enchFireProtection = 1, + enchFeatherFalling = 2, + enchBlastProtection = 3, + enchProjectileProtection = 4, + enchRespiration = 5, + enchAquaAffinity = 6, + enchThorns = 7, + enchSharpness = 16, + enchSmite = 17, + enchBaneOfArthropods = 18, + enchKnockback = 19, + enchFireAspect = 20, + enchLooting = 21, + enchEfficiency = 32, + enchSilkTouch = 33, + enchUnbreaking = 34, + enchFortune = 35, + enchPower = 48, + enchPunch = 49, + enchFlame = 50, + enchInfinity = 51, + enchLuckOfTheSea = 61, + enchLure = 62, + } ; + + /// Creates an empty enchantments container + cEnchantments(void); + + /// Creates an enchantments container filled with enchantments parsed from stringspec + cEnchantments(const AString & a_StringSpec); + + /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it + void AddFromString(const AString & a_StringSpec); + + /// Serializes all the enchantments into a string + AString ToString(void) const; + + /// Returns the level for the specified enchantment; 0 if not stored + int GetLevel(int a_EnchantmentID) const; + + /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 + void SetLevel(int a_EnchantmentID, int a_Level); + + /// Removes all enchantments + void Clear(void); + + /// Returns true if there are no enchantments + bool IsEmpty(void) const; + + /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive + static int StringToEnchantmentID(const AString & a_EnchantmentName); + + /// Returns true if a_Other contains exactly the same enchantments and levels + bool operator ==(const cEnchantments & a_Other) const; + + // tolua_end + + /// Returns true if a_Other doesn't contain exactly the same enchantments and levels + bool operator !=(const cEnchantments & a_Other) const; + + /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") + void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const; + + /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) + void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx); + +protected: + /// Maps enchantment ID -> enchantment level + typedef std::map<int, int> cMap; + + /// Currently stored enchantments + cMap m_Enchantments; +} ; // tolua_export + + + + diff --git a/src/Endianness.h b/src/Endianness.h new file mode 100644 index 000000000..86eb369f5 --- /dev/null +++ b/src/Endianness.h @@ -0,0 +1,70 @@ + +#pragma once + + + + + +// Changes endianness +inline unsigned long long HostToNetwork8(const void* a_Value ) +{ + unsigned long long __HostToNetwork8; + memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8 ) ); + __HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8) ) << 32) + htonl(__HostToNetwork8 >> 32)); + return __HostToNetwork8; +} + + + + + +inline unsigned int HostToNetwork4(const void* a_Value ) +{ + unsigned int __HostToNetwork4; + memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4 ) ); + __HostToNetwork4 = ntohl( __HostToNetwork4 ); + return __HostToNetwork4; +} + + + + + +inline double NetworkToHostDouble8(const void* a_Value ) +{ +#define ntohll(x) ((((unsigned long long)ntohl((u_long)x)) << 32) + ntohl(x >> 32)) + unsigned long long buf = 0;//(*(unsigned long long*)a_Value); + memcpy( &buf, a_Value, 8 ); + buf = ntohll(buf); + double x; + memcpy(&x, &buf, sizeof(double)); + return x; +} + + + + + +inline long long NetworkToHostLong8(const void * a_Value ) +{ + unsigned long long buf = *(unsigned long long*)a_Value; + buf = ntohll(buf); + return *reinterpret_cast<long long *>(&buf); +} + + + + + +inline float NetworkToHostFloat4(const void* a_Value ) +{ + u_long buf = *(u_long*)a_Value; + buf = ntohl( buf ); + float x = 0; + memcpy( &x, &buf, sizeof(float) ); + return x; +} + + + + diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp new file mode 100644 index 000000000..56e766dd4 --- /dev/null +++ b/src/Entities/Boat.cpp @@ -0,0 +1,87 @@ + +// Boat.cpp + +// Implements the cBoat class representing a boat in the world + +#include "Globals.h" +#include "Boat.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "Player.h" + + + + + +cBoat::cBoat(double a_X, double a_Y, double a_Z) : + super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) +{ + SetMass(20.f); + SetMaxHealth(6); + SetHealth(6); +} + + + + +void cBoat::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnVehicle(*this, 1); +} + + + + + +void cBoat::DoTakeDamage(TakeDamageInfo & TDI) +{ + super::DoTakeDamage(TDI); + + if (GetHealth() == 0) + { + Destroy(true); + } +} + + + + + +void cBoat::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this boat now: + m_Attachee->Detach(); + } + + // Attach the player to this boat + a_Player.AttachTo(this); +} + + + + + +void cBoat::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + super::HandlePhysics(a_Dt, a_Chunk); + BroadcastMovementUpdate(); +} + + + + diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h new file mode 100644 index 000000000..8c51ab86c --- /dev/null +++ b/src/Entities/Boat.h @@ -0,0 +1,37 @@ + +// Boat.h + +// Declares the cBoat class representing a boat in the world + + + + + +#pragma once + +#include "Entity.h" + + + + + +class cBoat : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cBoat); + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + + cBoat(double a_X, double a_Y, double a_Z); +} ; + + + + diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp new file mode 100644 index 000000000..3bea7bc01 --- /dev/null +++ b/src/Entities/Entity.cpp @@ -0,0 +1,1450 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Entity.h" +#include "../World.h" +#include "../Server.h" +#include "../Root.h" +#include "../Vector3d.h" +#include "../Matrix4f.h" +#include "../ReferenceManager.h" +#include "../ClientHandle.h" +#include "../Chunk.h" +#include "../Simulator/FluidSimulator.h" +#include "../PluginManager.h" +#include "../Tracer.h" +#include "Minecart.h" + + + + + +int cEntity::m_EntityCount = 0; +cCriticalSection cEntity::m_CSCount; + + + + + +cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) + : m_UniqueID(0) + , m_Health(1) + , m_MaxHealth(1) + , m_AttachedTo(NULL) + , m_Attachee(NULL) + , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS)) + , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES)) + , m_HeadYaw( 0.0 ) + , m_Rot(0.0, 0.0, 0.0) + , m_Pos(a_X, a_Y, a_Z) + , m_Mass (0.001) //Default 1g + , m_bDirtyHead(true) + , m_bDirtyOrientation(true) + , m_bDirtyPosition(true) + , m_bDirtySpeed(true) + , m_bOnGround( false ) + , m_Gravity( -9.81f ) + , m_IsInitialized(false) + , m_LastPosX( 0.0 ) + , m_LastPosY( 0.0 ) + , m_LastPosZ( 0.0 ) + , m_TimeLastTeleportPacket(0) + , m_TimeLastMoveReltPacket(0) + , m_TimeLastSpeedPacket(0) + , m_EntityType(a_EntityType) + , m_World(NULL) + , m_TicksSinceLastBurnDamage(0) + , m_TicksSinceLastLavaDamage(0) + , m_TicksSinceLastFireDamage(0) + , m_TicksSinceLastVoidDamage(0) + , m_TicksLeftBurning(0) + , m_WaterSpeed(0, 0, 0) + , m_Width(a_Width) + , m_Height(a_Height) +{ + cCSLock Lock(m_CSCount); + m_EntityCount++; + m_UniqueID = m_EntityCount; +} + + + + + +cEntity::~cEntity() +{ + ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world + + LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p", + m_UniqueID, + m_Pos.x, m_Pos.y, m_Pos.z, + (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), + this + ); + + if (m_AttachedTo != NULL) + { + Detach(); + } + if (m_Attachee != NULL) + { + m_Attachee->Detach(); + } + + if (m_IsInitialized) + { + LOGWARNING("ERROR: Entity deallocated without being destroyed"); + ASSERT(!"Entity deallocated without being destroyed or unlinked"); + } + delete m_Referencers; + delete m_References; +} + + + + + +const char * cEntity::GetClass(void) const +{ + return "cEntity"; +} + + + + + +const char * cEntity::GetClassStatic(void) +{ + return "cEntity"; +} + + + + + +const char * cEntity::GetParentClass(void) const +{ + return ""; +} + + + + + +bool cEntity::Initialize(cWorld * a_World) +{ + if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this)) + { + return false; + } + + LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}", + m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z + ); + m_IsInitialized = true; + m_World = a_World; + m_World->AddEntity(this); + + cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this); + + // Spawn the entity on the clients: + a_World->BroadcastSpawnEntity(*this); + + return true; +} + + + + + +void cEntity::WrapHeadYaw(void) +{ + while (m_HeadYaw > 180.f) m_HeadYaw -= 360.f; // Wrap it + while (m_HeadYaw < -180.f) m_HeadYaw += 360.f; +} + + + + + +void cEntity::WrapRotation(void) +{ + while (m_Rot.x > 180.f) m_Rot.x -= 360.f; // Wrap it + while (m_Rot.x < -180.f) m_Rot.x += 360.f; + while (m_Rot.y > 180.f) m_Rot.y -= 360.f; + while (m_Rot.y < -180.f) m_Rot.y += 360.f; +} + + + + +void cEntity::WrapSpeed(void) +{ + // There shoudn't be a need for flipping the flag on because this function is called + // after any update, so the flag is already turned on + if (m_Speed.x > 78.0f) m_Speed.x = 78.0f; + else if (m_Speed.x < -78.0f) m_Speed.x = -78.0f; + if (m_Speed.y > 78.0f) m_Speed.y = 78.0f; + else if (m_Speed.y < -78.0f) m_Speed.y = -78.0f; + if (m_Speed.z > 78.0f) m_Speed.z = 78.0f; + else if (m_Speed.z < -78.0f) m_Speed.z = -78.0f; +} + + + + + +void cEntity::Destroy(bool a_ShouldBroadcast) +{ + if (!m_IsInitialized) + { + return; + } + + if (a_ShouldBroadcast) + { + m_World->BroadcastDestroyEntity(*this); + } + + m_IsInitialized = false; + + Destroyed(); +} + + + + + +void cEntity::TakeDamage(cEntity & a_Attacker) +{ + int RawDamage = a_Attacker.GetRawDamageAgainst(*this); + + TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this)); +} + + + + + +void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount) +{ + int FinalDamage = a_RawDamage - GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage); + cEntity::TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount); +} + + + + + +void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount) +{ + TakeDamageInfo TDI; + TDI.DamageType = a_DamageType; + TDI.Attacker = a_Attacker; + TDI.RawDamage = a_RawDamage; + TDI.FinalDamage = a_FinalDamage; + Vector3d Heading; + Heading.x = sin(GetRotation()); + Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing + Heading.z = cos(GetRotation()); + TDI.Knockback = Heading * a_KnockbackAmount; + DoTakeDamage(TDI); +} + + + + + +void cEntity::SetRotationFromSpeed(void) +{ + const double EPS = 0.0000001; + if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) + { + // atan2() may overflow or is undefined, pick any number + SetRotation(0); + return; + } + SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI); +} + + + + + +void cEntity::SetPitchFromSpeed(void) +{ + const double EPS = 0.0000001; + double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component + if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS)) + { + // atan2() may overflow or is undefined, pick any number + SetPitch(0); + return; + } + SetPitch(atan2(m_Speed.y, xz) * 180 / PI); +} + + + + + +void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) + { + return; + } + + if (m_Health <= 0) + { + // Can't take damage if already dead + return; + } + + m_Health -= (short)a_TDI.FinalDamage; + + // TODO: Apply damage to armor + + if (m_Health < 0) + { + m_Health = 0; + } + + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); + + if (m_Health <= 0) + { + KilledBy(a_TDI.Attacker); + } +} + + + + + +int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver) +{ + // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items + // Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20 + switch (this->GetEquippedWeapon().m_ItemType) + { + case E_ITEM_WOODEN_SWORD: return 4; + case E_ITEM_GOLD_SWORD: return 4; + case E_ITEM_STONE_SWORD: return 5; + case E_ITEM_IRON_SWORD: return 6; + case E_ITEM_DIAMOND_SWORD: return 7; + + case E_ITEM_WOODEN_AXE: return 3; + case E_ITEM_GOLD_AXE: return 3; + case E_ITEM_STONE_AXE: return 4; + case E_ITEM_IRON_AXE: return 5; + case E_ITEM_DIAMOND_AXE: return 6; + + case E_ITEM_WOODEN_PICKAXE: return 2; + case E_ITEM_GOLD_PICKAXE: return 2; + case E_ITEM_STONE_PICKAXE: return 3; + case E_ITEM_IRON_PICKAXE: return 4; + case E_ITEM_DIAMOND_PICKAXE: return 5; + + case E_ITEM_WOODEN_SHOVEL: return 1; + case E_ITEM_GOLD_SHOVEL: return 1; + case E_ITEM_STONE_SHOVEL: return 2; + case E_ITEM_IRON_SHOVEL: return 3; + case E_ITEM_DIAMOND_SHOVEL: return 4; + } + // All other equipped items give a damage of 1: + return 1; +} + + + + + +int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage) +{ + // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover + + // Filter out damage types that are not protected by armor: + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20 + switch (a_DamageType) + { + case dtOnFire: + case dtSuffocating: + case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected + case dtStarving: + case dtInVoid: + case dtPoisoning: + case dtPotionOfHarming: + case dtFalling: + case dtLightning: + { + return 0; + } + } + + // Add up all armor points: + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 + int ArmorValue = 0; + switch (GetEquippedHelmet().m_ItemType) + { + case E_ITEM_LEATHER_CAP: ArmorValue += 1; break; + case E_ITEM_GOLD_HELMET: ArmorValue += 2; break; + case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break; + case E_ITEM_IRON_HELMET: ArmorValue += 2; break; + case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; break; + } + switch (GetEquippedChestplate().m_ItemType) + { + case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break; + case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break; + case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break; + case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break; + case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; break; + } + switch (GetEquippedLeggings().m_ItemType) + { + case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break; + case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break; + case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break; + case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break; + case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; break; + } + switch (GetEquippedBoots().m_ItemType) + { + case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break; + case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break; + case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break; + case E_ITEM_IRON_BOOTS: ArmorValue += 2; break; + case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; break; + } + + // TODO: Special armor cases, such as wool, saddles, dog's collar + // Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20 + + // Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that: + return a_Damage * (ArmorValue * 4) / 100; +} + + + + + +double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver) +{ + // Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit + + // TODO: Enchantments + return 1; +} + + + + + +void cEntity::KilledBy(cEntity * a_Killer) +{ + m_Health = 0; + + cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer); + + if (m_Health > 0) + { + // Plugin wants to 'unkill' the pawn. Abort + return; + } + + // Drop loot: + cItems Drops; + GetDrops(Drops, a_Killer); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); + + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); +} + + + + + +void cEntity::Heal(int a_HitPoints) +{ + m_Health += a_HitPoints; + if (m_Health > m_MaxHealth) + { + m_Health = m_MaxHealth; + } +} + + + + + +void cEntity::SetHealth(int a_Health) +{ + m_Health = std::max(0, std::min(m_MaxHealth, a_Health)); +} + + + + + +void cEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_AttachedTo != NULL) + { + if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5) + { + SetPosition(m_AttachedTo->GetPosition()); + } + } + else + { + if (a_Chunk.IsValid()) + { + HandlePhysics(a_Dt, a_Chunk); + } + } + if (a_Chunk.IsValid()) + { + TickBurning(a_Chunk); + } + if ((a_Chunk.IsValid()) && (GetPosY() < -46)) + { + TickInVoid(a_Chunk); + } + else { m_TicksSinceLastVoidDamage = 0; } +} + + + + + +void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + // TODO Add collision detection with entities. + a_Dt /= 1000; // Convert from msec to sec + Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ()); + Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ()); + int BlockX = (int) floor(NextPos.x); + int BlockY = (int) floor(NextPos.y); + int BlockZ = (int) floor(NextPos.z); + + if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) + { + // Outside of the world + + cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + // See if we can commit our changes. If not, we will discard them. + if (NextChunk != NULL) + { + SetSpeed(NextSpeed); + NextPos += (NextSpeed * a_Dt); + SetPosition(NextPos); + } + return; + } + + // Make sure we got the correct chunk and a valid one. No one ever knows... + cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + if (NextChunk != NULL) + { + int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); + int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); + BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, 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 + { + if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. + { + m_bOnGround = false; + } + } + } + else + { + // Push out entity. + BLOCKTYPE GotBlock; + + static const struct + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + } ; + + bool IsNoAirSurrounding = true; + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock)) + { + // The pickup is too close to an unloaded chunk, bail out of any physics handling + return; + } + if (!g_BlockIsSolid[GotBlock]) + { + NextPos.x += gCrossCoords[i].x; + NextPos.z += gCrossCoords[i].z; + IsNoAirSurrounding = false; + break; + } + } // for i - gCrossCoords[] + + if (IsNoAirSurrounding) + { + NextPos.y += 0.5; + } + + m_bOnGround = true; + + LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", + m_UniqueID, GetClass(), BlockX, BlockY, BlockZ + ); + } + + if (!m_bOnGround) + { + float fallspeed; + if (IsBlockWater(BlockIn)) + { + fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. + } + else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts + { + fallspeed = 0; + m_bOnGround = true; + } + else if (BlockIn == E_BLOCK_COBWEB) + { + NextSpeed.y *= 0.05; // Reduce overall falling speed + fallspeed = 0; // No falling. + } + else + { + // Normal gravity + fallspeed = m_Gravity * a_Dt; + } + NextSpeed.y += fallspeed; + } + else + { + if (IsMinecart()) + { + if (!IsBlockRail(BlockBelow)) + { + // Friction if minecart is off track, otherwise, Minecart.cpp handles this + if (NextSpeed.SqrLength() > 0.0004f) + { + NextSpeed.x *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.x) < 0.05) + { + NextSpeed.x = 0; + } + NextSpeed.z *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.z) < 0.05) + { + NextSpeed.z = 0; + } + } + } + } + else + { + // Friction for non-minecarts + if (NextSpeed.SqrLength() > 0.0004f) + { + NextSpeed.x *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.x) < 0.05) + { + NextSpeed.x = 0; + } + NextSpeed.z *= 0.7f / (1 + a_Dt); + if (fabs(NextSpeed.z) < 0.05) + { + NextSpeed.z = 0; + } + } + } + } + + // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we + // might have different speed modifiers according to terrain. + if (BlockIn == E_BLOCK_COBWEB) + { + NextSpeed.x *= 0.25; + NextSpeed.z *= 0.25; + } + + //Get water direction + Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ); + + m_WaterSpeed *= 0.9f; //Reduce speed each tick + + switch(WaterDir) + { + case X_PLUS: + m_WaterSpeed.x = 0.2f; + m_bOnGround = false; + break; + case X_MINUS: + m_WaterSpeed.x = -0.2f; + m_bOnGround = false; + break; + case Z_PLUS: + m_WaterSpeed.z = 0.2f; + m_bOnGround = false; + break; + case Z_MINUS: + m_WaterSpeed.z = -0.2f; + m_bOnGround = false; + break; + + default: + break; + } + + if (fabs(m_WaterSpeed.x) < 0.05) + { + m_WaterSpeed.x = 0; + } + + if (fabs(m_WaterSpeed.z) < 0.05) + { + m_WaterSpeed.z = 0; + } + + NextSpeed += m_WaterSpeed; + + if( NextSpeed.SqrLength() > 0.f ) + { + cTracer Tracer( GetWorld() ); + int Ret = Tracer.Trace( NextPos, NextSpeed, 2 ); + if( Ret ) // Oh noez! we hit something + { + // Set to hit position + if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() ) + { + if( Ret == 1 ) + { + if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f; + if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f; + if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f; + + if( Tracer.HitNormal.y > 0 ) // means on ground + { + m_bOnGround = true; + } + } + NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z); + NextPos.x += Tracer.HitNormal.x * 0.3f; + NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot + NextPos.z += Tracer.HitNormal.z * 0.3f; + } + else + { + NextPos += (NextSpeed * a_Dt); + } + } + else + { + // We didn't hit anything, so move =] + NextPos += (NextSpeed * a_Dt); + } + } + BlockX = (int) floor(NextPos.x); + BlockZ = (int) floor(NextPos.z); + NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ); + // See if we can commit our changes. If not, we will discard them. + if (NextChunk != NULL) + { + if (NextPos.x != GetPosX()) SetPosX(NextPos.x); + if (NextPos.y != GetPosY()) SetPosY(NextPos.y); + if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z); + if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x); + if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y); + if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z); + } + } +} + + + + + +void cEntity::TickBurning(cChunk & a_Chunk) +{ + // Remember the current burning state: + bool HasBeenBurning = (m_TicksLeftBurning > 0); + + // Do the burning damage: + if (m_TicksLeftBurning > 0) + { + m_TicksSinceLastBurnDamage++; + if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE) + { + TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0); + m_TicksSinceLastBurnDamage = 0; + } + m_TicksLeftBurning--; + } + + // Update the burning times, based on surroundings: + int MinRelX = (int)floor(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; + int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; + int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; + int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; + int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY()))); + int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height))); + bool HasWater = false; + bool HasLava = false; + bool HasFire = false; + + for (int x = MinRelX; x <= MaxRelX; x++) + { + for (int z = MinRelZ; z <= MaxRelZ; z++) + { + int RelX = x; + int RelZ = z; + cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ); + if (CurChunk == NULL) + { + continue; + } + for (int y = MinY; y <= MaxY; y++) + { + switch (CurChunk->GetBlock(RelX, y, RelZ)) + { + case E_BLOCK_FIRE: + { + HasFire = true; + break; + } + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + HasLava = true; + break; + } + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + HasWater = true; + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x + + if (HasWater) + { + // Extinguish the fire + m_TicksLeftBurning = 0; + } + + if (HasLava) + { + // Burn: + m_TicksLeftBurning = BURN_TICKS; + + // Periodically damage: + m_TicksSinceLastLavaDamage++; + if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE) + { + TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0); + m_TicksSinceLastLavaDamage = 0; + } + } + else + { + m_TicksSinceLastLavaDamage = 0; + } + + if (HasFire) + { + // Burn: + m_TicksLeftBurning = BURN_TICKS; + + // Periodically damage: + m_TicksSinceLastFireDamage++; + if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE) + { + TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0); + m_TicksSinceLastFireDamage = 0; + } + } + else + { + m_TicksSinceLastFireDamage = 0; + } + + // If just started / finished burning, notify descendants: + if ((m_TicksLeftBurning > 0) && !HasBeenBurning) + { + OnStartedBurning(); + } + else if ((m_TicksLeftBurning <= 0) && HasBeenBurning) + { + OnFinishedBurning(); + } +} + + + + + +void cEntity::TickInVoid(cChunk & a_Chunk) +{ + if (m_TicksSinceLastVoidDamage == 20) + { + TakeDamage(dtInVoid, NULL, 2, 0); + m_TicksSinceLastVoidDamage = 0; + } + else + { + m_TicksSinceLastVoidDamage++; + } +} + + + + + +/// Called when the entity starts burning +void cEntity::OnStartedBurning(void) +{ + // Broadcast the change: + m_World->BroadcastEntityMetadata(*this); +} + + + + + +/// Called when the entity finishes burning +void cEntity::OnFinishedBurning(void) +{ + // Broadcast the change: + m_World->BroadcastEntityMetadata(*this); +} + + + + + +/// Sets the maximum value for the health +void cEntity::SetMaxHealth(int a_MaxHealth) +{ + m_MaxHealth = a_MaxHealth; + + // Reset health, if too high: + if (m_Health > a_MaxHealth) + { + m_Health = a_MaxHealth; + } +} + + + + + +/// Puts the entity on fire for the specified amount of ticks +void cEntity::StartBurning(int a_TicksLeftBurning) +{ + if (m_TicksLeftBurning > 0) + { + // Already burning, top up the ticks left burning and bail out: + m_TicksLeftBurning = std::max(m_TicksLeftBurning, a_TicksLeftBurning); + return; + } + + m_TicksLeftBurning = a_TicksLeftBurning; + OnStartedBurning(); +} + + + + + +/// Stops the entity from burning, resets all burning timers +void cEntity::StopBurning(void) +{ + bool HasBeenBurning = (m_TicksLeftBurning > 0); + m_TicksLeftBurning = 0; + m_TicksSinceLastBurnDamage = 0; + m_TicksSinceLastFireDamage = 0; + m_TicksSinceLastLavaDamage = 0; + + // Notify if the entity has stopped burning + if (HasBeenBurning) + { + OnFinishedBurning(); + } +} + + + + + +void cEntity::TeleportToEntity(cEntity & a_Entity) +{ + TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ()); +} + + + + + +void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +{ + SetPosition(a_PosX, a_PosY, a_PosZ); + m_World->BroadcastTeleportEntity(*this); +} + + + + + +void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) +{ + //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks + if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2)) + { + m_World->BroadcastEntityVelocity(*this,a_Exclude); + m_bDirtySpeed = false; + m_TimeLastSpeedPacket = m_World->GetWorldAge(); + } + + //Have to process position related packets this every two ticks + if (m_World->GetWorldAge() % 2 == 0) + { + int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0)); + int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0)); + int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0)); + Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket; + // 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds + if (DiffTeleportPacket >= 400 || + ((DiffX > 127) || (DiffX < -128) || + (DiffY > 127) || (DiffY < -128) || + (DiffZ > 127) || (DiffZ < -128))) + { + // + m_World->BroadcastTeleportEntity(*this,a_Exclude); + m_TimeLastTeleportPacket = m_World->GetWorldAge(); + m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize. + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_bDirtyPosition = false; + m_bDirtyOrientation = false; + } + else + { + Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket; + //if the change is big enough. + if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition) + { + if (m_bDirtyOrientation) + { + m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); + m_bDirtyOrientation = false; + } + else + { + m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); + } + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_bDirtyPosition = false; + m_TimeLastMoveReltPacket = m_World->GetWorldAge(); + } + else + { + if (m_bDirtyOrientation) + { + m_World->BroadcastEntityLook(*this,a_Exclude); + m_bDirtyOrientation = false; + } + } + } + if (m_bDirtyHead) + { + m_World->BroadcastEntityHeadLook(*this,a_Exclude); + m_bDirtyHead = false; + } + } +} + + + + + +void cEntity::AttachTo(cEntity * a_AttachTo) +{ + if (m_AttachedTo == a_AttachTo) + { + // Already attached to that entity, nothing to do here + return; + } + + // Detach from any previous entity: + Detach(); + + // Attach to the new entity: + m_AttachedTo = a_AttachTo; + a_AttachTo->m_Attachee = this; + m_World->BroadcastAttachEntity(*this, a_AttachTo); +} + + + + + +void cEntity::Detach(void) +{ + if (m_AttachedTo == NULL) + { + // Attached to no entity, our work is done + return; + } + m_AttachedTo->m_Attachee = NULL; + m_AttachedTo = NULL; + m_World->BroadcastAttachEntity(*this, NULL); +} + + + + + +bool cEntity::IsA(const char * a_ClassName) const +{ + return (strcmp(a_ClassName, "cEntity") == 0); +} + + + + + +void cEntity::SetRot(const Vector3f & a_Rot) +{ + m_Rot = a_Rot; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetHeadYaw(double a_HeadYaw) +{ + m_HeadYaw = a_HeadYaw; + m_bDirtyHead = true; + WrapHeadYaw(); +} + + + + + +void cEntity::SetHeight(double a_Height) +{ + m_Height = a_Height; +} + + + + + +void cEntity::SetMass(double a_Mass) +{ + if (a_Mass > 0) + { + m_Mass = a_Mass; + } + else + { + // Make sure that mass is not zero. 1g is the default because we + // have to choose a number. It's perfectly legal to have a mass + // less than 1g as long as is NOT equal or less than zero. + m_Mass = 0.001; + } +} + + + + + +void cEntity::SetYaw(double a_Yaw) +{ + m_Rot.x = a_Yaw; + m_bDirtyOrientation = true; + WrapRotation(); +} + + + + + +void cEntity::SetPitch(double a_Pitch) +{ + m_Rot.y = a_Pitch; + m_bDirtyOrientation = true; + WrapRotation(); +} + + + + + +void cEntity::SetRoll(double a_Roll) +{ + m_Rot.z = a_Roll; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) +{ + m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ); + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + +void cEntity::SetSpeedX(double a_SpeedX) +{ + m_Speed.x = a_SpeedX; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + +void cEntity::SetSpeedY(double a_SpeedY) +{ + m_Speed.y = a_SpeedY; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + +void cEntity::SetSpeedZ(double a_SpeedZ) +{ + m_Speed.z = a_SpeedZ; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::SetWidth(double a_Width) +{ + m_Width = a_Width; +} + + + + + +void cEntity::AddPosX(double a_AddPosX) +{ + m_Pos.x += a_AddPosX; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddPosY(double a_AddPosY) +{ + m_Pos.y += a_AddPosY; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddPosZ(double a_AddPosZ) +{ + m_Pos.z += a_AddPosZ; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) +{ + m_Pos.x += a_AddPosX; + m_Pos.y += a_AddPosY; + m_Pos.z += a_AddPosZ; + m_bDirtyPosition = true; +} + + + + +void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ) +{ + m_Speed.x += a_AddSpeedX; + m_Speed.y += a_AddSpeedY; + m_Speed.z += a_AddSpeedZ; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::AddSpeedX(double a_AddSpeedX) +{ + m_Speed.x += a_AddSpeedX; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::AddSpeedY(double a_AddSpeedY) +{ + m_Speed.y += a_AddSpeedY; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::AddSpeedZ(double a_AddSpeedZ) +{ + m_Speed.z += a_AddSpeedZ; + m_bDirtySpeed = true; + WrapSpeed(); +} + + + + + +void cEntity::SteerVehicle(float a_Forward, float a_Sideways) +{ + if (m_AttachedTo == NULL) + { + return; + } + if ((a_Forward != 0) || (a_Sideways != 0)) + { + Vector3d LookVector = GetLookVector(); + double AddSpeedX = LookVector.x * a_Forward + LookVector.z * a_Sideways; + double AddSpeedZ = LookVector.z * a_Forward - LookVector.x * a_Sideways; + m_AttachedTo->AddSpeed(AddSpeedX, 0, AddSpeedZ); + } +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Get look vector (this is NOT a rotation!) +Vector3d cEntity::GetLookVector(void) const +{ + Matrix4d m; + m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y); + Vector3d Look = m.Transform(Vector3d(0, 0, 1)); + return Look; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Set position +void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ) +{ + m_Pos.Set(a_PosX, a_PosY, a_PosZ); + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosX(double a_PosX) +{ + m_Pos.x = a_PosX; + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosY(double a_PosY) +{ + m_Pos.y = a_PosY; + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosZ(double a_PosZ) +{ + m_Pos.z = a_PosZ; + m_bDirtyPosition = true; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Reference stuffs +void cEntity::AddReference(cEntity * & a_EntityPtr) +{ + m_References->AddReference(a_EntityPtr); + a_EntityPtr->ReferencedBy(a_EntityPtr); +} + + + + + +void cEntity::ReferencedBy(cEntity * & a_EntityPtr) +{ + m_Referencers->AddReference(a_EntityPtr); +} + + + + + +void cEntity::Dereference(cEntity * & a_EntityPtr) +{ + m_Referencers->Dereference(a_EntityPtr); +} + + + + diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h new file mode 100644 index 000000000..de5f176ae --- /dev/null +++ b/src/Entities/Entity.h @@ -0,0 +1,446 @@ + +#pragma once + +#include "../Item.h" +#include "../Vector3d.h" +#include "../Vector3f.h" + + + + + +// Place this macro in the public section of each cEntity descendant class and you're done :) +#define CLASS_PROTODEF(classname) \ + virtual bool IsA(const char * a_ClassName) const override\ + { \ + return ((strcmp(a_ClassName, #classname) == 0) || super::IsA(a_ClassName)); \ + } \ + virtual const char * GetClass(void) const override \ + { \ + return #classname; \ + } \ + static const char * GetClassStatic(void) \ + { \ + return #classname; \ + } \ + virtual const char * GetParentClass(void) const override \ + { \ + return super::GetClass(); \ + } + + + + + +class cWorld; +class cReferenceManager; +class cClientHandle; +class cPlayer; +class cChunk; + + + + + +// tolua_begin +struct TakeDamageInfo +{ + eDamageType DamageType; // Where does the damage come from? Being hit / on fire / contact with cactus / ... + cEntity * Attacker; // The attacking entity; valid only for dtAttack + int RawDamage; // What damage would the receiver get without any armor. Usually: attacker mob type + weapons + int FinalDamage; // What actual damage will be received. Usually: m_RawDamage minus armor + Vector3d Knockback; // The amount and direction of knockback received from the damage + // TODO: Effects - list of effects that the hit is causing. Unknown representation yet +} ; +// tolua_end + + + + + +// tolua_begin +class cEntity +{ +public: + + enum eEntityType + { + etEntity, // For all other types + etPlayer, + etPickup, + etMonster, + etFallingBlock, + etMinecart, + etBoat, + etTNT, + etProjectile, + etExpOrb, + + // Common variations + etMob = etMonster, // DEPRECATED, use etMonster instead! + } ; + + // tolua_end + + enum + { + ENTITY_STATUS_HURT = 2, + ENTITY_STATUS_DEAD = 3, + ENTITY_STATUS_WOLF_TAMING = 6, + ENTITY_STATUS_WOLF_TAMED = 7, + ENTITY_STATUS_WOLF_SHAKING = 8, + ENTITY_STATUS_EATING_ACCEPTED = 9, + ENTITY_STATUS_SHEEP_EATING = 10, + ENTITY_STATUS_GOLEM_ROSING = 11, + ENTITY_STATUS_VILLAGER_HEARTS = 12, + ENTITY_STATUS_VILLAGER_ANGRY = 13, + ENTITY_STATUS_VILLAGER_HAPPY = 14, + ENTITY_STATUS_WITCH_MAGICKING = 15, + // It seems 16 (zombie conversion) is now done with metadata + ENTITY_STATUS_FIREWORK_EXPLODE= 17, + } ; + + enum + { + FIRE_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in fire + FIRE_DAMAGE = 1, ///< How much damage to deal when standing in fire + LAVA_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in lava + LAVA_DAMAGE = 5, ///< How much damage to deal when standing in lava + BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning + BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning + BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire + } ; + + cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); + virtual ~cEntity(); + + /// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed) + virtual bool Initialize(cWorld * a_World); + + // tolua_begin + + eEntityType GetEntityType(void) const { return m_EntityType; } + + bool IsPlayer (void) const { return (m_EntityType == etPlayer); } + bool IsPickup (void) const { return (m_EntityType == etPickup); } + bool IsMob (void) const { return (m_EntityType == etMonster); } + bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } + bool IsMinecart (void) const { return (m_EntityType == etMinecart); } + bool IsBoat (void) const { return (m_EntityType == etBoat); } + bool IsTNT (void) const { return (m_EntityType == etTNT); } + bool IsProjectile (void) const { return (m_EntityType == etProjectile); } + + /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) + virtual bool IsA(const char * a_ClassName) const; + + /// Returns the topmost class name for the object + virtual const char * GetClass(void) const; + + // Returns the class name of this class + static const char * GetClassStatic(void); + + /// Returns the topmost class's parent class name for the object. cEntity returns an empty string (no parent). + virtual const char * GetParentClass(void) const; + + cWorld * GetWorld(void) const { return m_World; } + + double GetHeadYaw (void) const { return m_HeadYaw; } + double GetHeight (void) const { return m_Height; } + double GetMass (void) const { return m_Mass; } + const Vector3d & GetPosition (void) const { return m_Pos; } + double GetPosX (void) const { return m_Pos.x; } + double GetPosY (void) const { return m_Pos.y; } + double GetPosZ (void) const { return m_Pos.z; } + const Vector3d & GetRot (void) const { return m_Rot; } + double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead + double GetYaw (void) const { return m_Rot.x; } + double GetPitch (void) const { return m_Rot.y; } + double GetRoll (void) const { return m_Rot.z; } + Vector3d GetLookVector(void) const; + const Vector3d & GetSpeed (void) const { return m_Speed; } + double GetSpeedX (void) const { return m_Speed.x; } + double GetSpeedY (void) const { return m_Speed.y; } + double GetSpeedZ (void) const { return m_Speed.z; } + double GetWidth (void) const { return m_Width; } + + int GetChunkX(void) const {return (int)floor(m_Pos.x / cChunkDef::Width); } + int GetChunkZ(void) const {return (int)floor(m_Pos.z / cChunkDef::Width); } + + void SetHeadYaw (double a_HeadYaw); + void SetHeight (double a_Height); + void SetMass (double a_Mass); + void SetPosX (double a_PosX); + void SetPosY (double a_PosY); + void SetPosZ (double a_PosZ); + void SetPosition(double a_PosX, double a_PosY, double a_PosZ); + void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); } + void SetRot (const Vector3f & a_Rot); + void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead + void SetYaw (double a_Yaw); + void SetPitch (double a_Pitch); + void SetRoll (double a_Roll); + void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ); + void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); } + void SetSpeedX (double a_SpeedX); + void SetSpeedY (double a_SpeedY); + void SetSpeedZ (double a_SpeedZ); + void SetWidth (double a_Width); + + void AddPosX (double a_AddPosX); + void AddPosY (double a_AddPosY); + void AddPosZ (double a_AddPosZ); + void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ); + void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x,a_AddPos.y,a_AddPos.z);} + void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ); + void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x,a_AddSpeed.y,a_AddSpeed.z);} + void AddSpeedX (double a_AddSpeedX); + void AddSpeedY (double a_AddSpeedY); + void AddSpeedZ (double a_AddSpeedZ); + + void SteerVehicle(float a_Forward, float a_Sideways); + + inline int GetUniqueID(void) const { return m_UniqueID; } + inline bool IsDestroyed(void) const { return !m_IsInitialized; } + + /// Schedules the entity for destroying; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet + void Destroy(bool a_ShouldBroadcast = true); + + /// Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called + void TakeDamage(cEntity & a_Attacker); + + /// Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called + void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount); + + /// Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage() + void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount); + + float GetGravity(void) const { return m_Gravity; } + + void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } + + /// Sets the rotation to match the speed vector (entity goes "face-forward") + void SetRotationFromSpeed(void); + + /// Sets the pitch to match the speed vector (entity gies "face-forward") + void SetPitchFromSpeed(void); + + // tolua_end + + /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied + virtual void DoTakeDamage(TakeDamageInfo & a_TDI); + + // tolua_begin + + /// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items + virtual int GetRawDamageAgainst(const cEntity & a_Receiver); + + /// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover + virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage); + + /// Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit + virtual double GetKnockbackAmountAgainst(const cEntity & a_Receiver); + + /// Returns the curently equipped weapon; empty item if none + virtual cItem GetEquippedWeapon(void) const { return cItem(); } + + /// Returns the currently equipped helmet; empty item if none + virtual cItem GetEquippedHelmet(void) const { return cItem(); } + + /// Returns the currently equipped chestplate; empty item if none + virtual cItem GetEquippedChestplate(void) const { return cItem(); } + + /// Returns the currently equipped leggings; empty item if none + virtual cItem GetEquippedLeggings(void) const { return cItem(); } + + /// 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) + virtual void KilledBy(cEntity * a_Killer); + + /// Heals the specified amount of HPs + void Heal(int a_HitPoints); + + /// Returns the health of this entity + int GetHealth(void) const { return m_Health; } + + /// Sets the health of this entity; doesn't broadcast any hurt animation + void SetHealth(int a_Health); + + // tolua_end + + virtual void Tick(float a_Dt, cChunk & a_Chunk); + + /// Handles the physics of the entity - updates position based on speed, updates speed based on environment + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); + + /// Updates the state related to this entity being on fire + virtual void TickBurning(cChunk & a_Chunk); + + /// Handles when the entity is in the void + virtual void TickInVoid(cChunk & a_Chunk); + + /// Called when the entity starts burning + virtual void OnStartedBurning(void); + + /// Called when the entity finishes burning + virtual void OnFinishedBurning(void); + + // tolua_begin + + /// Sets the maximum value for the health + void SetMaxHealth(int a_MaxHealth); + + int GetMaxHealth(void) const { return m_MaxHealth; } + + /// Puts the entity on fire for the specified amount of ticks + void StartBurning(int a_TicksLeftBurning); + + /// Stops the entity from burning, resets all burning timers + void StopBurning(void); + + // tolua_end + + /** Descendants override this function to send a command to the specified client to spawn the entity on the client. + To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity() + */ + virtual void SpawnOn(cClientHandle & a_Client) = 0; + + // tolua_begin + + /// Teleports to the entity specified + virtual void TeleportToEntity(cEntity & a_Entity); + + /// Teleports to the coordinates specified + virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ); + + // tolua_end + + /// Updates clients of changes in the entity. + virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL); + + /// Attaches to the specified entity; detaches from any previous one first + void AttachTo(cEntity * a_AttachTo); + + /// Detaches from the currently attached entity, if any + void Detach(void); + + /// Makes sure head yaw is not over the specified range. + void WrapHeadYaw(); + + /// Makes sure rotation is not over the specified range. + void WrapRotation(); + + /// Makes speed is not over 20. Max speed is 20 blocks / second + void WrapSpeed(); + + // tolua_begin + + // COMMON metadata flags; descendants may override the defaults: + virtual bool IsOnFire (void) const {return (m_TicksLeftBurning > 0); } + virtual bool IsCrouched (void) const {return false; } + virtual bool IsRiding (void) const {return false; } + virtual bool IsSprinting(void) const {return false; } + virtual bool IsRclking (void) const {return false; } + virtual bool IsInvisible(void) const {return false; } + + // tolua_end + + /// Called when the specified player right-clicks this entity + virtual void OnRightClicked(cPlayer & a_Player) {}; + + /// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy(). + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) {} + +protected: + static cCriticalSection m_CSCount; + static int m_EntityCount; + + int m_UniqueID; + + int m_Health; + int m_MaxHealth; + + /// The entity to which this entity is attached (vehicle), NULL if none + cEntity * m_AttachedTo; + + /// The entity which is attached to this entity (rider), NULL if none + cEntity * m_Attachee; + + cReferenceManager* m_Referencers; + cReferenceManager* m_References; + + // Flags that signal that we haven't updated the clients with the latest. + bool m_bDirtyHead; + bool m_bDirtyOrientation; + bool m_bDirtyPosition; + bool m_bDirtySpeed; + + bool m_bOnGround; + float m_Gravity; + + // Last Position. + double m_LastPosX, m_LastPosY, m_LastPosZ; + + // This variables keep track of the last time a packet was sent + Int64 m_TimeLastTeleportPacket,m_TimeLastMoveReltPacket,m_TimeLastSpeedPacket; // In ticks + + bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() ) + + eEntityType m_EntityType; + + cWorld * m_World; + + /// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire()) + int m_TicksSinceLastBurnDamage; + + /// Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava. + int m_TicksSinceLastLavaDamage; + + /// Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire. + int m_TicksSinceLastFireDamage; + + /// Time, in ticks, until the entity extinguishes its fire + int m_TicksLeftBurning; + + /// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void. + int m_TicksSinceLastVoidDamage; + + virtual void Destroyed(void) {} // Called after the entity has been destroyed + + void SetWorld(cWorld * a_World) { m_World = a_World; } + + friend class cReferenceManager; + void AddReference( cEntity*& a_EntityPtr ); + void ReferencedBy( cEntity*& a_EntityPtr ); + void Dereference( cEntity*& a_EntityPtr ); + +private: + // Measured in degrees (MAX 360°) + double m_HeadYaw; + // Measured in meter/second (m/s) + Vector3d m_Speed; + // Measured in degrees (MAX 360°) + Vector3d m_Rot; + + /// Position of the entity's XZ center and Y bottom + Vector3d m_Pos; + + // Measured in meter / second + Vector3d m_WaterSpeed; + + // Measured in Kilograms (Kg) + double m_Mass; + + /// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. + double m_Width; + + /// Height of the entity (Y axis) + double m_Height; +} ; // tolua_export + +typedef std::list<cEntity *> cEntityList; + + + + diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp new file mode 100644 index 000000000..1e5ee00ce --- /dev/null +++ b/src/Entities/ExpOrb.cpp @@ -0,0 +1,60 @@ +#include "Globals.h" + +#include "ExpOrb.h" +#include "Player.h" +#include "../ClientHandle.h" + + +cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) : + cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98), + m_Reward(a_Reward) +{ +} + + + + + +cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward) : + cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), + m_Reward(a_Reward) +{ +} + + + + + +void cExpOrb::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendExperienceOrb(*this); + m_bDirtyPosition = false; + m_bDirtySpeed = false; + m_bDirtyOrientation = false; + m_bDirtyHead = false; +} + + + + + +void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) +{ + cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 4)); + if (a_ClosestPlayer) + { + Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition()); + Vector3f a_Distance(a_PlayerPos - GetPosition()); + if (a_Distance.Length() < 0.1f) + { + a_ClosestPlayer->DeltaExperience(m_Reward); + a_ClosestPlayer->SendExperience(); + Destroy(true); + } + a_Distance.y = 0; + a_Distance.Normalize(); + a_Distance *= 3; + SetSpeedX( a_Distance.x ); + SetSpeedZ( a_Distance.z ); + } +}
\ No newline at end of file diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h new file mode 100644 index 000000000..a062eedd3 --- /dev/null +++ b/src/Entities/ExpOrb.h @@ -0,0 +1,29 @@ + +#pragma once + +#include "Entity.h" + + + + + +class cExpOrb : + public cEntity +{ + typedef cExpOrb super; + +public: + + cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward); + cExpOrb(const Vector3d & a_Pos, int a_Reward); + + // Override functions + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SpawnOn(cClientHandle & a_Client) override; + + // cExpOrb functions + int GetReward(void) const { return m_Reward; } + +protected: + int m_Reward; +} ;
\ No newline at end of file diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp new file mode 100644 index 000000000..9fcd9ac80 --- /dev/null +++ b/src/Entities/FallingBlock.cpp @@ -0,0 +1,93 @@ +#include "Globals.h" + +#include "FallingBlock.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../Simulator/SandSimulator.h" +#include "../Chunk.h" + + + + + +cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_OriginalPosition(a_BlockPosition) +{ +} + + + + + +void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnFallingBlock(*this); +} + + + + + +void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) +{ + float MilliDt = a_Dt * 0.001f; + AddSpeedY(MilliDt * -9.8f); + AddPosY(GetSpeedY() * MilliDt); + + // GetWorld()->BroadcastTeleportEntity(*this); // Test position + + int BlockX = m_OriginalPosition.x; + int BlockY = (int)(GetPosY() - 0.5); + int BlockZ = m_OriginalPosition.z; + + if (BlockY < 0) + { + // Fallen out of this world, just continue falling until out of sight, then destroy: + if (BlockY < 100) + { + Destroy(true); + } + return; + } + + if (BlockY >= cChunkDef::Height) + { + // Above the world, just wait for it to fall back down + return; + } + + int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); + BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); + NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); + if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) + { + // Fallen onto a block that breaks this into pickups (e. g. half-slab) + // Must finish the fall with coords one below the block: + cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); + Destroy(true); + return; + } + else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) + { + // Fallen onto a solid block + /* + LOGD( + "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", + BlockX, BlockY, BlockZ, + BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, + ItemTypeToString(BlockBelow).c_str() + ); + */ + + cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); + Destroy(true); + return; + } +} + + + + diff --git a/src/Entities/FallingBlock.h b/src/Entities/FallingBlock.h new file mode 100644 index 000000000..5ba9909bb --- /dev/null +++ b/src/Entities/FallingBlock.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "Entity.h" + + + + +class cPlayer; +class cItem; + + + + + + +class cFallingBlock : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cFallingBlock); + + /// Creates a new falling block. a_BlockPosition is expected in world coords + cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords +} ; + + + + diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp new file mode 100644 index 000000000..f75e23d8b --- /dev/null +++ b/src/Entities/Minecart.cpp @@ -0,0 +1,541 @@ + +// Minecart.cpp + +// Implements the cMinecart class representing a minecart in the world +// Indiana Jones! + +#include "Globals.h" +#include "Minecart.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../Chunk.h" +#include "Player.h" + + + + + +cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : + super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), + m_Payload(a_Payload), + m_LastDamage(0) +{ + SetMass(20.f); + SetMaxHealth(6); + SetHealth(6); +} + + + + +void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) +{ + char SubType = 0; + switch (m_Payload) + { + case mpNone: SubType = 0; break; + case mpChest: SubType = 1; break; + case mpFurnace: SubType = 2; break; + case mpTNT: SubType = 3; break; + case mpHopper: SubType = 5; break; + default: + { + ASSERT(!"Unknown payload, cannot spawn on client"); + return; + } + } + a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart +} + + + + + +void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + int PosY = (int)floor(GetPosY()); + if ((PosY <= 0) || (PosY >= cChunkDef::Height)) + { + // 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)) + { + HandleRailPhysics(a_Dt, *Chunk); + } + else + { + if (IsBlockRail(InsideType)) + { + SetPosY(PosY + 1); + HandleRailPhysics(a_Dt, *Chunk); + } + else + { + super::HandlePhysics(a_Dt, *Chunk); + BroadcastMovementUpdate(); + } + } +} + + + + + +static const double MAX_SPEED = 8; +static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); + +void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) +{ + + super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling + + /* + NOTE: Please bear in mind that taking away from negatives make them even more negative, + adding to negatives make them positive, etc. + */ + + // Get block meta below the cart + int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ); + double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed + + switch (BelowMeta) + { + case E_META_RAIL_ZM_ZP: // NORTHSOUTH + { + SetRotation(270); + SpeedY = 0; // Don't move vertically as on ground + SpeedX = 0; // Correct diagonal movement from curved rails + + if (SpeedZ != 0) // Don't do anything if cart is stationary + { + if (SpeedZ > 0) + { + // Going SOUTH, slow down + SpeedZ = SpeedZ - 0.1; + } + else + { + // Going NORTH, slow down + SpeedZ = SpeedZ + 0.1; + } + } + break; + } + + case E_META_RAIL_XM_XP: // EASTWEST + { + SetRotation(180); + SpeedY = 0; + SpeedZ = 0; + + if (SpeedX != 0) + { + if (SpeedX > 0) + { + SpeedX = SpeedX - 0.1; + } + else + { + SpeedX = SpeedX + 0.1; + } + } + break; + } + + case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH + { + SetRotation(270); + SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/ + SpeedX = 0; + + if (SpeedZ >= 0) + { + // SpeedZ POSITIVE, going SOUTH + if (SpeedZ <= MAX_SPEED) // Speed limit + { + SpeedZ = SpeedZ + 0.5; // Speed up + SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative) + } + else + { + SpeedZ = MAX_SPEED; // Enforce speed limit + SpeedY = (0 - SpeedZ); + } + } + else + { + // SpeedZ NEGATIVE, going NORTH + SpeedZ = SpeedZ + 0.4; // Slow down + SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number) + } + break; + } + + case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH + { + SetRotation(270); + SetPosY(floor(GetPosY()) + 0.2); + SpeedX = 0; + + if (SpeedZ > 0) + { + // SpeedZ POSITIVE, going SOUTH + SpeedZ = SpeedZ - 0.4; // Slow down + SpeedY = SpeedZ; // Upward movement positive + } + else + { + if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit + { + // SpeedZ NEGATIVE, going NORTH + SpeedZ = SpeedZ - 0.5; // Speed up + SpeedY = SpeedZ; // Downward movement negative + } + else + { + SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit + SpeedY = SpeedZ; + } + } + break; + } + + case E_META_RAIL_ASCEND_XM: // ASCEND EAST + { + SetRotation(180); + SetPosY(floor(GetPosY()) + 0.2); + SpeedZ = 0; + + if (SpeedX >= 0) + { + if (SpeedX <= MAX_SPEED) + { + SpeedX = SpeedX + 0.5; + SpeedY = (0 - SpeedX); + } + else + { + SpeedX = MAX_SPEED; + SpeedY = (0 - SpeedX); + } + } + else + { + SpeedX = SpeedX + 0.4; + SpeedY = (0 - SpeedX); + } + break; + } + + case E_META_RAIL_ASCEND_XP: // ASCEND WEST + { + SetRotation(180); + SetPosY(floor(GetPosY()) + 0.2); + SpeedZ = 0; + + if (SpeedX > 0) + { + SpeedX = SpeedX - 0.4; + SpeedY = SpeedX; + } + else + { + if (SpeedX >= MAX_SPEED_NEGATIVE) + { + SpeedX = SpeedX - 0.5; + SpeedY = SpeedX; + } + else + { + SpeedX = MAX_SPEED_NEGATIVE; + SpeedY = SpeedX; + } + } + break; + } + + case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST + { + SetRotation(315); // Set correct rotation server side + SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart + + if (SpeedZ > 0) // Cart moving south + { + SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail) + } + else if (SpeedX > 0) // Cart moving east + { + SpeedZ = (0 - SpeedX); // Diagonally move northeast + } + break; + } + + case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST + { + SetRotation(225); + SetPosY(floor(GetPosY()) + 0.2); + + if (SpeedZ > 0) + { + SpeedX = SpeedZ; + } + else if (SpeedX < 0) + { + SpeedZ = SpeedX; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST + { + SetRotation(135); + SetPosY(floor(GetPosY()) + 0.2); + + if (SpeedZ < 0) + { + SpeedX = SpeedZ; + } + else if (SpeedX > 0) + { + SpeedZ = SpeedX; + } + break; + } + + case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST + { + SetRotation(45); + SetPosY(floor(GetPosY()) + 0.2); + + if (SpeedZ < 0) + { + SpeedX = (0 - SpeedZ); + } + else if (SpeedX < 0) + { + SpeedZ = (0 - SpeedX); + } + break; + } + + default: + { + ASSERT(!"Unhandled rail meta!"); // Dun dun DUN! + break; + } + } + + // Set speed to speed variables + SetSpeedX(SpeedX); + SetSpeedY(SpeedY); + SetSpeedZ(SpeedZ); + + + // Broadcast position to client + BroadcastMovementUpdate(); +} + + + + + +void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) +{ + m_LastDamage = TDI.FinalDamage; + super::DoTakeDamage(TDI); + + m_World->BroadcastEntityMetadata(*this); + + if (GetHealth() <= 0) + { + Destroy(true); + + cItems Drops; + switch (m_Payload) + { + case mpNone: + { + Drops.push_back(cItem(E_ITEM_MINECART, 1, 0)); + break; + } + case mpChest: + { + Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0)); + break; + } + case mpFurnace: + { + Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0)); + break; + } + case mpTNT: + { + Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0)); + break; + } + case mpHopper: + { + Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0)); + break; + } + default: + { + ASSERT(!"Unhandled minecart type when spawning pickup!"); + return; + } + } + + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEmptyMinecart: + +cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : + super(mpNone, a_X, a_Y, a_Z) +{ +} + + + + + +void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this minecart now: + m_Attachee->Detach(); + } + + // Attach the player to this minecart + a_Player.AttachTo(this); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithChest: + +cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : + super(mpChest, a_X, a_Y, a_Z) +{ +} + + + + + +void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) +{ + ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); + + m_Items[a_Idx] = a_Item; +} + + + + + +void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) +{ + // Show the chest UI window to the player + // TODO +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithFurnace: + +cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : + super(mpFurnace, a_X, a_Y, a_Z), + m_IsFueled(false) +{ +} + + + + + +void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) +{ + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + m_IsFueled = true; + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithTNT: + +cMinecartWithTNT::cMinecartWithTNT(double a_X, double a_Y, double a_Z) : + super(mpTNT, a_X, a_Y, a_Z) +{ +} + +// TODO: Make it activate when passing over activator rail + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithHopper: + +cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) : + super(mpHopper, a_X, a_Y, a_Z) +{ +} + +// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks +// AND AVARYTHING!!
\ No newline at end of file diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h new file mode 100644 index 000000000..b1b48be4e --- /dev/null +++ b/src/Entities/Minecart.h @@ -0,0 +1,169 @@ + +// Minecart.h + +// Declares the cMinecart class representing a minecart in the world + + + + + +#pragma once + +#include "Entity.h" + + + + + +inline bool IsBlockRail(BLOCKTYPE a_BlockType) + { + return ( + (a_BlockType == E_BLOCK_RAIL) || + (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) || + (a_BlockType == E_BLOCK_DETECTOR_RAIL) || + (a_BlockType == E_BLOCK_POWERED_RAIL) + ) ; + } + + + + + +class cMinecart : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cMinecart); + + enum ePayload + { + mpNone, // Empty minecart, ridable by player or mobs + mpChest, // Minecart-with-chest, can store a grid of 3*8 items + mpFurnace, // Minecart-with-furnace, can be powered + mpTNT, // Minecart-with-TNT, can be blown up with activator rail + mpHopper, // Minecart-with-hopper, can be hopper + // TODO: Spawner minecarts, (and possibly any block in a minecart with NBT editing) + } ; + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + + int LastDamage(void) const { return m_LastDamage; } + void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); + ePayload GetPayload(void) const { return m_Payload; } + +protected: + ePayload m_Payload; + + cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); + + int m_LastDamage; + +} ; + + + + + +class cEmptyMinecart : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cEmptyMinecart); + + cEmptyMinecart(double a_X, double a_Y, double a_Z); + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + + +class cMinecartWithChest : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithChest); + + /// Number of item slots in the chest + static const int NumSlots = 9 * 3; + + cMinecartWithChest(double a_X, double a_Y, double a_Z); + + const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } + cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } + + void SetSlot(int a_Idx, const cItem & a_Item); + +protected: + + /// The chest contents: + cItem m_Items[NumSlots]; + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + + +class cMinecartWithFurnace : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithFurnace); + + cMinecartWithFurnace(double a_X, double a_Y, double a_Z); + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsFueled (void) const { return m_IsFueled; } + +private: + + bool m_IsFueled; + +} ; + + + + + +class cMinecartWithTNT : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithTNT); + + cMinecartWithTNT(double a_X, double a_Y, double a_Z); +} ; + + + + + +class cMinecartWithHopper : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithHopper); + + cMinecartWithHopper(double a_X, double a_Y, double a_Z); +} ;
\ No newline at end of file diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp new file mode 100644 index 000000000..fffefd538 --- /dev/null +++ b/src/Entities/Pawn.cpp @@ -0,0 +1,19 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Pawn.h" + + + + + +cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) + : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height) + , m_bBurnable(true) +{ +} + + + + + diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h new file mode 100644 index 000000000..e76337d86 --- /dev/null +++ b/src/Entities/Pawn.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "Entity.h" + + + + + +// tolua_begin +class cPawn : + public cEntity +{ + // tolua_end + typedef cEntity super; + +public: + CLASS_PROTODEF(cPawn); + + cPawn(eEntityType a_EntityType, double a_Width, double a_Height); + +protected: + bool m_bBurnable; +} ; // tolua_export + + + + diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp new file mode 100644 index 000000000..f8aae9703 --- /dev/null +++ b/src/Entities/Pickup.cpp @@ -0,0 +1,166 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 +#include <cstdlib> +#endif + +#include "Pickup.h" +#include "../ClientHandle.h" +#include "../Inventory.h" +#include "../World.h" +#include "../Simulator/FluidSimulator.h" +#include "../Server.h" +#include "Player.h" +#include "../PluginManager.h" +#include "../Item.h" +#include "../Root.h" +#include "../Chunk.h" + +#include "../Vector3d.h" +#include "../Vector3f.h" + + + + + +cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) + : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2) + , m_Timer( 0.f ) + , m_Item(a_Item) + , m_bCollected( false ) + , m_bIsPlayerCreated( IsPlayerCreated ) +{ + SetGravity(-10.5f); + SetMaxHealth(5); + SetHealth(5); + SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); +} + + + + + +void cPickup::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendPickupSpawn(*this); +} + + + + + +void cPickup::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + BroadcastMovementUpdate(); //Notify clients of position + + m_Timer += a_Dt; + + if (!m_bCollected) + { + int BlockY = (int) floor(GetPosY()); + if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world + { + int BlockX = (int) floor(GetPosX()); + int BlockZ = (int) floor(GetPosZ()); + // Position might have changed due to physics. So we have to make sure we have the correct chunk. + cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); + if (CurrentChunk != NULL) // Make sure the chunk is loaded + { + int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width); + int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width); + + // If the pickup is on the bottommost block position, make it think the void is made of air: (#131) + BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; + BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ); + + if ( + IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) || + IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE) + ) + { + m_bCollected = true; + m_Timer = 0; // We have to reset the timer. + m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. + if (m_Timer > 500.f) + { + Destroy(true); + return; + } + } + } + } + } + else + { + if (m_Timer > 500.f) // 0.5 second + { + Destroy(true); + return; + } + } + + if (m_Timer > 1000 * 60 * 5) // 5 minutes + { + Destroy(true); + return; + } + + if (GetPosY() < -8) // Out of this world and no more visible! + { + Destroy(true); + return; + } +} + + + + + +bool cPickup::CollectedBy(cPlayer * a_Dest) +{ + ASSERT(a_Dest != NULL); + + if (m_bCollected) + { + // LOG("Pickup %d cannot be collected by \"%s\", because it has already been collected.", m_UniqueID, a_Dest->GetName().c_str()); + return false; // It's already collected! + } + + // Two seconds if player created the pickup (vomiting), half a second if anything else + if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f)) + { + // LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str()); + return false; // Not old enough + } + + if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this)) + { + // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str()); + return false; + } + + int NumAdded = a_Dest->GetInventory().AddItem(m_Item); + if (NumAdded > 0) + { + m_Item.m_ItemCount -= NumAdded; + m_World->BroadcastCollectPickup(*this, *a_Dest); + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + if (m_Item.m_ItemCount == 0) + { + // All of the pickup has been collected, schedule the pickup for destroying + m_bCollected = true; + } + m_Timer = 0; + return true; + } + + // LOG("Pickup %d cannot be collected by \"%s\", because there's no space in the inventory.", a_Dest->GetName().c_str(), m_UniqueID); + return false; +} + + + + diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h new file mode 100644 index 000000000..d39eda298 --- /dev/null +++ b/src/Entities/Pickup.h @@ -0,0 +1,64 @@ + +#pragma once + +#include "Entity.h" +#include "../Item.h" + + + + + +class cPlayer; + + + + + +// tolua_begin +class cPickup : + public cEntity +{ + // tolua_end + typedef cEntity super; + +public: + CLASS_PROTODEF(cPickup); + + cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, 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; } + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + + bool CollectedBy(cPlayer * a_Dest); // tolua_export + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + /// Returns the number of ticks that this entity has existed + int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export + + /// Returns true if the pickup has already been collected + bool IsCollected(void) const { return m_bCollected; } // tolua_export + + /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time + bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export + +private: + Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) + + Vector3d m_WaterSpeed; + + /// The number of ticks that the entity has existed / timer between collect and destroy; in msec + float m_Timer; + + cItem m_Item; + + bool m_bCollected; + + bool m_bIsPlayerCreated; +}; // tolua_export + + + + diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp new file mode 100644 index 000000000..f4ddd979c --- /dev/null +++ b/src/Entities/Player.cpp @@ -0,0 +1,1765 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Player.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../UI/Window.h" +#include "../UI/WindowOwner.h" +#include "../World.h" +#include "Pickup.h" +#include "../PluginManager.h" +#include "../BlockEntities/BlockEntity.h" +#include "../GroupManager.h" +#include "../Group.h" +#include "../ChatColor.h" +#include "../Item.h" +#include "../Tracer.h" +#include "../Root.h" +#include "../OSSupport/Timer.h" +#include "../MersenneTwister.h" +#include "../Chunk.h" +#include "../Items/ItemHandler.h" + +#include "../Vector3d.h" +#include "../Vector3f.h" + +#include "lib/iniFile/iniFile.h" +#include "lib/jsoncpp/include/json/json.h" + +#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) + + + + + + +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) + : super(etPlayer, 0.6, 1.8) + , m_GameMode(eGameMode_NotSet) + , m_IP("") + , m_LastBlockActionTime( 0 ) + , m_LastBlockActionCnt( 0 ) + , m_AirLevel( MAX_AIR_LEVEL ) + , m_AirTickTimer( DROWNING_TICKS ) + , m_bVisible( true ) + , m_LastGroundHeight( 0 ) + , m_bTouchGround( false ) + , m_Stance( 0.0 ) + , m_Inventory(*this) + , m_CurrentWindow(NULL) + , m_InventoryWindow(NULL) + , m_TimeLastPickupCheck( 0.f ) + , m_Color('-') + , m_ClientHandle( a_Client ) + , m_FoodLevel(MAX_FOOD_LEVEL) + , m_FoodSaturationLevel(5) + , m_FoodTickTimer(0) + , m_FoodExhaustionLevel(0) + , m_FoodPoisonedTicksRemaining(0) + , m_NormalMaxSpeed(0.1) + , m_SprintingMaxSpeed(0.13) + , m_IsCrouched(false) + , m_IsSprinting(false) + , m_IsSwimming(false) + , m_IsSubmerged(false) + , m_EatingFinishTick(-1) + , m_IsChargingBow(false) + , m_BowCharge(0) + , m_CurrentXp(0) + , m_LifetimeTotalXp(0) + , m_bDirtyExperience(false) +{ + LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", + a_PlayerName.c_str(), a_Client->GetIPString().c_str(), + this, GetUniqueID() + ); + + m_InventoryWindow = new cInventoryWindow(*this); + m_CurrentWindow = m_InventoryWindow; + m_InventoryWindow->OpenedByPlayer(*this); + + SetMaxHealth(MAX_HEALTH); + m_Health = MAX_HEALTH; + + cTimer t1; + m_LastPlayerListTime = t1.GetNowTime(); + + m_TimeLastTeleportPacket = 0; + m_TimeLastPickupCheck = 0; + + m_PlayerName = a_PlayerName; + m_bDirtyPosition = true; // So chunks are streamed to player at spawn + + if (!LoadFromDisk()) + { + m_Inventory.Clear(); + SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); + SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); + SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); + + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", + a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() + ); + } + m_LastJumpHeight = (float)(GetPosY()); + m_LastGroundHeight = (float)(GetPosY()); + m_Stance = GetPosY() + 1.62; + + cRoot::Get()->GetServer()->PlayerCreated(this); +} + + + + + +cPlayer::~cPlayer(void) +{ + LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); + + // Notify the server that the player is being destroyed + cRoot::Get()->GetServer()->PlayerDestroying(this); + + SaveToDisk(); + + m_World->RemovePlayer( this ); + + m_ClientHandle = NULL; + + delete m_InventoryWindow; + + LOGD("Player %p deleted", this); +} + + + + + +bool cPlayer::Initialize(cWorld * a_World) +{ + ASSERT(a_World != NULL); + + if (super::Initialize(a_World)) + { + // Remove the client handle from the server, it will be ticked from this object from now on + if (m_ClientHandle != NULL) + { + cRoot::Get()->GetServer()->ClientMovedToWorld(m_ClientHandle); + } + + GetWorld()->AddPlayer(this); + return true; + } + return false; +} + + + + + +void cPlayer::Destroyed() +{ + CloseWindow(false); + + m_ClientHandle = NULL; +} + + + + + +void cPlayer::SpawnOn(cClientHandle & a_Client) +{ + if (!m_bVisible || (m_ClientHandle == (&a_Client))) + { + return; + } + a_Client.SendPlayerSpawn(*this); + a_Client.SendEntityHeadLook(*this); + a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() ); + a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() ); + a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() ); + a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() ); + a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() ); +} + + + + + +void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_ClientHandle != NULL) + { + if (m_ClientHandle->IsDestroyed()) + { + // This should not happen, because destroying a client will remove it from the world, but just in case + m_ClientHandle = NULL; + return; + } + + if (!m_ClientHandle->IsPlaying()) + { + // We're not yet in the game, ignore everything + return; + } + } + + if (!a_Chunk.IsValid()) + { + // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) + return; + } + + super::Tick(a_Dt, a_Chunk); + + // Set player swimming state + SetSwimState(a_Chunk); + + // Handle air drowning stuff + HandleAir(); + + // Handle charging the bow: + if (m_IsChargingBow) + { + m_BowCharge += 1; + } + + //handle updating experience + if (m_bDirtyExperience) + { + SendExperience(); + } + + if (m_bDirtyPosition) + { + // Apply food exhaustion from movement: + ApplyFoodExhaustionFromMovement(); + + cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); + BroadcastMovementUpdate(m_ClientHandle); + m_ClientHandle->StreamChunks(); + } + else + { + BroadcastMovementUpdate(m_ClientHandle); + } + + if (m_Health > 0) // make sure player is alive + { + m_World->CollectPickupsByPlayer(this); + + if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) + { + FinishEating(); + } + + HandleFood(); + } + + // Send Player List (Once per m_LastPlayerListTime/1000 ms) + cTimer t1; + if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) + { + m_World->SendPlayerList(this); + m_LastPlayerListTime = t1.GetNowTime(); + } +} + + + + + +short cPlayer::CalcLevelFromXp(short a_XpTotal) +{ + //level 0 to 15 + if(a_XpTotal <= XP_TO_LEVEL15) + { + return a_XpTotal / XP_PER_LEVEL_TO15; + } + + //level 30+ + if(a_XpTotal > XP_TO_LEVEL30) + { + return (short) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; + } + + //level 16 to 30 + return (short) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal )))) / 3; +} + + + + + +short cPlayer::XpForLevel(short a_Level) +{ + //level 0 to 15 + if(a_Level <= 15) + { + return a_Level * XP_PER_LEVEL_TO15; + } + + //level 30+ + if(a_Level >= 31) + { + return (short) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220 ); + } + + //level 16 to 30 + return (short) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360 ); +} + + + + + +short cPlayer::GetXpLevel() +{ + return CalcLevelFromXp(m_CurrentXp); +} + + + + + +float cPlayer::GetXpPercentage() +{ + short int currentLevel = CalcLevelFromXp(m_CurrentXp); + short int currentLevel_XpBase = XpForLevel(currentLevel); + + return (float)(m_CurrentXp - currentLevel_XpBase) / + (float)(XpForLevel(1+currentLevel) - currentLevel_XpBase); +} + + + + + +bool cPlayer::SetCurrentExperience(short int a_CurrentXp) +{ + if(!(a_CurrentXp >= 0) || (a_CurrentXp > (SHRT_MAX - m_LifetimeTotalXp))) + { + LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); + return false; //oops, they gave us a dodgey number + } + + m_CurrentXp = a_CurrentXp; + + // Set experience to be updated + m_bDirtyExperience = true; + + return true; +} + + + + + +short cPlayer::DeltaExperience(short a_Xp_delta) +{ + if (a_Xp_delta > (SHRT_MAX - m_CurrentXp)) + { + // Value was bad, abort and report + LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", + a_Xp_delta); + return -1; // Should we instead just return the current Xp? + } + + m_CurrentXp += a_Xp_delta; + + // Make sure they didn't subtract too much + if (m_CurrentXp < 0) + { + m_CurrentXp = 0; + } + + // Update total for score calculation + if (a_Xp_delta > 0) + { + m_LifetimeTotalXp += a_Xp_delta; + } + + LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", + m_PlayerName.c_str(), a_Xp_delta, m_CurrentXp); + + // Set experience to be updated + m_bDirtyExperience = true; + + return m_CurrentXp; +} + + + + + +void cPlayer::StartChargingBow(void) +{ + LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str()); + m_IsChargingBow = true; + m_BowCharge = 0; +} + + + + + +int cPlayer::FinishChargingBow(void) +{ + LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); + int res = m_BowCharge; + m_IsChargingBow = false; + m_BowCharge = 0; + return res; +} + + + + + +void cPlayer::CancelChargingBow(void) +{ + LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); + m_IsChargingBow = false; + m_BowCharge = 0; +} + + + + + +void cPlayer::SetTouchGround(bool a_bTouchGround) +{ + m_bTouchGround = a_bTouchGround; + + if (!m_bTouchGround) + { + if (GetPosY() > m_LastJumpHeight) + { + m_LastJumpHeight = (float)GetPosY(); + } + cWorld * World = GetWorld(); + if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height)) + { + BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ())); + if (BlockType != E_BLOCK_AIR) + { + m_bTouchGround = true; + } + if ( + (BlockType == E_BLOCK_WATER) || + (BlockType == E_BLOCK_STATIONARY_WATER) || + (BlockType == E_BLOCK_LADDER) || + (BlockType == E_BLOCK_VINES) + ) + { + m_LastGroundHeight = (float)GetPosY(); + } + } + } + else + { + float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); + int Damage = (int)(Dist - 3.f); + if (m_LastJumpHeight > m_LastGroundHeight) Damage++; + m_LastJumpHeight = (float)GetPosY(); + + if ((Damage > 0) && (!IsGameModeCreative())) + { + TakeDamage(dtFalling, NULL, Damage, Damage, 0); + } + + m_LastGroundHeight = (float)GetPosY(); + } +} + + + + + +void cPlayer::Heal(int a_Health) +{ + super::Heal(a_Health); + SendHealth(); +} + + + + + +void cPlayer::SetFoodLevel(int a_FoodLevel) +{ + m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); + SendHealth(); +} + + + + + +void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) +{ + m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel)); +} + + + + + +void cPlayer::SetFoodTickTimer(int a_FoodTickTimer) +{ + m_FoodTickTimer = a_FoodTickTimer; +} + + + + + +void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel) +{ + m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 4.0)); +} + + + + + +void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) +{ + m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; +} + + + + + +bool cPlayer::Feed(int a_Food, double a_Saturation) +{ + if (m_FoodLevel >= MAX_FOOD_LEVEL) + { + return false; + } + + m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); + m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); + + SendHealth(); + return true; +} + + + + + +void cPlayer::FoodPoison(int a_NumTicks) +{ + bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); + m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); + if (!HasBeenFoodPoisoned) + { + // TODO: Send the poisoning indication to the client - how? + SendHealth(); + } +} + + + + + +void cPlayer::StartEating(void) +{ + // Set the timer: + m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; + + // Send the packets: + m_World->BroadcastPlayerAnimation(*this, 5); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::FinishEating(void) +{ + // Reset the timer: + m_EatingFinishTick = -1; + + // Send the packets: + m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED); + m_World->BroadcastPlayerAnimation(*this, 0); + m_World->BroadcastEntityMetadata(*this); + + // consume the item: + cItem Item(GetEquippedItem()); + Item.m_ItemCount = 1; + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType); + if (!ItemHandler->EatItem(this, &Item)) + { + return; + } + ItemHandler->OnFoodEaten(m_World, this, &Item); + + GetInventory().RemoveOneEquippedItem(); + + //if the food is mushroom soup, return a bowl to the inventory + if( Item.m_ItemType == E_ITEM_MUSHROOM_SOUP ) { + cItem emptyBowl(E_ITEM_BOWL, 1, 0, ""); + GetInventory().AddItem(emptyBowl, true, true); + } +} + + + + + +void cPlayer::AbortEating(void) +{ + m_EatingFinishTick = -1; + m_World->BroadcastPlayerAnimation(*this, 0); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::SendHealth(void) +{ + if (m_ClientHandle != NULL) + { + m_ClientHandle->SendHealth(); + } +} + + + + + +void cPlayer::SendExperience(void) +{ + if (m_ClientHandle != NULL) + { + m_ClientHandle->SendExperience(); + m_bDirtyExperience = false; + } +} + + + + + +void cPlayer::ClearInventoryPaintSlots(void) +{ + // Clear the list of slots that are being inventory-painted. Used by cWindow only + m_InventoryPaintSlots.clear(); +} + + + + + +void cPlayer::AddInventoryPaintSlot(int a_SlotNum) +{ + // Add a slot to the list for inventory painting. Used by cWindow only + m_InventoryPaintSlots.push_back(a_SlotNum); +} + + + + + +const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const +{ + // Return the list of slots currently stored for inventory painting. Used by cWindow only + return m_InventoryPaintSlots; +} + + + + + +double cPlayer::GetMaxSpeed(void) const +{ + return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed; +} + + + + + +void cPlayer::SetNormalMaxSpeed(double a_Speed) +{ + m_NormalMaxSpeed = a_Speed; + if (!m_IsSprinting) + { + m_ClientHandle->SendPlayerMaxSpeed(); + } +} + + + + + +void cPlayer::SetSprintingMaxSpeed(double a_Speed) +{ + m_SprintingMaxSpeed = a_Speed; + if (m_IsSprinting) + { + m_ClientHandle->SendPlayerMaxSpeed(); + } +} + + + + + +void cPlayer::SetCrouch(bool a_IsCrouched) +{ + // Set the crouch status, broadcast to all visible players + + if (a_IsCrouched == m_IsCrouched) + { + // No change + return; + } + m_IsCrouched = a_IsCrouched; + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::SetSprint(bool a_IsSprinting) +{ + if (a_IsSprinting == m_IsSprinting) + { + // No change + return; + } + + m_IsSprinting = a_IsSprinting; + m_ClientHandle->SendPlayerMaxSpeed(); +} + + + + + +void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if (a_TDI.DamageType != dtInVoid) + { + if (IsGameModeCreative()) + { + // No damage / health in creative mode + return; + } + } + + super::DoTakeDamage(a_TDI); + + // Any kind of damage adds food exhaustion + AddFoodExhaustion(0.3f); + + SendHealth(); +} + + + + + +void cPlayer::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + + if (m_Health > 0) + { + return; // not dead yet =] + } + + m_bVisible = false; // So new clients don't see the player + + // Puke out all the items + cItems Pickups; + m_Inventory.CopyToItems(Pickups); + m_Inventory.Clear(); + m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); + SaveToDisk(); // Save it, yeah the world is a tough place ! +} + + + + + +void cPlayer::Respawn(void) +{ + m_Health = GetMaxHealth(); + + // Reset food level: + m_FoodLevel = MAX_FOOD_LEVEL; + m_FoodSaturationLevel = 5; + + // Reset Experience + m_CurrentXp = 0; + m_LifetimeTotalXp = 0; + // ToDo: send score to client? How? + + m_ClientHandle->SendRespawn(); + + // Extinguish the fire: + StopBurning(); + + TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); + + SetVisible(true); +} + + + + + +double cPlayer::GetEyeHeight(void) const +{ + return m_Stance; +} + + + + +Vector3d cPlayer::GetEyePosition(void) const +{ + return Vector3d( GetPosX(), m_Stance, GetPosZ() ); +} + + + + + +bool cPlayer::IsGameModeCreative(void) const +{ + return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative + ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative +} + + + + + +bool cPlayer::IsGameModeSurvival(void) const +{ + return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival + ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival +} + + + + + +bool cPlayer::IsGameModeAdventure(void) const +{ + return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure + ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure +} + + + + + +void cPlayer::OpenWindow(cWindow * a_Window) +{ + if (a_Window != m_CurrentWindow) + { + CloseWindow(false); + } + a_Window->OpenedByPlayer(*this); + m_CurrentWindow = a_Window; + a_Window->SendWholeWindow(*GetClientHandle()); +} + + + + + +void cPlayer::CloseWindow(bool a_CanRefuse) +{ + if (m_CurrentWindow == NULL) + { + m_CurrentWindow = m_InventoryWindow; + return; + } + + if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) + { + // Close accepted, go back to inventory window (the default): + m_CurrentWindow = m_InventoryWindow; + } + else + { + // Re-open the window + m_CurrentWindow->OpenedByPlayer(*this); + m_CurrentWindow->SendWholeWindow(*GetClientHandle()); + } +} + + + + + +void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) +{ + if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID)) + { + return; + } + CloseWindow(); +} + + + + + +void cPlayer::SetLastBlockActionTime() +{ + if (m_World != NULL) + { + m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; + } +} + + + + + +void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) +{ + m_LastBlockActionCnt = a_LastBlockActionCnt; +} + + + + + +void cPlayer::SetGameMode(eGameMode a_GameMode) +{ + if ((a_GameMode < gmMin) || (a_GameMode >= gmMax)) + { + LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); + return; + } + + if (m_GameMode == a_GameMode) + { + // Gamemode already set + return; + } + + m_GameMode = a_GameMode; + m_ClientHandle->SendGameMode(a_GameMode); +} + + + + + +void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) +{ + m_GameMode = a_GameMode; +} + + + + + +void cPlayer::SetIP(const AString & a_IP) +{ + m_IP = a_IP; +} + + + + + +void cPlayer::SendMessage(const AString & a_Message) +{ + m_ClientHandle->SendChat(a_Message); +} + + + + + +void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +{ + SetPosition( a_PosX, a_PosY, a_PosZ ); + m_LastGroundHeight = (float)a_PosY; + + m_World->BroadcastTeleportEntity(*this, GetClientHandle()); + m_ClientHandle->SendPlayerMoveLook(); +} + + + + + +Vector3d cPlayer::GetThrowStartPos(void) const +{ + Vector3d res = GetEyePosition(); + + // Adjust the position to be just outside the player's bounding box: + res.x += 0.16 * cos(GetPitch()); + res.y += -0.1; + res.z += 0.16 * sin(GetPitch()); + + return res; +} + + + + + +Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const +{ + Vector3d res = GetLookVector(); + res.Normalize(); + + // TODO: Add a slight random change (+-0.0075 in each direction) + + return res * a_SpeedCoeff; +} + + + + + +void cPlayer::MoveTo( const Vector3d & a_NewPos ) +{ + if ((a_NewPos.y < -990) && (GetPosY() > -100)) + { + // When attached to an entity, the client sends position packets with weird coords: + // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 + // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while + // the client may still send more of these nonsensical packets. + if (m_AttachedTo != NULL) + { + Vector3d AddSpeed(a_NewPos); + AddSpeed.y = 0; + m_AttachedTo->AddSpeed(AddSpeed); + } + return; + } + + // TODO: should do some checks to see if player is not moving through terrain + // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too + + SetPosition( a_NewPos ); + SetStance(a_NewPos.y + 1.62); +} + + + + + +void cPlayer::SetVisible(bool a_bVisible) +{ + if (a_bVisible && !m_bVisible) // Make visible + { + m_bVisible = true; + m_World->BroadcastSpawnEntity(*this); + } + if (!a_bVisible && m_bVisible) + { + m_bVisible = false; + m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients + } +} + + + + + +void cPlayer::AddToGroup( const AString & a_GroupName ) +{ + cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); + m_Groups.push_back( Group ); + LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); + ResolveGroups(); + ResolvePermissions(); +} + + + + + +void cPlayer::RemoveFromGroup( const AString & a_GroupName ) +{ + bool bRemoved = false; + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->GetName().compare(a_GroupName ) == 0 ) + { + m_Groups.erase( itr ); + bRemoved = true; + break; + } + } + + if( bRemoved ) + { + LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); + ResolveGroups(); + ResolvePermissions(); + } + else + { + LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() ); + } +} + + + + + +bool cPlayer::CanUseCommand( const AString & a_Command ) +{ + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + + + + + +bool cPlayer::HasPermission(const AString & a_Permission) +{ + if (a_Permission.empty()) + { + // Empty permission request is always granted + return true; + } + + AStringVector Split = StringSplit( a_Permission, "." ); + PermissionMap Possibilities = m_ResolvedPermissions; + // Now search the namespaces + while( Possibilities.begin() != Possibilities.end() ) + { + PermissionMap::iterator itr = Possibilities.begin(); + if( itr->second ) + { + AStringVector OtherSplit = StringSplit( itr->first, "." ); + if( OtherSplit.size() <= Split.size() ) + { + unsigned int i; + for( i = 0; i < OtherSplit.size(); ++i ) + { + if( OtherSplit[i].compare( Split[i] ) != 0 ) + { + if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! + break; + } + } + if( i == Split.size() ) return true; + } + } + Possibilities.erase( itr ); + } + + // Nothing that matched :( + return false; +} + + + + + +bool cPlayer::IsInGroup( const AString & a_Group ) +{ + for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) + { + if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 ) + return true; + } + return false; +} + + + + + +void cPlayer::ResolvePermissions() +{ + m_ResolvedPermissions.clear(); // Start with an empty map yo~ + + // Copy all player specific permissions into the resolved permissions map + for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + + for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) + { + const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); + for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + } +} + + + + + +void cPlayer::ResolveGroups() +{ + // Clear resolved groups first + m_ResolvedGroups.clear(); + + // Get a complete resolved list of all groups the player is in + std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates + GroupList ToIterate; + for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) + { + ToIterate.push_back( *GroupItr ); + } + while( ToIterate.begin() != ToIterate.end() ) + { + cGroup* CurrentGroup = *ToIterate.begin(); + if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) + { + LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", + m_PlayerName.c_str(), CurrentGroup->GetName().c_str() + ); + } + else + { + AllGroups[ CurrentGroup ] = true; + m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list + const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); + for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) + { + if( AllGroups.find( *itr ) != AllGroups.end() ) + { + LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); + continue; + } + ToIterate.push_back( *itr ); + } + } + ToIterate.erase( ToIterate.begin() ); + } +} + + + + + +AString cPlayer::GetColor(void) const +{ + if ( m_Color != '-' ) + { + return cChatColor::MakeColor( m_Color ); + } + + if ( m_Groups.size() < 1 ) + { + return cChatColor::White; + } + + return (*m_Groups.begin())->GetColor(); +} + + + + + +void cPlayer::TossItem( + bool a_bDraggingItem, + char a_Amount /* = 1 */, + short a_CreateType /* = 0 */, + short a_CreateHealth /* = 0 */ +) +{ + cItems Drops; + if (a_CreateType != 0) + { + // Just create item without touching the inventory (used in creative mode) + Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); + } + else + { + // Drop an item from the inventory: + if (a_bDraggingItem) + { + cItem & Item = GetDraggingItem(); + if (!Item.IsEmpty()) + { + char OriginalItemAmount = Item.m_ItemCount; + Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); + Drops.push_back(Item); + if (OriginalItemAmount > a_Amount) + { + Item.m_ItemCount = OriginalItemAmount - (char)a_Amount; + } + else + { + Item.Empty(); + } + } + } + else + { + // Else drop equipped item + cItem DroppedItem(GetInventory().GetEquippedItem()); + if (!DroppedItem.IsEmpty()) + { + if (GetInventory().RemoveOneEquippedItem()) + { + DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again + Drops.push_back(DroppedItem); + } + } + } + } + double vX = 0, vY = 0, vZ = 0; + EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player +} + + + + + +bool cPlayer::MoveToWorld(const char * a_WorldName) +{ + cWorld * World = cRoot::Get()->GetWorld(a_WorldName); + if (World == NULL) + { + LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); + return false; + } + + eDimension OldDimension = m_World->GetDimension(); + + // Remove all links to the old world + m_World->RemovePlayer(this); + m_ClientHandle->RemoveFromAllChunks(); + m_World->RemoveEntity(this); + + // If the dimension is different, we can send the respawn packet + // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 + m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension())); + + // Add player to all the necessary parts of the new world + SetWorld(World); + World->AddEntity(this); + World->AddPlayer(this); + + return true; +} + + + + + +void cPlayer::LoadPermissionsFromDisk() +{ + m_Groups.clear(); + m_Permissions.clear(); + + cIniFile IniFile; + if (IniFile.ReadFile("users.ini")) + { + std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); + if (!Groups.empty()) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++ ) + { + AddToGroup( Split[i].c_str() ); + } + } + else + { + AddToGroup("Default"); + } + + m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; + } + else + { + LOGWARN("Failed to read the users.ini file. The player will be added only to the Default group."); + AddToGroup("Default"); + } + ResolvePermissions(); +} + + + + +bool cPlayer::LoadFromDisk() +{ + LoadPermissionsFromDisk(); + + // Log player permissions, cause it's what the cool kids do + LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); + for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) LOGINFO("%s", itr->first.c_str() ); + } + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + // This is a new player whom we haven't seen yet, bail out, let them have the defaults + return false; + } + + AString buffer; + if (f.ReadRestOfFile(buffer) != f.GetSize()) + { + LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); + return false; + } + f.Close(); //cool kids play nice + + Json::Value root; + Json::Reader reader; + if (!reader.parse(buffer, root, false)) + { + LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); + } + + Json::Value & JSON_PlayerPosition = root["position"]; + if (JSON_PlayerPosition.size() == 3) + { + SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); + SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); + SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_LastFoodPos = GetPosition(); + } + + Json::Value & JSON_PlayerRotation = root["rotation"]; + if (JSON_PlayerRotation.size() == 3) + { + SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); + SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); + SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); + } + + m_Health = root.get("health", 0).asInt(); + m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt(); + m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); + m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); + m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); + m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); + m_LifetimeTotalXp = (short) root.get("xpTotal", 0).asInt(); + m_CurrentXp = (short) root.get("xpCurrent", 0).asInt(); + + //SetExperience(root.get("experience", 0).asInt()); + + m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); + + m_Inventory.LoadFromJson(root["inventory"]); + + m_LoadedWorldName = root.get("world", "world").asString(); + + LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() + ); + + return true; +} + + + + + +bool cPlayer::SaveToDisk() +{ + cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); + + // create the JSON data + Json::Value JSON_PlayerPosition; + JSON_PlayerPosition.append(Json::Value(GetPosX())); + JSON_PlayerPosition.append(Json::Value(GetPosY())); + JSON_PlayerPosition.append(Json::Value(GetPosZ())); + + Json::Value JSON_PlayerRotation; + JSON_PlayerRotation.append(Json::Value(GetRotation())); + JSON_PlayerRotation.append(Json::Value(GetPitch())); + JSON_PlayerRotation.append(Json::Value(GetRoll())); + + Json::Value JSON_Inventory; + m_Inventory.SaveToJson(JSON_Inventory); + + Json::Value root; + root["position"] = JSON_PlayerPosition; + root["rotation"] = JSON_PlayerRotation; + root["inventory"] = JSON_Inventory; + root["health"] = m_Health; + root["xpTotal"] = m_LifetimeTotalXp; + root["xpCurrent"] = m_CurrentXp; + root["air"] = m_AirLevel; + root["food"] = m_FoodLevel; + root["foodSaturation"] = m_FoodSaturationLevel; + root["foodTickTimer"] = m_FoodTickTimer; + root["foodExhaustion"] = m_FoodExhaustionLevel; + root["world"] = GetWorld()->GetName(); + + if (m_GameMode == GetWorld()->GetGameMode()) + { + root["gamemode"] = (int) eGameMode_NotSet; + } + else + { + root["gamemode"] = (int) m_GameMode; + } + + Json::StyledWriter writer; + std::string JsonData = writer.write(root); + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmWrite)) + { + LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); + return false; + } + if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) + { + LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); + return false; + } + return true; +} + + + + + +cPlayer::StringList cPlayer::GetResolvedPermissions() +{ + StringList Permissions; + + const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; + for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) Permissions.push_back( itr->first ); + } + + return Permissions; +} + + + + + +void cPlayer::UseEquippedItem(void) +{ + if (IsGameModeCreative()) // No damage in creative + { + return; + } + + GetInventory().DamageEquippedItem(); +} + + + + + +void cPlayer::SetSwimState(cChunk & a_Chunk) +{ + int RelY = (int)floor(m_LastPosY + 0.1); + if ((RelY < 0) || (RelY >= cChunkDef::Height - 1)) + { + m_IsSwimming = false; + m_IsSubmerged = false; + return; + } + + BLOCKTYPE BlockIn; + int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width; + + // Check if the player is swimming: + // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk + if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)) + { + // This sometimes happens on Linux machines + // Ref.: http://forum.mc-server.org/showthread.php?tid=1244 + LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}", + RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ() + ); + m_IsSwimming = false; + m_IsSubmerged = false; + return; + } + m_IsSwimming = IsBlockWater(BlockIn); + + // Check if the player is submerged: + VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn)); + m_IsSubmerged = IsBlockWater(BlockIn); +} + + + + + +void cPlayer::HandleAir(void) +{ + // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format + // see if the player is /submerged/ water (block above is water) + // Get the type of block the player's standing in: + + if (IsSubmerged()) + { + // either reduce air level or damage player + if (m_AirLevel < 1) + { + if (m_AirTickTimer < 1) + { + // damage player + TakeDamage(dtDrowning, NULL, 1, 1, 0); + // reset timer + m_AirTickTimer = DROWNING_TICKS; + } + else + { + m_AirTickTimer -= 1; + } + } + else + { + // reduce air supply + m_AirLevel -= 1; + } + } + else + { + // set the air back to maximum + m_AirLevel = MAX_AIR_LEVEL; + m_AirTickTimer = DROWNING_TICKS; + } +} + + + + + +void cPlayer::HandleFood(void) +{ + // Ref.: http://www.minecraftwiki.net/wiki/Hunger + + // Remember the food level before processing, for later comparison + int LastFoodLevel = m_FoodLevel; + + // Heal or damage, based on the food level, using the m_FoodTickTimer: + if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) + { + m_FoodTickTimer++; + if (m_FoodTickTimer >= 80) + { + m_FoodTickTimer = 0; + + if (m_FoodLevel >= 17) + { + // Regenerate health from food, incur 3 pts of food exhaustion: + Heal(1); + m_FoodExhaustionLevel += 3; + } + else if (m_FoodLevel <= 0) + { + // Damage from starving + TakeDamage(dtStarving, NULL, 1, 1, 0); + } + } + } + + // Apply food poisoning food exhaustion: + if (m_FoodPoisonedTicksRemaining > 0) + { + m_FoodPoisonedTicksRemaining--; + m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick + } + + // Apply food exhaustion that has accumulated: + if (m_FoodExhaustionLevel >= 4) + { + m_FoodExhaustionLevel -= 4; + + if (m_FoodSaturationLevel >= 1) + { + m_FoodSaturationLevel -= 1; + } + else + { + m_FoodLevel = std::max(m_FoodLevel - 1, 0); + } + } + + if (m_FoodLevel != LastFoodLevel) + { + SendHealth(); + } +} + + + + + +void cPlayer::ApplyFoodExhaustionFromMovement() +{ + if (IsGameModeCreative()) + { + return; + } + + // Calculate the distance travelled, update the last pos: + Vector3d Movement(GetPosition() - m_LastFoodPos); + Movement.y = 0; // Only take XZ movement into account + m_LastFoodPos = GetPosition(); + + // If riding anything, apply no food exhaustion + if (m_AttachedTo != NULL) + { + return; + } + + // Apply the exhaustion based on distance travelled: + double BaseExhaustion = Movement.Length(); + if (IsSprinting()) + { + // 0.1 pt per meter sprinted + BaseExhaustion = BaseExhaustion * 0.1; + } + else if (IsSwimming()) + { + // 0.015 pt per meter swum + BaseExhaustion = BaseExhaustion * 0.015; + } + else + { + // 0.01 pt per meter walked / sneaked + BaseExhaustion = BaseExhaustion * 0.01; + } + m_FoodExhaustionLevel += BaseExhaustion; +} + + + + diff --git a/src/Entities/Player.h b/src/Entities/Player.h new file mode 100644 index 000000000..44cab7d74 --- /dev/null +++ b/src/Entities/Player.h @@ -0,0 +1,456 @@ + +#pragma once + +#include "Pawn.h" +#include "../Inventory.h" +#include "../Defines.h" +#include "../World.h" + + + + + +class cGroup; +class cWindow; +class cClientHandle; + + + + + +// tolua_begin +class cPlayer : + public cPawn +{ + typedef cPawn super; + +public: + enum + { + MAX_HEALTH = 20, + MAX_FOOD_LEVEL = 20, + EATING_TICKS = 30, ///< Number of ticks it takes to eat an item + MAX_AIR_LEVEL = 300, + DROWNING_TICKS = 10, //number of ticks per heart of damage + } ; + // tolua_end + + CLASS_PROTODEF(cPlayer) + + + cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); + virtual ~cPlayer(); + + virtual bool Initialize(cWorld * a_World) override; + + virtual void SpawnOn(cClientHandle & a_Client) override; + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { }; + + /// Returns the curently equipped weapon; empty item if none + virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } + + /// Returns the currently equipped helmet; empty item if nonte + virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } + + /// Returns the currently equipped chestplate; empty item if none + virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } + + /// Returns the currently equipped leggings; empty item if none + virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } + + /// Returns the currently equipped boots; empty item if none + virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } + + + // tolua_begin + + /** Sets the experience total + Returns true on success + "should" really only be called at init or player death, plugins excepted + */ + bool SetCurrentExperience(short a_XpTotal); + + /* changes Xp by Xp_delta, you "shouldn't" inc more than MAX_EXPERIENCE_ORB_SIZE + Wont't allow xp to go negative + Returns the new current experience, -1 on error + */ + short DeltaExperience(short a_Xp_delta); + + /// Gets the experience total - XpTotal for score on death + inline short GetXpLifetimeTotal(void) { return m_LifetimeTotalXp; } + + /// Gets the currrent experience + inline short GetCurrentXp(void) { return m_CurrentXp; } + + /// Gets the current level - XpLevel + short GetXpLevel(void); + + /// Gets the experience bar percentage - XpP + float GetXpPercentage(void); + + /// Caculates the amount of XP needed for a given level, ref: http://minecraft.gamepedia.com/XP + static short XpForLevel(short int a_Level); + + /// inverse of XpForLevel, ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations + static short CalcLevelFromXp(short int a_CurrentXp); + + // tolua_end + + /// Starts charging the equipped bow + void StartChargingBow(void); + + /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged + int FinishChargingBow(void); + + /// Cancels the current bow charging + void CancelChargingBow(void); + + /// Returns true if the player is currently charging the bow + bool IsChargingBow(void) const { return m_IsChargingBow; } + + void SetTouchGround( bool a_bTouchGround ); + inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; } + double GetEyeHeight(void) const; // tolua_export + Vector3d GetEyePosition(void) const; // tolua_export + inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export + inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. + inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export + inline const cInventory & GetInventory(void) const { return m_Inventory; } + + inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export + + virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; + + // tolua_begin + + /// Returns the position where projectiles thrown by this player should start, player eye position + adjustment + Vector3d GetThrowStartPos(void) const; + + /// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. + Vector3d GetThrowSpeed(double a_SpeedCoeff) const; + + /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable + eGameMode GetGameMode(void) const { return m_GameMode; } + + /// Returns the current effective gamemode (inherited gamemode is resolved before returning) + eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } + + /** Sets the gamemode for the player. + The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. + Updates the gamemode on the client (sends the packet) + */ + void SetGameMode(eGameMode a_GameMode); + + /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world + bool IsGameModeCreative(void) const; + + /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world + bool IsGameModeSurvival(void) const; + + /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world + bool IsGameModeAdventure(void) const; + + AString GetIP(void) const { return m_IP; } // tolua_export + + // tolua_end + + void SetIP(const AString & a_IP); + + float GetLastBlockActionTime() { return m_LastBlockActionTime; } + int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } + void SetLastBlockActionCnt( int ); + void SetLastBlockActionTime(); + + // Sets the current gamemode, doesn't check validity, doesn't send update packets to client + void LoginSetGameMode(eGameMode a_GameMode); + + /// Tries to move to a new position, with attachment-related checks (y == -999) + void MoveTo(const Vector3d & a_NewPos); // tolua_export + + cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export + const cWindow * GetWindow(void) const { return m_CurrentWindow; } + + /// Opens the specified window; closes the current one first using CloseWindow() + void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp + + // tolua_begin + + /// Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true + void CloseWindow(bool a_CanRefuse = true); + + /// Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow + void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); + + cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } + + void SendMessage(const AString & a_Message); + + const AString & GetName(void) const { return m_PlayerName; } + void SetName(const AString & a_Name) { m_PlayerName = a_Name; } + + // tolua_end + + typedef std::list< cGroup* > GroupList; + typedef std::list< std::string > StringList; + + /// Adds a player to existing group or creates a new group when it doesn't exist + void AddToGroup( const AString & a_GroupName ); // tolua_export + + /// Removes a player from the group, resolves permissions and group inheritance (case sensitive) + void RemoveFromGroup( const AString & a_GroupName ); // tolua_export + + bool CanUseCommand( const AString & a_Command ); // tolua_export + bool HasPermission( const AString & a_Permission ); // tolua_export + const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << + StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << + bool IsInGroup( const AString & a_Group ); // tolua_export + + // tolua_begin + + /// Returns the full color code to use for this player, based on their primary group or set in m_Color + AString GetColor(void) const; + + void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); + + /// Heals the player by the specified amount of HPs (positive only); sends health update + void Heal(int a_Health); + + int GetFoodLevel (void) const { return m_FoodLevel; } + double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } + int GetFoodTickTimer (void) const { return m_FoodTickTimer; } + double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } + int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; } + + int GetAirLevel (void) const { return m_AirLevel; } + + /// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore + bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } + + void SetFoodLevel (int a_FoodLevel); + void SetFoodSaturationLevel (double a_FoodSaturationLevel); + void SetFoodTickTimer (int a_FoodTickTimer); + void SetFoodExhaustionLevel (double a_FoodExhaustionLevel); + void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining); + + /// Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" + bool Feed(int a_Food, double a_Saturation); + + /// Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. + void AddFoodExhaustion(double a_Exhaustion) + { + m_FoodExhaustionLevel += a_Exhaustion; + } + + /// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two + void FoodPoison(int a_NumTicks); + + /// Returns true if the player is currently in the process of eating the currently equipped item + bool IsEating(void) const { return (m_EatingFinishTick >= 0); } + + // tolua_end + + /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet + void StartEating(void); + + /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets + void FinishEating(void); + + /// Aborts the current eating operation + void AbortEating(void); + + virtual void KilledBy(cEntity * a_Killer) override; + + void Respawn(void); // tolua_export + + void SetVisible( bool a_bVisible ); // tolua_export + bool IsVisible(void) const { return m_bVisible; } // tolua_export + + bool MoveToWorld(const char * a_WorldName); // tolua_export + + bool SaveToDisk(void); + bool LoadFromDisk(void); + void LoadPermissionsFromDisk(void); // tolua_export + + const AString & GetLoadedWorldName() { return m_LoadedWorldName; } + + void UseEquippedItem(void); + + void SendHealth(void); + + void SendExperience(void); + + // In UI windows, the item that the player is dragging: + bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); } + cItem & GetDraggingItem(void) {return m_DraggingItem; } + + // In UI windows, when inventory-painting: + /// Clears the list of slots that are being inventory-painted. To be used by cWindow only + void ClearInventoryPaintSlots(void); + + /// Adds a slot to the list for inventory painting. To be used by cWindow only + void AddInventoryPaintSlot(int a_SlotNum); + + /// Returns the list of slots currently stored for inventory painting. To be used by cWindow only + const cSlotNums & GetInventoryPaintSlots(void) const; + + // tolua_begin + + /// Returns the current maximum speed, as reported in the 1.6.1+ protocol (takes current sprinting state into account) + double GetMaxSpeed(void) const; + + /// Gets the normal maximum speed, as reported in the 1.6.1+ protocol, in the protocol units + double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } + + /// Gets the sprinting maximum speed, as reported in the 1.6.1+ protocol, in the protocol units + double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } + + /// Sets the normal maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. + void SetNormalMaxSpeed(double a_Speed); + + /// Sets the sprinting maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. + void SetSprintingMaxSpeed(double a_Speed); + + /// Sets the crouch status, broadcasts to all visible players + void SetCrouch(bool a_IsCrouched); + + /// Starts or stops sprinting, sends the max speed update to the client, if needed + void SetSprint(bool a_IsSprinting); + + /// Returns whether the player is swimming or not + virtual bool IsSwimming(void) const{ return m_IsSwimming; } + + /// Return whether the player is under water or not + virtual bool IsSubmerged(void) const{ return m_IsSubmerged; } + + // tolua_end + + // cEntity overrides: + virtual bool IsCrouched (void) const { return m_IsCrouched; } + virtual bool IsSprinting(void) const { return m_IsSprinting; } + virtual bool IsRclking (void) const { return IsEating(); } + +protected: + typedef std::map< std::string, bool > PermissionMap; + PermissionMap m_ResolvedPermissions; + PermissionMap m_Permissions; + + GroupList m_ResolvedGroups; + GroupList m_Groups; + + std::string m_PlayerName; + std::string m_LoadedWorldName; + + /// Xp Level stuff + enum + { + XP_TO_LEVEL15 = 255, + XP_PER_LEVEL_TO15 = 17, + XP_TO_LEVEL30 = 825 + } ; + + /// Player's air level (for swimming) + int m_AirLevel; + + /// used to time ticks between damage taken via drowning/suffocation + int m_AirTickTimer; + + bool m_bVisible; + + // Food-related variables: + /// Represents the food bar, one point equals half a "drumstick" + int m_FoodLevel; + + /// "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel + double m_FoodSaturationLevel; + + /// Count-up to the healing or damaging action, based on m_FoodLevel + int m_FoodTickTimer; + + /// A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little + double m_FoodExhaustionLevel; + + /// Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned + int m_FoodPoisonedTicksRemaining; + + /// Last position that has been recorded for food-related processing: + Vector3d m_LastFoodPos; + + float m_LastJumpHeight; + float m_LastGroundHeight; + bool m_bTouchGround; + double m_Stance; + cInventory m_Inventory; + cWindow * m_CurrentWindow; + cWindow * m_InventoryWindow; + + float m_TimeLastPickupCheck; + + void ResolvePermissions(); + + void ResolveGroups(); + char m_Color; + + float m_LastBlockActionTime; + int m_LastBlockActionCnt; + eGameMode m_GameMode; + std::string m_IP; + + cItem m_DraggingItem; + + long long m_LastPlayerListTime; + static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second + + cClientHandle * m_ClientHandle; + + cSlotNums m_InventoryPaintSlots; + + /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is walking. 0.1 by default + double m_NormalMaxSpeed; + + /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is sprinting. 0.13 by default + double m_SprintingMaxSpeed; + + bool m_IsCrouched; + bool m_IsSprinting; + + bool m_IsSwimming; + bool m_IsSubmerged; + + /// The world tick in which eating will be finished. -1 if not eating + Int64 m_EatingFinishTick; + + /// Player Xp level + short int m_LifetimeTotalXp; + short int m_CurrentXp; + + // flag saying we need to send a xp update to client + bool m_bDirtyExperience; + + bool m_IsChargingBow; + int m_BowCharge; + + + virtual void Destroyed(void); + + /// Filters out damage for creative mode + virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + + /// Called in each tick to handle food-related processing + void HandleFood(void); + + /// Called in each tick to handle air-related processing i.e. drowning + void HandleAir(); + + /// Called once per tick to set IsSwimming and IsSubmerged + void SetSwimState(cChunk & a_Chunk); + + /// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) + void ApplyFoodExhaustionFromMovement(); +} ; // tolua_export + + + + diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp new file mode 100644 index 000000000..fb25aea35 --- /dev/null +++ b/src/Entities/ProjectileEntity.cpp @@ -0,0 +1,855 @@ + +// ProjectileEntity.cpp + +// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types + +#include "Globals.h" +#include "ProjectileEntity.h" +#include "../ClientHandle.h" +#include "Player.h" +#include "../LineBlockTracer.h" +#include "../BoundingBox.h" +#include "../ChunkMap.h" +#include "../Chunk.h" + + + + + +/// Converts an angle in radians into a byte representation used by the network protocol +#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProjectileTracerCallback: + +class cProjectileTracerCallback : + public cBlockTracer::cCallbacks +{ +public: + cProjectileTracerCallback(cProjectileEntity * a_Projectile) : + m_Projectile(a_Projectile), + m_SlowdownCoeff(0.99) // Default slowdown when not in water + { + } + + double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; } + +protected: + cProjectileEntity * m_Projectile; + double m_SlowdownCoeff; + + // cCallbacks overrides: + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + { + /* + // DEBUG: + LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)", + a_BlockType, a_BlockMeta, + a_BlockX, a_BlockY, a_BlockZ, a_EntryFace, + g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid", + ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() + ); + */ + + if (g_BlockIsSolid[a_BlockType]) + { + // The projectile hit a solid block + // Calculate the exact hit coords: + cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); + Vector3d Line1 = m_Projectile->GetPosition(); + Vector3d Line2 = Line1 + m_Projectile->GetSpeed(); + double LineCoeff = 0; + char Face; + if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face)) + { + Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff; + m_Projectile->OnHitSolidBlock(Intersection, Face); + return true; + } + else + { + LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit."); + } + } + + // Convey some special effects from special blocks: + switch (a_BlockType) + { + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + m_Projectile->StartBurning(30); + m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.9); // Slow down to 0.9* the speed each tick when moving through lava + break; + } + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + m_Projectile->StopBurning(); + m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.8); // Slow down to 0.8* the speed each tick when moving through water + break; + } + } // switch (a_BlockType) + + // Continue tracing + return false; + } +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProjectileEntityCollisionCallback: + +class cProjectileEntityCollisionCallback : + public cEntityCallback +{ +public: + cProjectileEntityCollisionCallback(cProjectileEntity * a_Projectile, const Vector3d & a_Pos, const Vector3d & a_NextPos) : + m_Projectile(a_Projectile), + m_Pos(a_Pos), + m_NextPos(a_NextPos), + m_MinCoeff(1), + m_HitEntity(NULL) + { + } + + + virtual bool Item(cEntity * a_Entity) override + { + if ( + (a_Entity == m_Projectile) || // Do not check collisions with self + (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile + ) + { + // TODO: Don't check creator only for the first 5 ticks + // so that arrows stuck in ground and dug up can hurt the player + return false; + } + + cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); + + // Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline. + // The results should be good enough for our purposes + double LineCoeff; + char Face; + EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2); + if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face)) + { + // No intersection whatsoever + return false; + } + + // TODO: Some entities don't interact with the projectiles (pickups, falling blocks) + // TODO: Allow plugins to interfere about which entities can be hit + + if (LineCoeff < m_MinCoeff) + { + // The entity is closer than anything we've stored so far, replace it as the potential victim + m_MinCoeff = LineCoeff; + m_HitEntity = a_Entity; + } + + // Don't break the enumeration, we want all the entities + return false; + } + + /// Returns the nearest entity that was hit, after the enumeration has been completed + cEntity * GetHitEntity(void) const { return m_HitEntity; } + + /// Returns the line coeff where the hit was encountered, after the enumeration has been completed + double GetMinCoeff(void) const { return m_MinCoeff; } + + /// Returns true if the callback has encountered a true hit + bool HasHit(void) const { return (m_MinCoeff < 1); } + +protected: + cProjectileEntity * m_Projectile; + const Vector3d & m_Pos; + const Vector3d & m_NextPos; + double m_MinCoeff; // The coefficient of the nearest hit on the Pos line + + // Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback + // is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing + cEntity * m_HitEntity; // The nearest hit entity +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProjectileEntity: + +cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) : + super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height), + m_ProjectileKind(a_Kind), + m_Creator(a_Creator), + m_IsInGround(false) +{ +} + + + + + +cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) : + super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height), + m_ProjectileKind(a_Kind), + m_Creator(a_Creator), + m_IsInGround(false) +{ + SetSpeed(a_Speed); + SetRotationFromSpeed(); + SetPitchFromSpeed(); +} + + + + + +cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed) +{ + Vector3d Speed; + if (a_Speed != NULL) + { + Speed = *a_Speed; + } + + switch (a_Kind) + { + case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed); + case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z ); + // TODO: the rest + } + + LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); + return NULL; +} + + + + + +void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // Set the position based on what face was hit: + SetPosition(a_HitPos); + SetSpeed(0, 0, 0); + + // DEBUG: + LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d", + m_UniqueID, + a_HitPos.x, a_HitPos.y, a_HitPos.z, + a_HitFace + ); + + m_IsInGround = true; +} + + + + + +AString cProjectileEntity::GetMCAClassName(void) const +{ + switch (m_ProjectileKind) + { + case pkArrow: return "Arrow"; + case pkSnowball: return "Snowball"; + case pkEgg: return "Egg"; + case pkGhastFireball: return "Fireball"; + case pkFireCharge: return "SmallFireball"; + case pkEnderPearl: return "ThrownEnderPearl"; + case pkExpBottle: return "ThrownExpBottle"; + case pkSplashPotion: return "ThrownPotion"; + case pkWitherSkull: return "WitherSkull"; + case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? + } + ASSERT(!"Unhandled projectile entity kind!"); + return ""; +} + + + + + +void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (GetProjectileKind() != pkArrow) // See cArrow::Tick + { + BroadcastMovementUpdate(); + } +} + + + + + +void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + if (m_IsInGround) + { + // Already-grounded projectiles don't move at all + return; + } + + Vector3d PerTickSpeed = GetSpeed() / 20; + Vector3d Pos = GetPosition(); + + // Trace the tick's worth of movement as a line: + Vector3d NextPos = Pos + PerTickSpeed; + cProjectileTracerCallback TracerCallback(this); + if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) + { + // Something has been hit, abort all other processing + return; + } + // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff + + // Test for entity collisions: + cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos); + a_Chunk.ForEachEntity(EntityCollisionCallback); + if (EntityCollisionCallback.HasHit()) + { + // An entity was hit: + Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff(); + + // DEBUG: + LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)", + m_UniqueID, + EntityCollisionCallback.GetHitEntity()->GetUniqueID(), + EntityCollisionCallback.GetHitEntity()->GetClass(), + HitPos.x, HitPos.y, HitPos.z, + EntityCollisionCallback.GetMinCoeff() + ); + + OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); + } + // TODO: Test the entities in the neighboring chunks, too + + // Update the position: + SetPosition(NextPos); + + // Add slowdown and gravity effect to the speed: + Vector3d NewSpeed(GetSpeed()); + NewSpeed.y += m_Gravity / 20; + NewSpeed *= TracerCallback.GetSlowdownCoeff(); + SetSpeed(NewSpeed); + SetRotationFromSpeed(); + SetPitchFromSpeed(); + + // DEBUG: + LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}", + m_UniqueID, + GetPosX(), GetPosY(), GetPosZ(), + GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetRotation(), GetPitch() + ); +} + + + + + +void cProjectileEntity::SpawnOn(cClientHandle & a_Client) +{ + // Default spawning - use the projectile kind to spawn an object: + a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch())); + a_Client.SendEntityMetadata(*this); +} + + + + + +void cProjectileEntity::CollectedBy(cPlayer * a_Dest) +{ + // Overriden in arrow + UNUSED(a_Dest); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cArrowEntity: + +cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5), + m_PickupState(psNoPickup), + m_DamageCoeff(2), + m_IsCritical(false), + m_Timer(0), + m_bIsCollected(false), + m_HitBlockPos(Vector3i(0, 0, 0)), + m_HitGroundTimer(0) +{ + SetSpeed(a_Speed); + SetMass(0.1); + SetRotationFromSpeed(); + SetPitchFromSpeed(); + LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", + m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetRotation(), GetPitch() + ); +} + + + + + +cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : + super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5), + m_PickupState(psInSurvivalOrCreative), + m_DamageCoeff(2), + m_IsCritical((a_Force >= 1)), + m_Timer(0), + m_bIsCollected(false), + m_HitBlockPos(0, 0, 0), + m_HitGroundTimer(0) +{ +} + + + + + +bool cArrowEntity::CanPickup(const cPlayer & a_Player) const +{ + switch (m_PickupState) + { + case psNoPickup: return false; + case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative()); + case psInCreative: return a_Player.IsGameModeCreative(); + } + ASSERT(!"Unhandled pickup state"); + return false; +} + + + + + +void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + if (a_HitFace == BLOCK_FACE_NONE) { return; } + + super::OnHitSolidBlock(a_HitPos, a_HitFace); + int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z; + + switch (a_HitFace) + { + case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed + case BLOCK_FACE_YM: + { + break; + } + default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true); + } + + m_HitBlockPos = Vector3i(a_X, a_Y, a_Z); + + // Broadcast arrow hit sound + m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); +} + + + + + +void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat()) + { + // Not an entity that interacts with an arrow + return; + } + + int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); + if (m_IsCritical) + { + Damage += m_World->GetTickRandomNumber(Damage / 2 + 2); + } + a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); + + // Broadcast successful hit sound + m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + + Destroy(); +} + + + + + +void cArrowEntity::CollectedBy(cPlayer * a_Dest) +{ + if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) + { + int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); + if (NumAdded > 0) // Only play effects if there was space in inventory + { + m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_bIsCollected = true; + } + } +} + + + + + +void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + m_Timer += a_Dt; + + if (m_bIsCollected) + { + if (m_Timer > 500.f) // 0.5 seconds + { + Destroy(); + return; + } + } + else if (m_Timer > 1000 * 60 * 5) // 5 minutes + { + Destroy(); + return; + } + + if (m_IsInGround) + { + // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL + // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing) + // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync + // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position + + if (m_HitGroundTimer != -1) // Sent a teleport already, don't do again + { + if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case + { + m_World->BroadcastTeleportEntity(*this); + m_HitGroundTimer = -1; + } + else + { + m_HitGroundTimer += a_Dt; + } + } + + int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + + if (Chunk == NULL) + { + // Inside an unloaded chunk, abort + return; + } + + if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? + { + m_IsInGround = false; // Yes, begin simulating physics again + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cThrownEggEntity: + +cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + if (m_World->GetTickRandomNumber(7) == 1) + { + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + } + else if (m_World->GetTickRandomNumber(32) == 1) + { + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + } + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cThrownEnderPearlEntity : + +cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // Teleport the creator here, make them take 5 damage: + if (m_Creator != NULL) + { + // TODO: The coords might need some tweaking based on the block face + m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5); + m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); + } + + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cThrownSnowballEntity : + +cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs + + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBottleOEnchantingEntity : + +cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : +super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + // TODO: Spawn experience orbs + + Destroy(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFireworkEntity : + +cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) : +super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ +} + + + + + +void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE)) + { + return; + } + + SetSpeed(0, 0, 0); + SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ()); + + std::cout << a_HitPos.x << " " << a_HitPos.y << " " << a_HitPos.z << std::endl; + + m_IsInGround = true; + + BroadcastMovementUpdate(); +} + + + + + +void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + if (m_IsInGround) + { + if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR) + { + m_IsInGround = false; + } + else + { + return; + } + } + + Vector3d PerTickSpeed = GetSpeed() / 20; + Vector3d Pos = GetPosition(); + + // Trace the tick's worth of movement as a line: + Vector3d NextPos = Pos + PerTickSpeed; + cProjectileTracerCallback TracerCallback(this); + if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) + { + // Something has been hit, abort all other processing + return; + } + // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff + + // Update the position: + SetPosition(NextPos); + + // Add slowdown and gravity effect to the speed: + Vector3d NewSpeed(GetSpeed()); + NewSpeed.y += 2; + NewSpeed *= TracerCallback.GetSlowdownCoeff(); + SetSpeed(NewSpeed); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cGhastFireballEntity : + +cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1) +{ + SetSpeed(a_Speed); + SetGravity(0); +} + + + + + +void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this); +} + + + + + +void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFireChargeEntity : + +cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125) +{ + SetSpeed(a_Speed); + SetGravity(0); +} + + + + + +void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) + { + m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1); + } +} + + + + + +void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); + + // TODO: Some entities are immune to hits + a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning +} + + + + diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h new file mode 100644 index 000000000..959e81ae5 --- /dev/null +++ b/src/Entities/ProjectileEntity.h @@ -0,0 +1,382 @@ + +// ProjectileEntity.h + +// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types + + + + + +#pragma once + +#include "Entity.h" + + + + + +// tolua_begin + +class cProjectileEntity : + public cEntity +{ + typedef cEntity super; + +public: + /// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet. + enum eKind + { + pkArrow = 60, + pkSnowball = 61, + pkEgg = 62, + pkGhastFireball = 63, + pkFireCharge = 64, + pkEnderPearl = 65, + pkExpBottle = 75, + pkSplashPotion = 73, + pkFirework = 76, + pkWitherSkull = 66, + pkFishingFloat = 90, + } ; + + // tolua_end + + CLASS_PROTODEF(cProjectileEntity); + + cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); + cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); + + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL); + + /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace); + + /// Called by the physics blocktracer when the entity hits another entity + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {} + + /// Called by Chunk when the projectile is eligible for player collection + virtual void CollectedBy(cPlayer * a_Dest); + + // tolua_begin + + /// Returns the kind of the projectile (fast class identification) + eKind GetProjectileKind(void) const { return m_ProjectileKind; } + + /// Returns the entity who created this projectile; may be NULL + cEntity * GetCreator(void) { return m_Creator; } + + /// Returns the string that is used as the entity type (class name) in MCA files + AString GetMCAClassName(void) const; + + /// Returns true if the projectile has hit the ground and is stuck there + bool IsInGround(void) const { return m_IsInGround; } + + // tolua_end + + /// Sets the internal InGround flag. To be used by MCA loader only! + void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } + +protected: + eKind m_ProjectileKind; + + /// The entity who has created this projectile; may be NULL (e. g. for dispensers) + cEntity * m_Creator; + + /// True if the projectile has hit the ground and is stuck there + bool m_IsInGround; + + // cEntity overrides: + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void SpawnOn(cClientHandle & a_Client) override; + + // tolua_begin +} ; + + + + + +class cArrowEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field + enum ePickupState + { + psNoPickup = 0, + psInSurvivalOrCreative = 1, + psInCreative = 2, + } ; + + // tolua_end + + CLASS_PROTODEF(cArrowEntity); + + /// Creates a new arrow with psNoPickup state and default damage modifier coeff + cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + + /// Creates a new arrow as shot by a player, initializes it from the player object + cArrowEntity(cPlayer & a_Player, double a_Force); + + // tolua_begin + + /// Returns whether the arrow can be picked up by players + ePickupState GetPickupState(void) const { return m_PickupState; } + + /// Sets a new pickup state + void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; } + + /// Returns the damage modifier coeff. + double GetDamageCoeff(void) const { return m_DamageCoeff; } + + /// Sets the damage modifier coeff + void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } + + /// Returns true if the specified player can pick the arrow up + bool CanPickup(const cPlayer & a_Player) const; + + /// Returns true if the arrow is set as critical + bool IsCritical(void) const { return m_IsCritical; } + + /// Sets the IsCritical flag + void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } + + // tolua_end + +protected: + + /// Determines when the arrow can be picked up by players + ePickupState m_PickupState; + + /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow + double m_DamageCoeff; + + /// If true, the arrow deals more damage + bool m_IsCritical; + + /// Timer for pickup collection animation or five minute timeout + float m_Timer; + + /// Timer for client arrow position confirmation via TeleportEntity + float m_HitGroundTimer; + + /// If true, the arrow is in the process of being collected - don't go to anyone else + bool m_bIsCollected; + + /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air + Vector3i m_HitBlockPos; + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + virtual void CollectedBy(cPlayer * a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + // tolua_begin +} ; + + + + + +class cThrownEggEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownEggEntity); + + cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // tolua_end + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +} ; + + + + + +class cThrownEnderPearlEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownEnderPearlEntity); + + cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // tolua_end + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +} ; + + + + + +class cThrownSnowballEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownSnowballEntity); + + cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +} ; + + + + + +class cExpBottleEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cExpBottleEntity); + + cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + + // tolua_begin + +}; + + + + + +class cFireworkEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cFireworkEntity); + + cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + + // tolua_begin + +}; + + + + + +class cGhastFireballEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cGhastFireballEntity); + + cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // TODO: Deflecting the fireballs by arrow- or sword- hits + + // tolua_begin + +} ; + + + + + +class cFireChargeEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cFireChargeEntity); + + cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // tolua_begin + +} ; + + + + +// tolua_end + + + diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp new file mode 100644 index 000000000..339107b2e --- /dev/null +++ b/src/Entities/TNTEntity.cpp @@ -0,0 +1,62 @@ +#include "Globals.h" + +#include "TNTEntity.h" +#include "../World.h" +#include "../ClientHandle.h" + + + + + +cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) : + super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), + m_Counter(0), + m_MaxFuseTime(a_FuseTimeInSec) +{ +} + + + + + +cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) : + super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), + m_Counter(0), + m_MaxFuseTime(a_FuseTimeInSec) +{ +} + + + + +void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT + m_bDirtyPosition = false; + m_bDirtySpeed = false; + m_bDirtyOrientation = false; + m_bDirtyHead = false; +} + + + + + +void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + BroadcastMovementUpdate(); + float delta_time = a_Dt / 1000; // Convert miliseconds to seconds + m_Counter += delta_time; + if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM + { + Destroy(true); + LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); + m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this); + return; + } +} + + + + diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h new file mode 100644 index 000000000..eb5040e8a --- /dev/null +++ b/src/Entities/TNTEntity.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "Entity.h" + + + + + +class cTNTEntity : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cTNTEntity); + + cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec); + cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec); + + // cEntity overrides: + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +protected: + double m_Counter; ///< How much time has elapsed since the object was created, in seconds + double m_MaxFuseTime; ///< How long the fuse is, in seconds +}; + + + + diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp new file mode 100644 index 000000000..887e4426d --- /dev/null +++ b/src/FastRandom.cpp @@ -0,0 +1,174 @@ + +// FastRandom.cpp + +// Implements the cFastRandom class representing a fast random number generator + +#include "Globals.h" +#include <time.h> +#include "FastRandom.h" + + + + + +#if 0 && defined(_DEBUG) +// Self-test +// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, +// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint +class cFastRandomTest +{ +public: + cFastRandomTest(void) + { + TestInts(); + TestFloats(); + } + + + void TestInts(void) + { + printf("Testing ints...\n"); + cFastRandom rnd; + int sum = 0; + const int BUCKETS = 8; + int Counts[BUCKETS]; + memset(Counts, 0, sizeof(Counts)); + const int ITER = 10000; + for (int i = 0; i < ITER; i++) + { + int v = rnd.NextInt(1000); + ASSERT(v >= 0); + ASSERT(v < 1000); + Counts[v % BUCKETS]++; + sum += v; + } + double avg = (double)sum / ITER; + printf("avg: %f\n", avg); + for (int i = 0; i < BUCKETS; i++) + { + printf(" bucket %d: %d\n", i, Counts[i]); + } + } + + + void TestFloats(void) + { + printf("Testing floats...\n"); + cFastRandom rnd; + float sum = 0; + const int BUCKETS = 8; + int Counts[BUCKETS]; + memset(Counts, 0, sizeof(Counts)); + const int ITER = 10000; + for (int i = 0; i < ITER; i++) + { + float v = rnd.NextFloat(1000); + ASSERT(v >= 0); + ASSERT(v <= 1000); + Counts[((int)v) % BUCKETS]++; + sum += v; + } + sum = sum / ITER; + printf("avg: %f\n", sum); + for (int i = 0; i < BUCKETS; i++) + { + printf(" bucket %d: %d\n", i, Counts[i]); + } + } +} g_Test; + +#endif + + + + + + +int cFastRandom::m_SeedCounter = 0; + + + + + +cFastRandom::cFastRandom(void) : + m_Seed(m_SeedCounter++) +{ +} + + + + + +int cFastRandom::NextInt(int a_Range) +{ + ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges + ASSERT(a_Range > 0); + + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + return ((n / 11) % a_Range); +} + + + + + +int cFastRandom::NextInt(int a_Range, int a_Salt) +{ + ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges + ASSERT(a_Range > 0); + + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + return ((n / 11) % a_Range); +} + + + + + +float cFastRandom::NextFloat(float a_Range) +{ + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + + // Convert the integer into float with the specified range: + return (((float)n / (float)0x7fffffff) * a_Range); +} + + + + + +float cFastRandom::NextFloat(float a_Range, int a_Salt) +{ + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + + // Convert the integer into float with the specified range: + return (((float)n / (float)0x7fffffff) * a_Range); +} + + + + diff --git a/src/FastRandom.h b/src/FastRandom.h new file mode 100644 index 000000000..bf70822cf --- /dev/null +++ b/src/FastRandom.h @@ -0,0 +1,57 @@ + +// FastRandom.h + +// Declares the cFastRandom class representing a fast random number generator + +/* +The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator. +It is fast to instantiate, fast to query next, and partially multi-thread-safe. +It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may +yield duplicate numbers in that case. + +Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is +taken from a global counter and the random is calculated using a counter that is incremented on each use (hence +the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter, +and another that takes an additional "salt" parameter; this salt is used as an additional input to the random, +in order to avoid multi-thread duplication. If two threads both use the class at the same time with different +salts, the values they get will be different. +*/ + + + + + +#pragma once + + + + + +class cFastRandom +{ +public: + cFastRandom(void); + + /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M + int NextInt(int a_Range); + + /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness + int NextInt(int a_Range, int a_Salt); + + /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M + float NextFloat(float a_Range); + + /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness + float NextFloat(float a_Range, int a_Salt); + +protected: + int m_Seed; + int m_Counter; + + /// Counter that is used to initialize the seed, incremented for each object created + static int m_SeedCounter; +} ; + + + + diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp new file mode 100644 index 000000000..2e2276981 --- /dev/null +++ b/src/FurnaceRecipe.cpp @@ -0,0 +1,255 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceRecipe.h" +#include "Item.h" + +#include <fstream> +#include <sstream> + + + + + +typedef std::list< cFurnaceRecipe::Recipe > RecipeList; +typedef std::list< cFurnaceRecipe::Fuel > FuelList; + + + + + +struct cFurnaceRecipe::sFurnaceRecipeState +{ + RecipeList Recipes; + FuelList Fuel; +}; + + + + + +cFurnaceRecipe::cFurnaceRecipe() + : m_pState( new sFurnaceRecipeState ) +{ + ReloadRecipes(); +} + + + + + +cFurnaceRecipe::~cFurnaceRecipe() +{ + ClearRecipes(); + delete m_pState; +} + + + + + +void cFurnaceRecipe::ReloadRecipes(void) +{ + ClearRecipes(); + LOGD("Loading furnace recipes..."); + + std::ifstream f; + char a_File[] = "furnace.txt"; + f.open(a_File, std::ios::in); + std::string input; + + if (!f.good()) + { + f.close(); + LOG("Could not open the furnace recipes file \"%s\"", a_File); + return; + } + + // TODO: Replace this messy parse with a high-level-structured one (ReadLine / ProcessLine) + bool bSyntaxError = false; + while (f.good()) + { + char c; + + ////////////////////////////////////////////////////////////////////////// + // comments + f >> c; + f.unget(); + if( c == '#' ) + { + while( f.good() && c != '\n' ) + { + f.get( c ); + } + continue; + } + + + ////////////////////////////////////////////////////////////////////////// + // Line breaks + f.get( c ); + while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } + if (f.eof()) + { + break; + } + f.unget(); + + ////////////////////////////////////////////////////////////////////////// + // Check for fuel + f >> c; + if( c == '!' ) // It's fuel :) + { + // Read item + int IItemID = 0, IItemCount = 0, IItemHealth = 0; + f >> IItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> IItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> IItemHealth; + } + + // Burn time + int BurnTime; + f >> c; if( c != '=' ) { bSyntaxError = true; break; } + f >> BurnTime; + + // Add to fuel list + Fuel F; + F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth ); + F.BurnTime = BurnTime; + m_pState->Fuel.push_back( F ); + continue; + } + f.unget(); + + ////////////////////////////////////////////////////////////////////////// + // Read items + int IItemID = 0, IItemCount = 0, IItemHealth = 0; + f >> IItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> IItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> IItemHealth; + } + + int CookTime; + f >> c; if( c != '@' ) { bSyntaxError = true; break; } + f >> CookTime; + + int OItemID = 0, OItemCount = 0, OItemHealth = 0; + f >> c; if( c != '=' ) { bSyntaxError = true; break; } + f >> OItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> OItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> OItemHealth; + } + + // Add to recipe list + Recipe R; + R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth ); + R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth ); + R.CookTime = CookTime; + m_pState->Recipes.push_back( R ); + } + if (bSyntaxError) + { + LOGERROR("ERROR: FurnaceRecipe, syntax error" ); + } + LOG("Loaded %u furnace recipes and %u fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); +} + + + + + +void cFurnaceRecipe::ClearRecipes(void) +{ + for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) + { + Recipe R = *itr; + delete R.In; + delete R.Out; + } + m_pState->Recipes.clear(); + + for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) + { + Fuel F = *itr; + delete F.In; + } + m_pState->Fuel.clear(); +} + + + + + +const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const +{ + const Recipe * BestRecipe = 0; + for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) + { + const Recipe & R = *itr; + if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount)) + { + if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount)) + { + continue; + } + else + { + BestRecipe = &R; + } + } + } + return BestRecipe; +} + + + + + +int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const +{ + int BestFuel = 0; + for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) + { + const Fuel & F = *itr; + if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount)) + { + if (BestFuel > 0 && (BestFuel > F.BurnTime)) + { + continue; + } + else + { + BestFuel = F.BurnTime; + } + } + } + return BestFuel; +} + + + + diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h new file mode 100644 index 000000000..2f91e9bcb --- /dev/null +++ b/src/FurnaceRecipe.h @@ -0,0 +1,50 @@ + +#pragma once + + + + + +class cItem; + + + + + +class cFurnaceRecipe +{ +public: + cFurnaceRecipe(void); + ~cFurnaceRecipe(); + + void ReloadRecipes(void); + + struct Fuel + { + cItem * In; + int BurnTime; ///< How long this fuel burns, in ticks + }; + + struct Recipe + { + cItem * In; + cItem * Out; + int CookTime; ///< How long this recipe takes to smelt, in ticks + }; + + /// Returns a recipe for the specified input, NULL if no recipe found + const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const; + + /// Returns the amount of time that the specified fuel burns, in ticks + int GetBurnTime(const cItem & a_Fuel) const; + +private: + void ClearRecipes(void); + + struct sFurnaceRecipeState; + sFurnaceRecipeState * m_pState; +}; + + + + diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp new file mode 100644 index 000000000..4dff50918 --- /dev/null +++ b/src/Generating/BioGen.cpp @@ -0,0 +1,707 @@ + +// BioGen.cpp + +// Implements the various biome generators + +#include "Globals.h" +#include "BioGen.h" +#include "lib/inifile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenConstant: + +void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + a_BiomeMap[i] = m_Biome; + } +} + + + + + +void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile) +{ + AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains"); + m_Biome = StringToBiome(Biome); + if (m_Biome == -1) + { + LOGWARN("[Generator]::ConstantBiome value \"%s\" not recognized, using \"Plains\".", Biome.c_str()); + m_Biome = biPlains; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenCache: + +cBioGenCache::cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize) : + m_BioGenToCache(a_BioGenToCache), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cBioGenCache::~cBioGenCache() +{ + delete[] m_CacheData; + delete[] m_CacheOrder; +} + + + + + +void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_BiomeMap, m_CacheData[Idx].m_BiomeMap, sizeof(a_BiomeMap)); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_BioGenToCache->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_BiomeMap, a_BiomeMap, sizeof(a_BiomeMap)); + m_CacheData[Idx].m_ChunkX = a_ChunkX; + m_CacheData[Idx].m_ChunkZ = a_ChunkZ; +} + + + + + +void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile) +{ + super::InitializeBiomeGen(a_IniFile); + m_BioGenToCache->InitializeBiomeGen(a_IniFile); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBiomeGenList: + +void cBiomeGenList::InitializeBiomes(const AString & a_Biomes) +{ + AStringVector Split = StringSplit(a_Biomes, ","); + + // Convert each string in the list into biome: + for (AStringVector::const_iterator itr = Split.begin(); itr != Split.end(); ++itr) + { + AStringVector Split2 = StringSplit(*itr, ":"); + if (Split2.size() < 1) + { + continue; + } + int Count = 1; + if (Split2.size() >= 2) + { + Count = atol(Split2[1].c_str()); + if (Count <= 0) + { + LOGWARNING("Cannot decode biome count: \"%s\"; using 1.", Split2[1].c_str()); + Count = 1; + } + } + EMCSBiome Biome = StringToBiome(Split2[0]); + if (Biome != -1) + { + for (int i = 0; i < Count; i++) + { + m_Biomes.push_back(Biome); + } + } + else + { + LOGWARNING("Cannot decode biome name: \"%s\"; skipping", Split2[0].c_str()); + } + } // for itr - Split[] + if (!m_Biomes.empty()) + { + m_BiomesCount = (int)m_Biomes.size(); + return; + } + + // There were no biomes, add default biomes: + static EMCSBiome Biomes[] = + { + biOcean, + biPlains, + biDesert, + biExtremeHills, + biForest, + biTaiga, + biSwampland, + biRiver, + biFrozenOcean, + biFrozenRiver, + biIcePlains, + biIceMountains, + biMushroomIsland, + biMushroomShore, + biBeach, + biDesertHills, + biForestHills, + biTaigaHills, + biExtremeHillsEdge, + biJungle, + biJungleHills, + } ; + m_Biomes.reserve(ARRAYCOUNT(Biomes)); + for (int i = 0; i < ARRAYCOUNT(Biomes); i++) + { + m_Biomes.push_back(Biomes[i]); + } + m_BiomesCount = (int)m_Biomes.size(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenCheckerboard: + +void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + int Base = cChunkDef::Width * a_ChunkZ + z; + for (int x = 0; x < cChunkDef::Width; x++) + { + int Add = cChunkDef::Width * a_ChunkX + x; + a_BiomeMap[x + cChunkDef::Width * z] = m_Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % m_BiomesCount]; + } + } +} + + + + + +void cBioGenCheckerboard::InitializeBiomeGen(cIniFile & a_IniFile) +{ + super::InitializeBiomeGen(a_IniFile); + AString Biomes = a_IniFile.GetValueSet ("Generator", "CheckerBoardBiomes", ""); + m_BiomeSize = a_IniFile.GetValueSetI("Generator", "CheckerboardBiomeSize", 64); + m_BiomeSize = (m_BiomeSize < 8) ? 8 : m_BiomeSize; + InitializeBiomes(Biomes); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenVoronoi : + +void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + int BaseZ = cChunkDef::Width * a_ChunkZ; + int BaseX = cChunkDef::Width * a_ChunkX; + for (int z = 0; z < cChunkDef::Width; z++) + { + int AbsoluteZ = BaseZ + z; + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(BaseX + x, AbsoluteZ)); + } // for x + } // for z +} + + + + + +void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) +{ + super::InitializeBiomeGen(a_IniFile); + m_CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64); + AString Biomes = a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", ""); + InitializeBiomes(Biomes); +} + + + + + +EMCSBiome cBioGenVoronoi::VoronoiBiome(int a_BlockX, int a_BlockZ) +{ + int CellX = a_BlockX / m_CellSize; + int CellZ = a_BlockZ / m_CellSize; + + // Note that Noise values need to be divided by 8 to gain a uniform modulo-2^n distribution + + // Get 5x5 neighboring cell seeds, compare distance to each. Return the biome in the minumim-distance cell + int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this + EMCSBiome res = biPlains; // Will be overriden + for (int x = CellX - 2; x <= CellX + 2; x++) + { + int BaseX = x * m_CellSize; + for (int z = CellZ - 2; z < CellZ + 2; z++) + { + int OffsetX = (m_Noise.IntNoise3DInt(x, 16 * x + 32 * z, z) / 8) % m_CellSize; + int OffsetZ = (m_Noise.IntNoise3DInt(x, 32 * x - 16 * z, z) / 8) % m_CellSize; + int SeedX = BaseX + OffsetX; + int SeedZ = z * m_CellSize + OffsetZ; + + int Dist = (SeedX - a_BlockX) * (SeedX - a_BlockX) + (SeedZ - a_BlockZ) * (SeedZ - a_BlockZ); + if (Dist < MinDist) + { + MinDist = Dist; + res = m_Biomes[(m_Noise.IntNoise3DInt(x, x - z + 1000, z) / 8) % m_BiomesCount]; + } + } // for z + } // for x + + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenDistortedVoronoi: + +void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + int BaseZ = cChunkDef::Width * a_ChunkZ; + int BaseX = cChunkDef::Width * a_ChunkX; + + // Distortions for linear interpolation: + int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1]; + int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1]; + for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++) + { + Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z]); + } + + LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + + for (int z = 0; z < cChunkDef::Width; z++) + { + int AbsoluteZ = BaseZ + z; + for (int x = 0; x < cChunkDef::Width; x++) + { + // Distort(BaseX + x, AbsoluteZ, DistX, DistZ); + cChunkDef::SetBiome(a_BiomeMap, x, z, VoronoiBiome(DistortX[x][z], DistortZ[x][z])); + } // for x + } // for z +} + + + + + +void cBioGenDistortedVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) +{ + // Do NOT call super::InitializeBiomeGen(), as it would try to read Voronoi params instead of DistortedVoronoi params + m_CellSize = a_IniFile.GetValueSetI("Generator", "DistortedVoronoiCellSize", 96); + AString Biomes = a_IniFile.GetValueSet ("Generator", "DistortedVoronoiBiomes", ""); + InitializeBiomes(Biomes); +} + + + + +void cBioGenDistortedVoronoi::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ) +{ + double NoiseX = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 1000); + NoiseX += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 2000); + NoiseX += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 3000); + double NoiseZ = m_Noise.CubicNoise3D((float)a_BlockX / m_CellSize, (float)a_BlockZ / m_CellSize, 4000); + NoiseZ += 0.5 * m_Noise.CubicNoise3D(2 * (float)a_BlockX / m_CellSize, 2 * (float)a_BlockZ / m_CellSize, 5000); + NoiseZ += 0.08 * m_Noise.CubicNoise3D(16 * (float)a_BlockX / m_CellSize, 16 * (float)a_BlockZ / m_CellSize, 6000); + + a_DistortedX = a_BlockX + (int)(m_CellSize * 0.5 * NoiseX); + a_DistortedZ = a_BlockZ + (int)(m_CellSize * 0.5 * NoiseZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBioGenMultiStepMap : + +cBioGenMultiStepMap::cBioGenMultiStepMap(int a_Seed) : + m_Noise1(a_Seed + 1000), + m_Noise2(a_Seed + 2000), + m_Noise3(a_Seed + 3000), + m_Noise4(a_Seed + 4000), + m_Noise5(a_Seed + 5000), + m_Noise6(a_Seed + 6000), + m_Seed(a_Seed), + m_OceanCellSize(384), + m_MushroomIslandSize(64), + m_RiverCellSize(384), + m_RiverWidthThreshold(0.125), + m_LandBiomesSize(1024) +{ +} + + + + + +void cBioGenMultiStepMap::InitializeBiomeGen(cIniFile & a_IniFile) +{ + m_OceanCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapOceanCellSize", m_OceanCellSize); + m_MushroomIslandSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapMushroomIslandSize", m_MushroomIslandSize); + m_RiverCellSize = a_IniFile.GetValueSetI("Generator", "MultiStepMapRiverCellSize", m_RiverCellSize); + m_RiverWidthThreshold = a_IniFile.GetValueSetF("Generator", "MultiStepMapRiverWidth", m_RiverWidthThreshold); + m_LandBiomesSize = (float)a_IniFile.GetValueSetI("Generator", "MultiStepMapLandBiomeSize", (int)m_LandBiomesSize); +} + + + + + +void cBioGenMultiStepMap::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + DecideOceanLandMushroom(a_ChunkX, a_ChunkZ, a_BiomeMap); + AddRivers(a_ChunkX, a_ChunkZ, a_BiomeMap); + ApplyTemperatureHumidity(a_ChunkX, a_ChunkZ, a_BiomeMap); +} + + + + + +void cBioGenMultiStepMap::DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + // Distorted Voronoi over 3 biomes, with mushroom having only a special occurence. + + // Prepare a distortion lookup table, by distorting a 5x5 area and using that as 1:4 zoom (linear interpolate): + int BaseZ = cChunkDef::Width * a_ChunkZ; + int BaseX = cChunkDef::Width * a_ChunkX; + int DistortX[cChunkDef::Width + 1][cChunkDef::Width + 1]; + int DistortZ[cChunkDef::Width + 1][cChunkDef::Width + 1]; + int DistortSize = m_OceanCellSize / 2; + for (int x = 0; x <= 4; x++) for (int z = 0; z <= 4; z++) + { + Distort(BaseX + x * 4, BaseZ + z * 4, DistortX[4 * x][4 * z], DistortZ[4 * x][4 * z], DistortSize); + } + LinearUpscale2DArrayInPlace(&DistortX[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + LinearUpscale2DArrayInPlace(&DistortZ[0][0], cChunkDef::Width + 1, cChunkDef::Width + 1, 4, 4); + + // Prepare a 9x9 area of neighboring cell seeds + // (assuming that 7x7 cell area is larger than a chunk being generated) + const int NEIGHBORHOOD_SIZE = 4; // How many seeds in each direction to check + int CellX = BaseX / m_OceanCellSize; + int CellZ = BaseZ / m_OceanCellSize; + int SeedX[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; + int SeedZ[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; + EMCSBiome SeedV[2 * NEIGHBORHOOD_SIZE + 1][2 * NEIGHBORHOOD_SIZE + 1]; + for (int xc = 0; xc < 2 * NEIGHBORHOOD_SIZE + 1; xc++) + { + int RealCellX = xc + CellX - NEIGHBORHOOD_SIZE; + int CellBlockX = RealCellX * m_OceanCellSize; + for (int zc = 0; zc < 2 * NEIGHBORHOOD_SIZE + 1; zc++) + { + int RealCellZ = zc + CellZ - NEIGHBORHOOD_SIZE; + int CellBlockZ = RealCellZ * m_OceanCellSize; + int OffsetX = (m_Noise2.IntNoise3DInt(RealCellX, 16 * RealCellX + 32 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize; + int OffsetZ = (m_Noise4.IntNoise3DInt(RealCellX, 32 * RealCellX - 16 * RealCellZ, RealCellZ) / 8) % m_OceanCellSize; + SeedX[xc][zc] = CellBlockX + OffsetX; + SeedZ[xc][zc] = CellBlockZ + OffsetZ; + SeedV[xc][zc] = (((m_Noise6.IntNoise3DInt(RealCellX, RealCellX - RealCellZ + 1000, RealCellZ) / 11) % 256) > 90) ? biOcean : ((EMCSBiome)(-1)); + } // for z + } // for x + + for (int xc = 1; xc < 2 * NEIGHBORHOOD_SIZE; xc++) for (int zc = 1; zc < 2 * NEIGHBORHOOD_SIZE; zc++) + { + if ( + (SeedV[xc ][zc] == biOcean) && + (SeedV[xc - 1][zc] == biOcean) && + (SeedV[xc + 1][zc] == biOcean) && + (SeedV[xc ][zc - 1] == biOcean) && + (SeedV[xc ][zc + 1] == biOcean) && + (SeedV[xc - 1][zc - 1] == biOcean) && + (SeedV[xc + 1][zc - 1] == biOcean) && + (SeedV[xc - 1][zc + 1] == biOcean) && + (SeedV[xc + 1][zc + 1] == biOcean) + ) + { + SeedV[xc][zc] = biMushroomIsland; + } + } + + // For each column find the nearest distorted cell and use its value as the biome: + int MushroomOceanThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 1024; + int MushroomShoreThreshold = m_OceanCellSize * m_OceanCellSize * m_MushroomIslandSize / 2048; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int AbsoluteZ = DistortZ[x][z]; + int AbsoluteX = DistortX[x][z]; + int MinDist = m_OceanCellSize * m_OceanCellSize * 16; // There has to be a cell closer than this + EMCSBiome Biome = biPlains; + // Find the nearest cell seed: + for (int xs = 1; xs < 2 * NEIGHBORHOOD_SIZE; xs++) for (int zs = 1; zs < 2 * NEIGHBORHOOD_SIZE; zs++) + { + int Dist = (SeedX[xs][zs] - AbsoluteX) * (SeedX[xs][zs] - AbsoluteX) + (SeedZ[xs][zs] - AbsoluteZ) * (SeedZ[xs][zs] - AbsoluteZ); + if (Dist >= MinDist) + { + continue; + } + MinDist = Dist; + Biome = SeedV[xs][zs]; + // Shrink mushroom biome and add a shore: + if (Biome == biMushroomIsland) + { + if (Dist > MushroomOceanThreshold) + { + Biome = biOcean; + } + else if (Dist > MushroomShoreThreshold) + { + Biome = biMushroomShore; + } + } + } // for zs, xs + + cChunkDef::SetBiome(a_BiomeMap, x, z, Biome); + } // for x + } // for z +} + + + + + +void cBioGenMultiStepMap::AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_RiverCellSize; + for (int x = 0; x < cChunkDef::Width; x++) + { + if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1) + { + // Biome already set, skip this column + continue; + } + + float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_RiverCellSize; + + double Noise = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ); + Noise += 0.5 * m_Noise3.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); + Noise += 0.1 * m_Noise5.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); + + if ((Noise > 0) && (Noise < m_RiverWidthThreshold)) + { + cChunkDef::SetBiome(a_BiomeMap, x, z, biRiver); + } + } // for x + } // for z +} + + + + + +void cBioGenMultiStepMap::ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + IntMap TemperatureMap; + IntMap HumidityMap; + BuildTemperatureHumidityMaps(a_ChunkX, a_ChunkZ, TemperatureMap, HumidityMap); + + FreezeWaterBiomes(a_BiomeMap, TemperatureMap); + DecideLandBiomes(a_BiomeMap, TemperatureMap, HumidityMap); +} + + + + + +void cBioGenMultiStepMap::Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize) +{ + double NoiseX = m_Noise3.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize); + NoiseX += 0.5 * m_Noise2.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize); + NoiseX += 0.1 * m_Noise1.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize); + double NoiseZ = m_Noise6.CubicNoise2D( (float)a_BlockX / a_CellSize, (float)a_BlockZ / a_CellSize); + NoiseZ += 0.5 * m_Noise5.CubicNoise2D(2 * (float)a_BlockX / a_CellSize, 2 * (float)a_BlockZ / a_CellSize); + NoiseZ += 0.1 * m_Noise4.CubicNoise2D(16 * (float)a_BlockX / a_CellSize, 16 * (float)a_BlockZ / a_CellSize); + + a_DistortedX = a_BlockX + (int)(a_CellSize * 0.5 * NoiseX); + a_DistortedZ = a_BlockZ + (int)(a_CellSize * 0.5 * NoiseZ); +} + + + + + +void cBioGenMultiStepMap::BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap) +{ + // Linear interpolation over 8x8 blocks; use double for better precision: + DblMap TemperatureMap; + DblMap HumidityMap; + for (int z = 0; z < 17; z += 8) + { + float NoiseCoordZ = (float)(a_ChunkZ * cChunkDef::Width + z) / m_LandBiomesSize; + for (int x = 0; x < 17; x += 8) + { + float NoiseCoordX = (float)(a_ChunkX * cChunkDef::Width + x) / m_LandBiomesSize; + + double NoiseT = m_Noise1.CubicNoise2D( NoiseCoordX, NoiseCoordZ); + NoiseT += 0.5 * m_Noise2.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); + NoiseT += 0.1 * m_Noise3.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); + TemperatureMap[x + 17 * z] = NoiseT; + + double NoiseH = m_Noise4.CubicNoise2D( NoiseCoordX, NoiseCoordZ); + NoiseH += 0.5 * m_Noise5.CubicNoise2D(2 * NoiseCoordX, 2 * NoiseCoordZ); + NoiseH += 0.1 * m_Noise6.CubicNoise2D(8 * NoiseCoordX, 8 * NoiseCoordZ); + HumidityMap[x + 17 * z] = NoiseH; + } // for x + } // for z + LinearUpscale2DArrayInPlace(TemperatureMap, 17, 17, 8, 8); + LinearUpscale2DArrayInPlace(HumidityMap, 17, 17, 8, 8); + + // Re-map into integral values in [0 .. 255] range: + for (int idx = 0; idx < ARRAYCOUNT(a_TemperatureMap); idx++) + { + a_TemperatureMap[idx] = std::max(0, std::min(255, (int)(128 + TemperatureMap[idx] * 128))); + a_HumidityMap[idx] = std::max(0, std::min(255, (int)(128 + HumidityMap[idx] * 128))); + } +} + + + + + +void cBioGenMultiStepMap::DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap) +{ + static const EMCSBiome BiomeMap[] = + { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + /* 0 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, + /* 1 */ biTundra, biTundra, biTundra, biTundra, biPlains, biPlains, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, + /* 2 */ biTundra, biTundra, biTundra, biTundra, biPlains, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert, + /* 3 */ biTundra, biTundra, biTundra, biTundra, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biDesert, biDesert, biDesertHills, biDesertHills, biDesert, biDesert, + /* 4 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert, + /* 5 */ biTundra, biTundra, biIceMountains, biIceMountains, biExtremeHills, biExtremeHills, biPlains, biPlains, biPlains, biPlains, biForestHills, biForestHills, biExtremeHills, biExtremeHills, biDesertHills, biDesert, + /* 6 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 7 */ biTundra, biTundra, biIceMountains, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 8 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 9 */ biTundra, biTundra, biTaiga, biTaiga, biForest, biForest, biForest, biForest, biForest, biForest, biForest, biForestHills, biExtremeHills, biExtremeHills, biPlains, biPlains, + /* 10 */ biTaiga, biTaiga, biTaiga, biIceMountains, biForestHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 11 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biForestHills, biForest, biForest, biForest, biForest, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 12 */ biTaiga, biTaiga, biIceMountains, biIceMountains, biExtremeHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 13 */ biTaiga, biTaiga, biTaiga, biIceMountains, biJungleHills, biJungleHills, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 14 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + /* 15 */ biTaiga, biTaiga, biTaiga, biTaiga, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biJungle, biSwampland, biSwampland, biSwampland, biSwampland, + } ; + for (int z = 0; z < cChunkDef::Width; z++) + { + int idxZ = 17 * z; + for (int x = 0; x < cChunkDef::Width; x++) + { + if (cChunkDef::GetBiome(a_BiomeMap, x, z) != -1) + { + // Already set before + continue; + } + int idx = idxZ + x; + int Temperature = a_TemperatureMap[idx] / 16; // -> [0..15] range + int Humidity = a_HumidityMap[idx] / 16; // -> [0..15] range + cChunkDef::SetBiome(a_BiomeMap, x, z, BiomeMap[Temperature + 16 * Humidity]); + } // for x + } // for z +} + + + + + +void cBioGenMultiStepMap::FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap) +{ + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++, idx++) + { + if (a_TemperatureMap[idx] > 4 * 16) + { + // Not frozen + continue; + } + switch (cChunkDef::GetBiome(a_BiomeMap, x, z)) + { + case biRiver: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenRiver); break; + case biOcean: cChunkDef::SetBiome(a_BiomeMap, x, z, biFrozenOcean); break; + } + } // for x + idx += 1; + } // for z +} + + + + diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h new file mode 100644 index 000000000..bc70bfab2 --- /dev/null +++ b/src/Generating/BioGen.h @@ -0,0 +1,230 @@ + +// BioGen.h + +/* +Interfaces to the various biome generators: + - cBioGenConstant + - cBioGenCheckerboard + - cBioGenDistortedVoronoi +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cBioGenConstant : + public cBiomeGen +{ +public: + cBioGenConstant(void) : m_Biome(biPlains) {} + +protected: + + EMCSBiome m_Biome; + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// A simple cache that stores N most recently generated chunks' biomes; N being settable upon creation +class cBioGenCache : + public cBiomeGen +{ + typedef cBiomeGen super; + +public: + cBioGenCache(cBiomeGen * a_BioGenToCache, int a_CacheSize); // Doesn't take ownership of a_BioGenToCache + ~cBioGenCache(); + +protected: + + cBiomeGen * m_BioGenToCache; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::BiomeMap m_BiomeMap; + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) + + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// Base class for generators that use a list of available biomes. This class takes care of the list. +class cBiomeGenList : + public cBiomeGen +{ + typedef cBiomeGen super; + +protected: + // List of biomes that the generator is allowed to generate: + typedef std::vector<EMCSBiome> EMCSBiomes; + EMCSBiomes m_Biomes; + int m_BiomesCount; // Pulled out of m_Biomes for faster access + + /// Parses the INI file setting string into m_Biomes. + void InitializeBiomes(const AString & a_Biomes); +} ; + + + + + +class cBioGenCheckerboard : + public cBiomeGenList +{ + typedef cBiomeGenList super; + +protected: + int m_BiomeSize; + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cBioGenVoronoi : + public cBiomeGenList +{ + typedef cBiomeGenList super; + +public: + cBioGenVoronoi(int a_Seed) : + m_Noise(a_Seed) + { + } + +protected: + int m_CellSize; + + cNoise m_Noise; + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; + + EMCSBiome VoronoiBiome(int a_BlockX, int a_BlockZ); +} ; + + + + + +class cBioGenDistortedVoronoi : + public cBioGenVoronoi +{ + typedef cBioGenVoronoi super; +public: + cBioGenDistortedVoronoi(int a_Seed) : + cBioGenVoronoi(a_Seed) + { + } + +protected: + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; + + /// Distorts the coords using a Perlin-like noise + void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ); +} ; + + + + + +class cBioGenMultiStepMap : + public cBiomeGen +{ + typedef cBiomeGen super; + +public: + cBioGenMultiStepMap(int a_Seed); + +protected: + // Noises used for composing the perlin-noise: + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + cNoise m_Noise4; + cNoise m_Noise5; + cNoise m_Noise6; + + int m_Seed; + int m_OceanCellSize; + int m_MushroomIslandSize; + int m_RiverCellSize; + double m_RiverWidthThreshold; + float m_LandBiomesSize; + + typedef int IntMap[17 * 17]; // x + 17 * z, expected trimmed into [0..255] range + typedef double DblMap[17 * 17]; // x + 17 * z, expected trimmed into [0..1] range + + // cBiomeGen overrides: + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void InitializeBiomeGen(cIniFile & a_IniFile) override; + + /** Step 1: Decides between ocean, land and mushroom, using a DistVoronoi with special conditions and post-processing for mushroom islands + Sets biomes to biOcean, -1 (i.e. land), biMushroomIsland or biMushroomShore + */ + void DecideOceanLandMushroom(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + /** Step 2: Add rivers to the land + Flips some "-1" biomes into biRiver + */ + void AddRivers(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + /** Step 3: Decide land biomes using a temperature / humidity map; freeze ocean / river in low temperatures. + Flips all remaining "-1" biomes into land biomes. Also flips some biOcean and biRiver into biFrozenOcean, biFrozenRiver, based on temp map. + */ + void ApplyTemperatureHumidity(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + /// Distorts the coords using a Perlin-like noise, with a specified cell-size + void Distort(int a_BlockX, int a_BlockZ, int & a_DistortedX, int & a_DistortedZ, int a_CellSize); + + /// Builds two Perlin-noise maps, one for temperature, the other for humidity. Trims both into [0..255] range + void BuildTemperatureHumidityMaps(int a_ChunkX, int a_ChunkZ, IntMap & a_TemperatureMap, IntMap & a_HumidityMap); + + /// Flips all remaining "-1" biomes into land biomes using the two maps + void DecideLandBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap, const IntMap & a_HumidityMap); + + /// Flips biOcean and biRiver into biFrozenOcean and biFrozenRiver if the temperature is too low + void FreezeWaterBiomes(cChunkDef::BiomeMap & a_BiomeMap, const IntMap & a_TemperatureMap); +} ; + + + + diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp new file mode 100644 index 000000000..df45bb4c2 --- /dev/null +++ b/src/Generating/Caves.cpp @@ -0,0 +1,968 @@ + +// Caves.cpp + +// Implements the various cave structure generators: +// - cStructGenWormNestCaves +// - cStructGenDualRidgeCaves +// - cStructGenMarbleCaves +// - cStructGenNetherCaves + +/* +WormNestCave generator: +Caves are generated in "nests" - groups of tunnels generated from a single point. +For each chunk, all the nests that could intersect it are generated. +For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...) +Then each tunnel is randomized by inserting points in between its ends. +Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other. +When the tunnels are ready, they are simply carved into the chunk, one by one. +To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it. + +MarbleCaves generator: +For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept. +Problem with this is the amount of CPU work needed for each chunk. +Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground. + +DualRidgeCaves generator: +Instead of evaluating a single noise function, two different noise functions are multiplied. This produces +regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be +reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best. +*/ + +#include "Globals.h" +#include "Caves.h" + + + + + +/// How many nests in each direction are generated for a given chunk. Must be an even number +#define NEIGHBORHOOD_SIZE 8 + + + + + +const int MIN_RADIUS = 3; +const int MAX_RADIUS = 8; + + + + + +struct cCaveDefPoint +{ + int m_BlockX; + int m_BlockY; + int m_BlockZ; + int m_Radius; + + cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) : + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ), + m_Radius(a_Radius) + { + } +} ; + +typedef std::vector<cCaveDefPoint> cCaveDefPoints; + + + + + +/// A single non-branching tunnel of a WormNestCave +class cCaveTunnel +{ + // The bounding box, including the radii around defpoints: + int m_MinBlockX, m_MaxBlockX; + int m_MinBlockY, m_MaxBlockY; + int m_MinBlockZ, m_MaxBlockZ; + + /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise + void Randomize(cNoise & a_Noise); + + /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short) + bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst); + + /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true + void Smooth(void); + + /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block + void FinishLinear(void); + + /// Calculates the bounding box of the points present + void CalcBoundingBox(void); + +public: + cCaveDefPoints m_Points; + + cCaveTunnel( + int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, + int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, + cNoise & a_Noise + ); + + /// Carves the tunnel into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; + #endif // _DEBUG +} ; + +typedef std::vector<cCaveTunnel *> cCaveTunnels; + + + + + +/// A collection of connected tunnels, possibly branching. +class cStructGenWormNestCaves::cCaveSystem +{ +public: + // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk() + int m_BlockX; + int m_BlockZ; + + cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise); + ~cCaveSystem(); + + /// Carves the cave system into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; + #endif // _DEBUG + +protected: + int m_Size; + cCaveTunnels m_Tunnels; + + void Clear(void); + + /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments] + void GenerateTunnelsFromPoint( + int a_OriginX, int a_OriginY, int a_OriginZ, + cNoise & a_Noise, int a_Segments + ); + + /// Returns a radius based on the location provided. + int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ); +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCaveTunnel: + +cCaveTunnel::cCaveTunnel( + int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, + int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, + cNoise & a_Noise +) +{ + m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius)); + m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius)); + + if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0)) + { + // Don't bother detailing this cave, it's under the world anyway + return; + } + + Randomize(a_Noise); + Smooth(); + + // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data: + CalcBoundingBox(); + + FinishLinear(); +} + + + + + +void cCaveTunnel::Randomize(cNoise & a_Noise) +{ + // Repeat 4 times: + for (int i = 0; i < 4; i++) + { + // For each already present point, insert a point in between it and its predecessor, shifted randomly. + int PrevX = m_Points.front().m_BlockX; + int PrevY = m_Points.front().m_BlockY; + int PrevZ = m_Points.front().m_BlockZ; + int PrevR = m_Points.front().m_Radius; + cCaveDefPoints Pts; + Pts.reserve(m_Points.size() * 2 + 1); + Pts.push_back(m_Points.front()); + for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) + { + int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11; + int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX); + len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY); + len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ); + len = 3 * (int)sqrt((double)len) / 4; + int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1)); + Random /= 4; + int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2); + Random /= 256; + int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4); + Random /= 256; + int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2); + Pts.push_back(cCaveDefPoint(x, y, z, Rad)); + Pts.push_back(*itr); + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + } + std::swap(Pts, m_Points); + } +} + + + + + +bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst) +{ + // Smoothing: for each line segment, add points on its 1/4 lengths + bool res = false; + int Num = a_Src.size() - 2; // this many intermediary points + a_Dst.clear(); + a_Dst.reserve(Num * 2 + 2); + cCaveDefPoints::const_iterator itr = a_Src.begin() + 1; + a_Dst.push_back(a_Src.front()); + int PrevX = a_Src.front().m_BlockX; + int PrevY = a_Src.front().m_BlockY; + int PrevZ = a_Src.front().m_BlockZ; + int PrevR = a_Src.front().m_Radius; + for (int i = 0; i <= Num; ++i, ++itr) + { + int dx = itr->m_BlockX - PrevX; + int dy = itr->m_BlockY - PrevY; + int dz = itr->m_BlockZ - PrevZ; + if (abs(dx) + abs(dz) + abs(dy) < 6) + { + // Too short a segment to smooth-subdivide into quarters + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + continue; + } + int dr = itr->m_Radius - PrevR; + int Rad1 = std::max(PrevR + 1 * dr / 4, 1); + int Rad2 = std::max(PrevR + 3 * dr / 4, 1); + a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1)); + a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2)); + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + res = true; + } + a_Dst.push_back(a_Src.back()); + return res && (a_Src.size() < a_Dst.size()); +} + + + + + +void cCaveTunnel::Smooth(void) +{ + cCaveDefPoints Pts; + while (true) + { + if (!RefineDefPoints(m_Points, Pts)) + { + std::swap(Pts, m_Points); + return; + } + if (!RefineDefPoints(Pts, m_Points)) + { + return; + } + } +} + + + + + +void cCaveTunnel::FinishLinear(void) +{ + // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints + cCaveDefPoints Pts; + std::swap(Pts, m_Points); + + m_Points.reserve(Pts.size() * 3); + int PrevX = Pts.front().m_BlockX; + int PrevY = Pts.front().m_BlockY; + int PrevZ = Pts.front().m_BlockZ; + for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) + { + int x1 = itr->m_BlockX; + int y1 = itr->m_BlockY; + int z1 = itr->m_BlockZ; + int dx = abs(x1 - PrevX); + int dy = abs(y1 - PrevY); + int dz = abs(z1 - PrevZ); + int sx = (PrevX < x1) ? 1 : -1; + int sy = (PrevY < y1) ? 1 : -1; + int sz = (PrevZ < z1) ? 1 : -1; + int R = itr->m_Radius; + + if (dx >= std::max(dy, dz)) // x dominant + { + int yd = dy - dx / 2; + int zd = dz - dx / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevX == x1) + { + break; + } + + if (yd >= 0) // move along y + { + PrevY += sy; + yd -= dx; + } + + if (zd >= 0) // move along z + { + PrevZ += sz; + zd -= dx; + } + + // move along x + PrevX += sx; + yd += dy; + zd += dz; + } + } + else if (dy >= std::max(dx, dz)) // y dominant + { + int xd = dx - dy / 2; + int zd = dz - dy / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevY == y1) + { + break; + } + + if (xd >= 0) // move along x + { + PrevX += sx; + xd -= dy; + } + + if (zd >= 0) // move along z + { + PrevZ += sz; + zd -= dy; + } + + // move along y + PrevY += sy; + xd += dx; + zd += dz; + } + } + else + { + // z dominant + ASSERT(dz >= std::max(dx, dy)); + int xd = dx - dz / 2; + int yd = dy - dz / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevZ == z1) + { + break; + } + + if (xd >= 0) // move along x + { + PrevX += sx; + xd -= dz; + } + + if (yd >= 0) // move along y + { + PrevY += sy; + yd -= dz; + } + + // move along z + PrevZ += sz; + xd += dx; + yd += dy; + } + } // if (which dimension is dominant) + } // for itr +} + + + + + +void cCaveTunnel::CalcBoundingBox(void) +{ + m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX; + m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY; + m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ; + for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) + { + m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius); + m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius); + m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius); + m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius); + m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius); + m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius); + } // for itr - m_Points[] +} + + + + + +void cCaveTunnel::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + int BaseX = a_ChunkX * cChunkDef::Width; + int BaseZ = a_ChunkZ * cChunkDef::Width; + if ( + (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || + (BaseZ > m_MaxBlockZ) || (BaseZ + cChunkDef::Width < m_MinBlockZ) + ) + { + // Tunnel does not intersect the chunk at all, bail out + return; + } + + int BlockStartX = a_ChunkX * cChunkDef::Width; + int BlockStartZ = a_ChunkZ * cChunkDef::Width; + int BlockEndX = BlockStartX + cChunkDef::Width; + int BlockEndZ = BlockStartZ + cChunkDef::Width; + for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + if ( + (itr->m_BlockX + itr->m_Radius < BlockStartX) || + (itr->m_BlockX - itr->m_Radius > BlockEndX) || + (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || + (itr->m_BlockZ - itr->m_Radius > BlockEndZ) + ) + { + // Cannot intersect, bail out early + continue; + } + + // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: + int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc + int DifY = itr->m_BlockY; + int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc + int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); + int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height)); + int SqRad = itr->m_Radius * itr->m_Radius; + for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = Bottom; y <= Top; y++) + { + int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); + if (4 * SqDist <= SqRad) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + // Only carve out these specific block types + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_GRAVEL: + case E_BLOCK_SAND: + case E_BLOCK_SANDSTONE: + case E_BLOCK_NETHERRACK: + case E_BLOCK_COAL_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + break; + } + default: break; + } + } + } // for y + } // for x, z + } // for itr - m_Points[] + + /* + #ifdef _DEBUG + // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: + for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc + int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc + if ( + (DifX >= 0) && (DifX < cChunkDef::Width) && + (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && + (DifZ >= 0) && (DifZ < cChunkDef::Width) + ) + { + cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); + } + } // for itr - m_Points[] + #endif // _DEBUG + //*/ +} + + + + + +#ifdef _DEBUG +AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + SVG.reserve(m_Points.size() * 20 + 200); + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color); + char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L" + for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr) + { + AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ); + Prefix = 'L'; + } + SVG.append("\"/>\n"); + return SVG; +} +#endif // _DEBUG + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenWormNestCaves::cCaveSystem: + +cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_Size(a_Size) +{ + int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3; + for (int i = 0; i < Num; i++) + { + int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset; + int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset; + int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20; + + // Generate three branches from the origin point: + // The tunnels generated depend on X, Y, Z and Branches, + // for the same set of numbers it generates the same offsets! + // That's why we add a +1 to X in the third line + GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3); + GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2); + GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3); + } +} + + + + + +cStructGenWormNestCaves::cCaveSystem::~cCaveSystem() +{ + Clear(); +} + + + + + + +void cStructGenWormNestCaves::cCaveSystem::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap); + } // for itr - m_Tunnels[] +} + + + + + +#ifdef _DEBUG +AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + SVG.reserve(512 * 1024); + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ)); + } // for itr - m_Tunnels[] + + // Base point highlight: + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n", + a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ + ); + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 + ); + + // A gray line from the base point to the first point of the ravine, for identification: + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, + a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX, + a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ + ); + + // Offset guides: + if (a_OffsetX > 0) + { + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n", + a_OffsetX, a_OffsetX + ); + } + if (a_OffsetZ > 0) + { + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n", + a_OffsetZ, a_OffsetZ + ); + } + + return SVG; +} +#endif // _DEBUG + + + + + +void cStructGenWormNestCaves::cCaveSystem::Clear(void) +{ + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + delete *itr; + } + m_Tunnels.clear(); +} + + + + + +void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint( + int a_OriginX, int a_OriginY, int a_OriginZ, + cNoise & a_Noise, int a_NumSegments +) +{ + int DoubleSize = m_Size * 2; + int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX); + for (int i = a_NumSegments - 1; i >= 0; --i) + { + int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2; + int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4; + int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2; + int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX); + m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise)); + GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i); + a_OriginX = EndX; + a_OriginY = EndY; + a_OriginZ = EndZ; + Radius = EndR; + } // for i - a_NumSegments +} + + + + + +int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ) +{ + // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large + int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11; + /* + // Not good enough: + // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value. + // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise + int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff); + // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384. + // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center + int res = 3 + abs((sum / 24) - 16); + */ + + // Algorithm of choice: random value in the range of zero to random value - heavily towards zero + int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1); + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenWormNestCaves: + +cStructGenWormNestCaves::~cStructGenWormNestCaves() +{ + ClearCache(); +} + + + + + +void cStructGenWormNestCaves::ClearCache(void) +{ + for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cCaveSystems Caves; + GetCavesForChunk(ChunkX, ChunkZ, Caves); + for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr) + { + (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + } // for itr - Caves[] +} + + + + + +void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_Grid; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= NEIGHBORHOOD_SIZE / 2; + BaseZ -= NEIGHBORHOOD_SIZE / 2; + + // Walk the cache, move each cave system that we want into a_Caves: + int StartX = BaseX * m_Grid; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid; + int StartZ = BaseZ * m_Grid; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid; + for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_Caves.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_Grid; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_Grid; + bool Found = false; + for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } + if (!Found) + { + a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise)); + } + } + } + + // Copy a_Caves into m_Cache to the beginning: + cCaveSystems CavesCopy(a_Caves); + m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cCaveSystems::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } + + /* + // Uncomment this block for debugging the caves' shapes in 2D using an SVG export + #ifdef _DEBUG + AString SVG; + SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n"); + SVG.reserve(2 * 1024 * 1024); + for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) + { + int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid); + Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid); + SVG.append((*itr)->ExportAsSVG(Color, 512, 512)); + } + SVG.append("</svg>\n"); + + AString fnam; + Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); + cFile File(fnam, cFile::fmWrite); + File.Write(SVG.c_str(), SVG.size()); + #endif // _DEBUG + //*/ +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMarbleCaves: + +static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) +{ + static const float PI_2 = 1.57079633f; + float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4; + + oct1 = oct1 * oct1 * oct1; + if (oct1 < 0.f) oct1 = PI_2; + if (oct1 > PI_2) oct1 = PI_2; + + return oct1; +} + + + + + +void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + cNoise Noise(m_Seed); + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z); + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x); + + int Top = a_ChunkDesc.GetHeight(x, z); + for (int y = 1; y < Top; ++y ) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE) + { + continue; + } + + const float yy = (float)y; + const float WaveNoise = 1; + if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + } // for y + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDualRidgeCaves: + +void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10; + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10; + + int Top = a_ChunkDesc.GetHeight(x, z); + for (int y = 1; y <= Top; ++y) + { + const float yy = (float)y / 10; + float n1 = m_Noise1.CubicNoise3D(xx, yy, zz); + float n2 = m_Noise2.CubicNoise3D(xx, yy, zz); + float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; + float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; + if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/src/Generating/Caves.h b/src/Generating/Caves.h new file mode 100644 index 000000000..70cf6fe8c --- /dev/null +++ b/src/Generating/Caves.h @@ -0,0 +1,102 @@ + +// Caves.h + +// Interfaces to the various cave structure generators: +// - cStructGenWormNestCaves +// - cStructGenMarbleCaves +// - cStructGenNetherCaves + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenMarbleCaves : + public cStructureGen +{ +public: + cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {} + +protected: + + int m_Seed; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenDualRidgeCaves : + public cStructureGen +{ +public: + cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) : + m_Noise1(a_Seed), + m_Noise2(2 * a_Seed + 19999), + m_Seed(a_Seed), + m_Threshold(a_Threshold) + { + } + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + int m_Seed; + float m_Threshold; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenWormNestCaves : + public cStructureGen +{ +public: + cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) : + m_Noise(a_Seed), + m_Size(a_Size), + m_Grid(a_Grid), + m_MaxOffset(a_MaxOffset) + { + } + + ~cStructGenWormNestCaves(); + +protected: + class cCaveSystem; // fwd: Caves.cpp + typedef std::list<cCaveSystem *> cCaveSystems; + + cNoise m_Noise; + int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel + int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to + int m_Grid; // average spacing of the nests + cCaveSystems m_Cache; + + /// Clears everything from the cache + void ClearCache(void); + + /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function. + void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves); + + // cStructGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp new file mode 100644 index 000000000..6050430fd --- /dev/null +++ b/src/Generating/ChunkDesc.cpp @@ -0,0 +1,605 @@ + +// ChunkDesc.cpp + +// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. + +#include "Globals.h" +#include "ChunkDesc.h" +#include "../BlockArea.h" +#include "../Cuboid.h" +#include "../Noise.h" +#include "../BlockEntities/BlockEntity.h" + + + + + +cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) : + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ), + m_bUseDefaultBiomes(true), + m_bUseDefaultHeight(true), + m_bUseDefaultComposition(true), + m_bUseDefaultStructures(true), + m_bUseDefaultFinish(true) +{ + m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width); + /* + memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes)); + memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles)); + */ + memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap)); + memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap)); +} + + + + + +cChunkDesc::~cChunkDesc() +{ + // Nothing needed yet +} + + + + + +void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkX = a_ChunkX; + m_ChunkZ = a_ChunkZ; +} + + + + + +void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) +{ + cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType); +} + + + + + +BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ) +{ + return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ); +} + + + + + +NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ) +{ + return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ); +} + + + + + +void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID) +{ + cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID); +} + + + + +EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ) +{ + return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); +} + + + + + +void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height) +{ + cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height); +} + + + + + +int cChunkDesc::GetHeight(int a_RelX, int a_RelZ) +{ + return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ); +} + + + + + +void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes) +{ + m_bUseDefaultBiomes = a_bUseDefaultBiomes; +} + + + + + +bool cChunkDesc::IsUsingDefaultBiomes(void) const +{ + return m_bUseDefaultBiomes; +} + + + + + +void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight) +{ + m_bUseDefaultHeight = a_bUseDefaultHeight; +} + + + + + +bool cChunkDesc::IsUsingDefaultHeight(void) const +{ + return m_bUseDefaultHeight; +} + + + + + +void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition) +{ + m_bUseDefaultComposition = a_bUseDefaultComposition; +} + + + + + +bool cChunkDesc::IsUsingDefaultComposition(void) const +{ + return m_bUseDefaultComposition; +} + + + + + +void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures) +{ + m_bUseDefaultStructures = a_bUseDefaultStructures; +} + + + + + +bool cChunkDesc::IsUsingDefaultStructures(void) const +{ + return m_bUseDefaultStructures; +} + + + + + +void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish) +{ + m_bUseDefaultFinish = a_bUseDefaultFinish; +} + + + + + +bool cChunkDesc::IsUsingDefaultFinish(void) const +{ + return m_bUseDefaultFinish; +} + + + + +void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy) +{ + m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy); +} + + + + + +void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ) +{ + // Normalize the coords: + if (a_MinRelX > a_MaxRelX) + { + std::swap(a_MinRelX, a_MaxRelX); + } + if (a_MinRelY > a_MaxRelY) + { + std::swap(a_MinRelY, a_MaxRelY); + } + if (a_MinRelZ > a_MaxRelZ) + { + std::swap(a_MinRelZ, a_MaxRelZ); + } + + // Include the Max coords: + a_MaxRelX += 1; + a_MaxRelY += 1; + a_MaxRelZ += 1; + + // Check coords validity: + if (a_MinRelX < 0) + { + LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__); + a_MinRelX = 0; + } + else if (a_MinRelX >= cChunkDef::Width) + { + LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MinRelX = cChunkDef::Width - 1; + } + if (a_MaxRelX < 0) + { + LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelX = 0; + } + else if (a_MinRelX >= cChunkDef::Width) + { + LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MaxRelX = cChunkDef::Width - 1; + } + + if (a_MinRelY < 0) + { + LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__); + a_MinRelY = 0; + } + else if (a_MinRelY >= cChunkDef::Height) + { + LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinRelY = cChunkDef::Height - 1; + } + if (a_MaxRelY < 0) + { + LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelY = 0; + } + else if (a_MinRelY >= cChunkDef::Height) + { + LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MaxRelY = cChunkDef::Height - 1; + } + + if (a_MinRelZ < 0) + { + LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__); + a_MinRelZ = 0; + } + else if (a_MinRelZ >= cChunkDef::Width) + { + LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MinRelZ = cChunkDef::Width - 1; + } + if (a_MaxRelZ < 0) + { + LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelZ = 0; + } + else if (a_MinRelZ >= cChunkDef::Width) + { + LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MaxRelZ = cChunkDef::Width - 1; + } + + // Prepare the block area: + int SizeX = a_MaxRelX - a_MinRelX; + int SizeY = a_MaxRelY - a_MinRelY; + int SizeZ = a_MaxRelZ - a_MinRelZ; + a_Dest.Clear(); + a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX; + a_Dest.m_OriginY = a_MinRelY; + a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ; + a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas); + + for (int y = 0; y < SizeY; y++) + { + int CDY = a_MinRelY + y; + for (int z = 0; z < SizeZ; z++) + { + int CDZ = a_MinRelZ + z; + for (int x = 0; x < SizeX; x++) + { + int CDX = a_MinRelX + x; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta); + a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta); + } // for x + } // for z + } // for y +} + + + + + +HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const +{ + HEIGHTTYPE MaxHeight = m_HeightMap[0]; + for (unsigned int i = 1; i < ARRAYCOUNT(m_HeightMap); i++) + { + if (m_HeightMap[i] > MaxHeight) + { + MaxHeight = m_HeightMap[i]; + } + } + return MaxHeight; +} + + + + + +void cChunkDesc::FillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); + } + } // for z + } // for y +} + + + + + +void cChunkDesc::ReplaceRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(x, y, z, BlockType, BlockMeta); + if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta)) + { + SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); + } + } + } // for z + } // for y +} + + + + + +void cChunkDesc::FloorRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + switch (GetBlockType(x, y, z)) + { + case E_BLOCK_AIR: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); + break; + } + } // switch (GetBlockType) + } // for x + } // for z + } // for y +} + + + + + +void cChunkDesc::RandomFillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k +) +{ + cNoise Noise(a_RandomSeed); + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000; + if (rnd <= a_ChanceOutOf10k) + { + SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); + } + } + } // for z + } // for y +} + + + + + +cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ) +{ + int AbsX = a_RelX + m_ChunkX * cChunkDef::Width; + int AbsZ = a_RelZ + m_ChunkZ * cChunkDef::Width; + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr) + { + if (((*itr)->GetPosX() == AbsX) && ((*itr)->GetPosY() == a_RelY) && ((*itr)->GetPosZ() == AbsZ)) + { + // Already in the list: + if ((*itr)->GetBlockType() != GetBlockType(a_RelX, a_RelY, a_RelZ)) + { + // Wrong type, the block type has been overwritten. Erase and create new: + m_BlockEntities.erase(itr); + break; + } + // Correct type, already present. Return it: + return *itr; + } + } // for itr - m_BlockEntities[] + + // The block entity is not created yet, try to create it and add to list: + cBlockEntity * be = cBlockEntity::CreateByBlockType(GetBlockType(a_RelX, a_RelY, a_RelZ), GetBlockMeta(a_RelX, a_RelY, a_RelZ), AbsX, a_RelY, AbsZ); + if (be == NULL) + { + // No block entity for this block type + return NULL; + } + m_BlockEntities.push_back(be); + return be; +} + + + + + +void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas) +{ + const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas(); + for (unsigned int i = 0; i < ARRAYCOUNT(a_DestMetas); i++) + { + a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4); + } +} + + + + + +#ifdef _DEBUG + +void cChunkDesc::VerifyHeightmap(void) +{ + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = cChunkDef::Height - 1; y > 0; y--) + { + BLOCKTYPE BlockType = GetBlockType(x, y, z); + if (BlockType != E_BLOCK_AIR) + { + int Height = GetHeight(x, z); + ASSERT(Height == y); + break; + } + } // for y + } // for z + } // for x +} + +#endif // _DEBUG + + + + + diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h new file mode 100644 index 000000000..e130c463f --- /dev/null +++ b/src/Generating/ChunkDesc.h @@ -0,0 +1,217 @@ + +// ChunkDesc.h + +// Declares the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. + + + + + +#pragma once + +#include "../BlockArea.h" +#include "../ChunkDef.h" +#include "../Cuboid.h" + + + + + +// fwd: ../BlockArea.h +class cBlockArea; + + + + + +// tolua_begin +class cChunkDesc +{ +public: + // tolua_end + + /// Uncompressed block metas, 1 meta per byte + typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks]; + + 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 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); + + void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); + BLOCKTYPE GetBlockType(int a_RelX, int a_RelY, int a_RelZ); + + void SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); + NIBBLETYPE GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ); + + void SetBiome(int a_RelX, int a_RelZ, int a_BiomeID); + EMCSBiome GetBiome(int a_RelX, int a_RelZ); + + void SetHeight(int a_RelX, int a_RelZ, int a_Height); + int GetHeight(int a_RelX, int a_RelZ); + + // Default generation: + void SetUseDefaultBiomes(bool a_bUseDefaultBiomes); + bool IsUsingDefaultBiomes(void) const; + void SetUseDefaultHeight(bool a_bUseDefaultHeight); + bool IsUsingDefaultHeight(void) const; + void SetUseDefaultComposition(bool a_bUseDefaultComposition); + bool IsUsingDefaultComposition(void) const; + void SetUseDefaultStructures(bool a_bUseDefaultStructures); + bool IsUsingDefaultStructures(void) const; + void SetUseDefaultFinish(bool a_bUseDefaultFinish); + bool IsUsingDefaultFinish(void) const; + + /// 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, 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 + HEIGHTTYPE GetMaxHeight(void) const; + + /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk + void FillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ); + + /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk + void FillRelCuboid(const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + FillRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_BlockType, a_BlockMeta + ); + } + + /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk + void ReplaceRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ); + + /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk + void ReplaceRelCuboid( + const cCuboid & a_RelCuboid, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ) + { + ReplaceRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_SrcType, a_SrcMeta, + a_DstType, a_DstMeta + ); + } + + /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk + void FloorRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ); + + /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk + void FloorRelCuboid( + const cCuboid & a_RelCuboid, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta + ) + { + FloorRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_DstType, a_DstMeta + ); + } + + /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk + void RandomFillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k + ); + + /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk + void RandomFillRelCuboid( + const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k + ) + { + RandomFillRelCuboid( + a_RelCuboid.p1.x, a_RelCuboid.p2.x, + a_RelCuboid.p1.y, a_RelCuboid.p2.y, + a_RelCuboid.p1.z, a_RelCuboid.p2.z, + a_BlockType, a_BlockMeta, + a_RandomSeed, a_ChanceOutOf10k + ); + } + + /// Returns the block entity at the specified coords. + /// If there is no block entity at those coords, tries to create one, based on the block type + /// If the blocktype doesn't support a block entity, returns NULL. + cBlockEntity * GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ); + + // tolua_end + + // Accessors used by cChunkGenerator::Generator descendants: + inline cChunkDef::BiomeMap & GetBiomeMap (void) { return m_BiomeMap; } + inline cChunkDef::BlockTypes & GetBlockTypes (void) { return *((cChunkDef::BlockTypes *)m_BlockArea.GetBlockTypes()); } + // CANNOT, different compression! + // inline cChunkDef::BlockNibbles & GetBlockMetas (void) { return *((cChunkDef::BlockNibbles *)m_BlockArea.GetBlockMetas()); } + inline BlockNibbleBytes & GetBlockMetasUncompressed(void) { return *((BlockNibbleBytes *)m_BlockArea.GetBlockMetas()); } + inline cChunkDef::HeightMap & GetHeightMap (void) { return m_HeightMap; } + inline cEntityList & GetEntities (void) { return m_Entities; } + inline cBlockEntityList & GetBlockEntities (void) { return m_BlockEntities; } + + /// Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte) + void CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas); + + #ifdef _DEBUG + /// Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column + void VerifyHeightmap(void); + #endif // _DEBUG + +private: + int m_ChunkX; + int m_ChunkZ; + + cChunkDef::BiomeMap m_BiomeMap; + cBlockArea m_BlockArea; + cChunkDef::HeightMap m_HeightMap; + cEntityList m_Entities; // Individual entities are NOT owned by this object! + cBlockEntityList m_BlockEntities; // Individual block entities are NOT owned by this object! + + bool m_bUseDefaultBiomes; + bool m_bUseDefaultHeight; + bool m_bUseDefaultComposition; + bool m_bUseDefaultStructures; + bool m_bUseDefaultFinish; +} ; // tolua_export + + + + diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp new file mode 100644 index 000000000..cb993bb05 --- /dev/null +++ b/src/Generating/ChunkGenerator.cpp @@ -0,0 +1,329 @@ + +#include "Globals.h" + +#include "ChunkGenerator.h" +#include "../World.h" +#include "lib/iniFile/iniFile.h" +#include "../Root.h" +#include "../PluginManager.h" +#include "ChunkDesc.h" +#include "ComposableGenerator.h" +#include "Noise3DGenerator.h" + + + + + +/// If the generation queue size exceeds this number, a warning will be output +const unsigned int QUEUE_WARNING_LIMIT = 1000; + +/// If the generation queue size exceeds this number, chunks with no clients will be skipped +const unsigned int QUEUE_SKIP_LIMIT = 500; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkGenerator: + +cChunkGenerator::cChunkGenerator(void) : + super("cChunkGenerator"), + m_World(NULL), + m_Generator(NULL) +{ +} + + + + + +cChunkGenerator::~cChunkGenerator() +{ + Stop(); +} + + + + + +bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile) +{ + MTRand rnd; + m_World = a_World; + m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt()); + AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable"); + + if (NoCaseCompare(GeneratorName, "Noise3D") == 0) + { + m_Generator = new cNoise3DGenerator(*this); + } + else + { + if (NoCaseCompare(GeneratorName, "composable") != 0) + { + LOGWARN("[Generator]::Generator value \"%s\" not recognized, using \"Composable\".", GeneratorName.c_str()); + } + m_Generator = new cComposableGenerator(*this); + } + + if (m_Generator == NULL) + { + LOGERROR("Generator could not start, aborting the server"); + return false; + } + + m_Generator->Initialize(a_World, a_IniFile); + + return super::Start(); +} + + + + + +void cChunkGenerator::Stop(void) +{ + m_ShouldTerminate = true; + m_Event.Set(); + m_evtRemoved.Set(); // Wake up anybody waiting for empty queue + Wait(); + + delete m_Generator; + m_Generator = NULL; +} + + + + + +void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CS); + + // Check if it is already in the queue: + for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Already in the queue, bail out + return; + } + } // for itr - m_Queue[] + + // Add to queue, issue a warning if too many: + if (m_Queue.size() >= QUEUE_WARNING_LIMIT) + { + LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size()); + } + m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + } + + m_Event.Set(); +} + + + + + +void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (m_Generator != NULL) + { + m_Generator->GenerateBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + } +} + + + + + +void cChunkGenerator::WaitForQueueEmpty(void) +{ + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && !m_Queue.empty()) + { + cCSUnlock Unlock(Lock); + m_evtRemoved.Wait(); + } +} + + + + + +int cChunkGenerator::GetQueueLength(void) +{ + cCSLock Lock(m_CS); + return (int)m_Queue.size(); +} + + + + + +EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) +{ + ASSERT(m_Generator != NULL); + return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ); +} + + + + + +BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default) +{ + AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default); + BLOCKTYPE Block = BlockStringToType(BlockType); + if (Block < 0) + { + LOGWARN("[&s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(),a_Default.c_str()); + return BlockStringToType(a_Default); + } + return Block; +} + + + + + +void cChunkGenerator::Execute(void) +{ + // To be able to display performance information, the generator counts the chunks generated. + // When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time. + int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty + clock_t GenerationStart = clock(); // Clock tick when the queue started to fill + clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often) + + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_Queue.size() == 0) + { + if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC)) + { + LOG("Chunk generator performance: %.2f ch/s (%d ch total)", + (double)NumChunksGenerated * CLOCKS_PER_SEC/ (clock() - GenerationStart), + NumChunksGenerated + ); + } + cCSUnlock Unlock(Lock); + m_Event.Wait(); + if (m_ShouldTerminate) + { + return; + } + NumChunksGenerated = 0; + GenerationStart = clock(); + LastReportTick = clock(); + } + + cChunkCoords coords = m_Queue.front(); // Get next coord from queue + m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue + bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); + Lock.Unlock(); // Unlock ASAP + m_evtRemoved.Set(); + + // Display perf info once in a while: + if ((NumChunksGenerated > 16) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC)) + { + LOG("Chunk generator performance: %.2f ch/s (%d ch total)", + (double)NumChunksGenerated * CLOCKS_PER_SEC / (clock() - GenerationStart), + NumChunksGenerated + ); + LastReportTick = clock(); + } + + // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set + if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) + { + LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); + // Already generated, ignore request + continue; + } + + if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) + { + LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + continue; + } + + LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + + // Save the chunk right after generating, so that we don't have to generate it again on next run + m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ); + + NumChunksGenerated++; + } // while (!bStop) +} + + + + +void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ); + cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); + m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc); + cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc); + + #ifdef _DEBUG + // Verify that the generator has produced valid data: + ChunkDesc.VerifyHeightmap(); + #endif + + cChunkDef::BlockNibbles BlockMetas; + ChunkDesc.CompressBlockMetas(BlockMetas); + + m_World->SetChunkData( + a_ChunkX, a_ChunkZ, + ChunkDesc.GetBlockTypes(), BlockMetas, + NULL, NULL, // We don't have lighting, chunk will be lighted when needed + &ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(), + ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(), + true + ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkGenerator::cGenerator: + +cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) : + m_ChunkGenerator(a_ChunkGenerator) +{ +} + + + + + +void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + m_World = a_World; + UNUSED(a_IniFile); +} + + + + + +EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ) +{ + cChunkDef::BiomeMap Biomes; + int Y = 0; + int ChunkX, ChunkZ; + cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ); + GenerateBiomes(ChunkX, ChunkZ, Biomes); + return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ); +} + + + + diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h new file mode 100644 index 000000000..2d3bb8082 --- /dev/null +++ b/src/Generating/ChunkGenerator.h @@ -0,0 +1,113 @@ + +// ChunkGenerator.h + +// Interfaces to the cChunkGenerator class representing the thread that generates chunks + +/* +The object takes requests for generating chunks and processes them in a separate thread one by one. +The requests are not added to the queue if there is already a request with the same coords +Before generating, the thread checks if the chunk hasn't been already generated. +It is theoretically possible to have multiple generator threads by having multiple instances of this object, +but then it MAY happen that the chunk is generated twice. +If the generator queue is overloaded, the generator skips chunks with no clients in them +*/ + + + + + +#pragma once + +#include "../OSSupport/IsThread.h" +#include "../ChunkDef.h" + + + + + +// fwd: +class cWorld; +class cIniFile; +class cChunkDesc; + + + + + +class cChunkGenerator : + cIsThread +{ + typedef cIsThread super; + +public: + /// The interface that a class has to implement to become a generator + class cGenerator + { + public: + cGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cGenerator() {} ; // Force a virtual destructor + + /// Called to initialize the generator on server startup. + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile); + + /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; + + /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome. Default implementation uses GenerateBiomes(). + virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); + + /// Called in a separate thread to do the actual chunk generation. Generator should generate into a_ChunkDesc. + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0; + + protected: + cChunkGenerator & m_ChunkGenerator; + cWorld * m_World; + } ; + + + cChunkGenerator (void); + ~cChunkGenerator(); + + bool Start(cWorld * a_World, cIniFile & a_IniFile); + void Stop(void); + + /// Queues the chunk for generation; removes duplicate requests + void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. + void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); + + void WaitForQueueEmpty(void); + + int GetQueueLength(void); + + int GetSeed(void) const { return m_Seed; } + + /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome + EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); + + /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. + static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default); + +private: + + cWorld * m_World; + + int m_Seed; + + cCriticalSection m_CS; + cChunkCoordsList m_Queue; + cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate + cEvent m_evtRemoved; ///< Set when an item is removed from the queue + + cGenerator * m_Generator; ///< The actual generator engine used to generate chunks + + // cIsThread override: + virtual void Execute(void) override; + + void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ); +}; + + + + diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp new file mode 100644 index 000000000..7e1ade147 --- /dev/null +++ b/src/Generating/CompoGen.cpp @@ -0,0 +1,634 @@ + +// CompoGen.cpp + +/* Implements the various terrain composition generators: + - cCompoGenSameBlock + - cCompoGenDebugBiomes + - cCompoGenClassic +*/ + +#include "Globals.h" +#include "CompoGen.h" +#include "../BlockID.h" +#include "../Item.h" +#include "../LinearUpscale.h" +#include "lib/inifile/iniFile.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenSameBlock: + +void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int Start; + if (m_IsBedrocked) + { + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + Start = 1; + } + else + { + Start = 0; + } + for (int y = a_ChunkDesc.GetHeight(x, z); y >= Start; y--) + { + a_ChunkDesc.SetBlockType(x, y, z, m_BlockType); + } // for y + } // for z + } // for x +} + + + + + +void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_BlockType = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "SameBlockType", "stone").m_ItemType); + m_IsBedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenDebugBiomes: + +void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + static BLOCKTYPE Blocks[] = + { + E_BLOCK_STONE, + E_BLOCK_COBBLESTONE, + E_BLOCK_LOG, + E_BLOCK_PLANKS, + E_BLOCK_SANDSTONE, + E_BLOCK_WOOL, + E_BLOCK_COAL_ORE, + E_BLOCK_IRON_ORE, + E_BLOCK_GOLD_ORE, + E_BLOCK_DIAMOND_ORE, + E_BLOCK_LAPIS_ORE, + E_BLOCK_REDSTONE_ORE, + E_BLOCK_IRON_BLOCK, + E_BLOCK_GOLD_BLOCK, + E_BLOCK_DIAMOND_BLOCK, + E_BLOCK_LAPIS_BLOCK, + E_BLOCK_BRICK, + E_BLOCK_MOSSY_COBBLESTONE, + E_BLOCK_OBSIDIAN, + E_BLOCK_NETHERRACK, + E_BLOCK_SOULSAND, + E_BLOCK_NETHER_BRICK, + E_BLOCK_BEDROCK, + } ; + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + BLOCKTYPE BlockType = Blocks[a_ChunkDesc.GetBiome(x, z)]; + for (int y = a_ChunkDesc.GetHeight(x, z); y >= 0; y--) + { + a_ChunkDesc.SetBlockType(x, y, z, BlockType); + } // for y + } // for z + } // for x +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenClassic: + +cCompoGenClassic::cCompoGenClassic(void) : + m_SeaLevel(60), + m_BeachHeight(2), + m_BeachDepth(4), + m_BlockTop(E_BLOCK_GRASS), + m_BlockMiddle(E_BLOCK_DIRT), + m_BlockBottom(E_BLOCK_STONE), + m_BlockBeach(E_BLOCK_SAND), + m_BlockBeachBottom(E_BLOCK_SANDSTONE), + m_BlockSea(E_BLOCK_STATIONARY_WATER) +{ +} + + + + + +void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + /* The classic composition means: + - 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight + - 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight + - water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth + - water from waterlevel, then 3 dirt, the rest stone otherwise + - bedrock at the bottom + */ + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + // The patterns to use for different situations, must be same length! + const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ; + const BLOCKTYPE PatternBeach[] = {m_BlockBeach, m_BlockBeach, m_BlockBeach, m_BlockBeachBottom} ; + const BLOCKTYPE PatternOcean[] = {m_BlockMiddle, m_BlockMiddle, m_BlockMiddle, m_BlockBottom} ; + static int PatternLength = ARRAYCOUNT(PatternGround); + ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach)); + ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean)); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int Height = a_ChunkDesc.GetHeight(x, z); + const BLOCKTYPE * Pattern; + if (Height > m_SeaLevel + m_BeachHeight) + { + Pattern = PatternGround; + } + else if (Height > m_SeaLevel - m_BeachDepth) + { + Pattern = PatternBeach; + } + else + { + Pattern = PatternOcean; + } + + // Fill water from sealevel down to height (if any): + for (int y = m_SeaLevel; y >= Height; --y) + { + a_ChunkDesc.SetBlockType(x, y, z, m_BlockSea); + } + + // Fill from height till the bottom: + for (int y = Height; y >= 1; y--) + { + a_ChunkDesc.SetBlockType(x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : m_BlockBottom); + } + + // The last layer is always bedrock: + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel); + m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight); + m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth); + m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType); + m_BlockMiddle = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt").m_ItemType); + m_BlockBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBottom", "stone").m_ItemType); + m_BlockBeach = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeach", "sand").m_ItemType); + m_BlockBeachBottom = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone").m_ItemType); + m_BlockSea = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater").m_ItemType); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenBiomal: + +void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + /* + _X 2013_04_22: + There's no point in generating the whole cubic noise at once, because the noise values are used in + only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data + */ + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int Height = a_ChunkDesc.GetHeight(x, z); + if (Height > m_SeaLevel) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biOcean: + case biPlains: + case biExtremeHills: + case biForest: + case biTaiga: + case biSwampland: + case biRiver: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biForestHills: + case biTaigaHills: + case biExtremeHillsEdge: + case biJungle: + case biJungleHills: + { + FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + case biDesertHills: + case biDesert: + case biBeach: + { + FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + case biMushroomIsland: + case biMushroomShore: + { + FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + default: + { + // TODO + ASSERT(!"CompoGenBiomal: Biome not implemented yet!"); + break; + } + } + } + else + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biDesert: + case biBeach: + { + // Fill with water, sand, sandstone and stone + FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); + break; + } + default: + { + // Fill with water, sand/dirt/clay mix and stone + if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0) + { + FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes()); + } + else + { + FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes()); + } + break; + } + } // switch (biome) + a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1); + } // else (under water) + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1; +} + + + + + +void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + BLOCKTYPE Pattern[] = + { + E_BLOCK_GRASS, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } +} + + + + + +void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + BLOCKTYPE Pattern[] = + { + E_BLOCK_SAND, + E_BLOCK_SAND, + E_BLOCK_SAND, + E_BLOCK_SANDSTONE, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } +} + + + + + + +void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + BLOCKTYPE Pattern[] = + { + E_BLOCK_MYCELIUM, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } +} + + + + + +void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes); + for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER); + } +} + + + + + +void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes) +{ + // Dirt + BLOCKTYPE Pattern[] = + { + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + E_BLOCK_DIRT, + } ; + FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern)); + + for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE); + } + for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER); + } +} + + + + + + +void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize) +{ + for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++) + { + cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenNether: + +cCompoGenNether::cCompoGenNether(int a_Seed) : + m_Noise1(a_Seed + 10), + m_Noise2(a_Seed * a_Seed * 10 + a_Seed * 1000 + 6000), + m_Threshold(0) +{ +} + + + + + +void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); + + const int SEGMENT_HEIGHT = 8; + const int INTERPOL_X = 16; // Must be a divisor of 16 + const int INTERPOL_Z = 16; // Must be a divisor of 16 + // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. + // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them + // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. + + int FloorBuf1[17 * 17]; + int FloorBuf2[17 * 17]; + int * FloorHi = FloorBuf1; + int * FloorLo = FloorBuf2; + int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + + // Interpolate the lowest floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate segments: + for (int Segment = 0; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) + { + // First update the high floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate between FloorLo and FloorHi: + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + int Lo = FloorLo[x + 17 * z] / 256; + int Hi = FloorHi[x + 17 * z] / 256; + for (int y = 0; y < SEGMENT_HEIGHT; y++) + { + int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; + a_ChunkDesc.SetBlockType(x, y + Segment, z, (Val < m_Threshold) ? E_BLOCK_NETHERRACK : E_BLOCK_AIR); + } + } + + // Swap the floors: + std::swap(FloorLo, FloorHi); + } + + // Bedrock at the bottom and at the top: + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + a_ChunkDesc.SetBlockType(x, a_ChunkDesc.GetHeight(x, z), z, E_BLOCK_BEDROCK); + } +} + + + + + +void cCompoGenNether::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Threshold = a_IniFile.GetValueSetI("Generator", "NetherThreshold", m_Threshold); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompoGenCache: + +cCompoGenCache::cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize) : + m_Underlying(a_Underlying), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cCompoGenCache::~cCompoGenCache() +{ + delete[] m_CacheData; + delete[] m_CacheOrder; +} + + + + + +void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + #ifdef _DEBUG + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("CompoGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("CompoGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + #endif // _DEBUG + + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes())); + memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed())); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_Underlying.ComposeTerrain(a_ChunkDesc); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes())); + memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed())); + m_CacheData[Idx].m_ChunkX = ChunkX; + m_CacheData[Idx].m_ChunkZ = ChunkZ; +} + + + + + +void cCompoGenCache::InitializeCompoGen(cIniFile & a_IniFile) +{ + m_Underlying.InitializeCompoGen(a_IniFile); +} + + + + diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h new file mode 100644 index 000000000..2ee286b06 --- /dev/null +++ b/src/Generating/CompoGen.h @@ -0,0 +1,182 @@ + +// CompoGen.h + +/* Interfaces to the various terrain composition generators: + - cCompoGenSameBlock + - cCompoGenDebugBiomes + - cCompoGenClassic + - cCompoGenBiomal + - cCompoGenNether + - cCompoGenCache +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cCompoGenSameBlock : + public cTerrainCompositionGen +{ +public: + cCompoGenSameBlock(void) : + m_BlockType(E_BLOCK_STONE), + m_IsBedrocked(true) + {} + +protected: + + BLOCKTYPE m_BlockType; + bool m_IsBedrocked; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cCompoGenDebugBiomes : + public cTerrainCompositionGen +{ +public: + cCompoGenDebugBiomes(void) {} + +protected: + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cCompoGenClassic : + public cTerrainCompositionGen +{ +public: + cCompoGenClassic(void); + +protected: + + int m_SeaLevel; + int m_BeachHeight; + int m_BeachDepth; + BLOCKTYPE m_BlockTop; + BLOCKTYPE m_BlockMiddle; + BLOCKTYPE m_BlockBottom; + BLOCKTYPE m_BlockBeach; + BLOCKTYPE m_BlockBeachBottom; + BLOCKTYPE m_BlockSea; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cCompoGenBiomal : + public cTerrainCompositionGen +{ +public: + cCompoGenBiomal(int a_Seed) : + m_Noise(a_Seed + 1000), + m_SeaLevel(62) + { + } + +protected: + + cNoise m_Noise; + int m_SeaLevel; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; + + void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes); + + void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize); +} ; + + + + + +class cCompoGenNether : + public cTerrainCompositionGen +{ +public: + cCompoGenNether(int a_Seed); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + + int m_Threshold; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// Caches most-recently-used chunk composition of another composition generator. Caches only the types and metas +class cCompoGenCache : + public cTerrainCompositionGen +{ +public: + cCompoGenCache(cTerrainCompositionGen & a_Underlying, int a_CacheSize); // Doesn't take ownership of a_Underlying + ~cCompoGenCache(); + + // cTerrainCompositionGen override: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; + +protected: + + cTerrainCompositionGen & m_Underlying; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::BlockTypes m_BlockTypes; + cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) +} ; + + + + diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp new file mode 100644 index 000000000..496ada52d --- /dev/null +++ b/src/Generating/ComposableGenerator.cpp @@ -0,0 +1,501 @@ + +// ComposableGenerator.cpp + +// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks + +#include "Globals.h" + +#include "ComposableGenerator.h" +#include "../World.h" +#include "lib/iniFile/iniFile.h" +#include "../Root.h" + +// Individual composed algorithms: +#include "BioGen.h" +#include "HeiGen.h" +#include "CompoGen.h" +#include "StructGen.h" +#include "FinishGen.h" + +#include "Caves.h" +#include "DistortedHeightmap.h" +#include "EndGen.h" +#include "MineShafts.h" +#include "Noise3DGenerator.h" +#include "Ravines.h" + + + + + + + + + + +cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : + super(a_ChunkGenerator), + m_BiomeGen(NULL), + m_HeightGen(NULL), + m_CompositionGen(NULL), + m_UnderlyingBiomeGen(NULL), + m_UnderlyingHeightGen(NULL), + m_UnderlyingCompositionGen(NULL) +{ +} + + + + + +cComposableGenerator::~cComposableGenerator() +{ + // Delete the generating composition: + for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) + { + delete *itr; + } + m_FinishGens.clear(); + for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) + { + delete *itr; + } + m_StructureGens.clear(); + + delete m_CompositionGen; + m_CompositionGen = NULL; + delete m_HeightGen; + m_HeightGen = NULL; + delete m_BiomeGen; + m_BiomeGen = NULL; + delete m_UnderlyingCompositionGen; + m_UnderlyingCompositionGen = NULL; + delete m_UnderlyingHeightGen; + m_UnderlyingHeightGen = NULL; + delete m_UnderlyingBiomeGen; + m_UnderlyingBiomeGen = NULL; +} + + + + + +void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + super::Initialize(a_World, a_IniFile); + + InitBiomeGen(a_IniFile); + InitHeightGen(a_IniFile); + InitCompositionGen(a_IniFile); + InitStructureGens(a_IniFile); + InitFinishGens(a_IniFile); +} + + + + + +void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading + { + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + } +} + + + + + +void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) +{ + if (a_ChunkDesc.IsUsingDefaultBiomes()) + { + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap()); + } + + if (a_ChunkDesc.IsUsingDefaultHeight()) + { + m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap()); + } + + if (a_ChunkDesc.IsUsingDefaultComposition()) + { + m_CompositionGen->ComposeTerrain(a_ChunkDesc); + } + + if (a_ChunkDesc.IsUsingDefaultStructures()) + { + for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) + { + (*itr)->GenStructures(a_ChunkDesc); + } // for itr - m_StructureGens[] + } + + if (a_ChunkDesc.IsUsingDefaultFinish()) + { + for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) + { + (*itr)->GenFinish(a_ChunkDesc); + } // for itr - m_FinishGens[] + } +} + + + + + +void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) +{ + AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); + if (BiomeGenName.empty()) + { + LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\"."); + BiomeGenName = "MultiStepMap"; + } + + int Seed = m_ChunkGenerator.GetSeed(); + bool CacheOffByDefault = false; + if (NoCaseCompare(BiomeGenName, "constant") == 0) + { + m_BiomeGen = new cBioGenConstant; + CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) + } + else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) + { + m_BiomeGen = new cBioGenCheckerboard; + CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data + } + else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) + { + m_BiomeGen = new cBioGenVoronoi(Seed); + } + else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) + { + m_BiomeGen = new cBioGenDistortedVoronoi(Seed); + } + else + { + if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) + { + LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); + } + m_BiomeGen = new cBioGenMultiStepMap(Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cBioGenMultiStepMap..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 5000; x++) + { + cChunkDef::BiomeMap Biomes; + m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGD("Using a cache for biomegen of size %d.", CacheSize); + m_UnderlyingBiomeGen = m_BiomeGen; + m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); + } + m_BiomeGen->InitializeBiomeGen(a_IniFile); +} + + + + + +void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) +{ + AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); + if (HeightGenName.empty()) + { + LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\"."); + HeightGenName = "Biomal"; + } + + int Seed = m_ChunkGenerator.GetSeed(); + bool CacheOffByDefault = false; + if (NoCaseCompare(HeightGenName, "flat") == 0) + { + m_HeightGen = new cHeiGenFlat; + CacheOffByDefault = true; // We're generating faster than a cache would retrieve data + } + else if (NoCaseCompare(HeightGenName, "classic") == 0) + { + m_HeightGen = new cHeiGenClassic(Seed); + } + else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) + { + m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); + } + else if (NoCaseCompare(HeightGenName, "End") == 0) + { + m_HeightGen = new cEndGen(Seed); + } + else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) + { + m_HeightGen = new cNoise3DComposable(Seed); + } + else // "biomal" or <not found> + { + if (NoCaseCompare(HeightGenName, "biomal") != 0) + { + LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); + } + m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cHeiGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDef::HeightMap Heights; + m_HeightGen->GenHeightMap(x * 5, x * 5, Heights); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Read the settings: + m_HeightGen->InitializeHeightGen(a_IniFile); + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGD("Using a cache for Heightgen of size %d.", CacheSize); + m_UnderlyingHeightGen = m_HeightGen; + m_HeightGen = new cHeiGenCache(*m_UnderlyingHeightGen, CacheSize); + } +} + + + + + +void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) +{ + int Seed = m_ChunkGenerator.GetSeed(); + AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); + if (CompoGenName.empty()) + { + LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\"."); + CompoGenName = "Biomal"; + } + if (NoCaseCompare(CompoGenName, "sameblock") == 0) + { + m_CompositionGen = new cCompoGenSameBlock; + } + else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) + { + m_CompositionGen = new cCompoGenDebugBiomes; + } + else if (NoCaseCompare(CompoGenName, "classic") == 0) + { + m_CompositionGen = new cCompoGenClassic; + } + else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) + { + m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen); + } + else if (NoCaseCompare(CompoGenName, "end") == 0) + { + m_CompositionGen = new cEndGen(Seed); + } + else if (NoCaseCompare(CompoGenName, "nether") == 0) + { + m_CompositionGen = new cCompoGenNether(Seed); + } + else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) + { + m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); + } + else + { + if (NoCaseCompare(CompoGenName, "biomal") != 0) + { + LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); + } + m_CompositionGen = new cCompoGenBiomal(Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cCompoGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDesc Desc(200 + x * 8, 200 + x * 8); + m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); + m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); + m_CompositionGen->ComposeTerrain(Desc); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Read the settings from the ini file: + m_CompositionGen->InitializeCompoGen(a_IniFile); + + int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); + if (CompoGenCacheSize > 1) + { + m_UnderlyingCompositionGen = m_CompositionGen; + m_CompositionGen = new cCompoGenCache(*m_UnderlyingCompositionGen, 32); + } +} + + + + + +void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) +{ + AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees"); + + int Seed = m_ChunkGenerator.GetSeed(); + AStringVector Str = StringSplitAndTrim(Structures, ","); + for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) + { + if (NoCaseCompare(*itr, "DualRidgeCaves") == 0) + { + float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3); + m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold)); + } + else if (NoCaseCompare(*itr, "DirectOverhangs") == 0) + { + m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed)); + } + else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0) + { + m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed)); + } + else if (NoCaseCompare(*itr, "LavaLakes") == 0) + { + int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10); + m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability)); + } + else if (NoCaseCompare(*itr, "MarbleCaves") == 0) + { + m_StructureGens.push_back(new cStructGenMarbleCaves(Seed)); + } + else if (NoCaseCompare(*itr, "MineShafts") == 0) + { + int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512); + int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160); + int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600); + int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200); + int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200); + m_StructureGens.push_back(new cStructGenMineShafts( + Seed, GridSize, MaxSystemSize, + ChanceCorridor, ChanceCrossing, ChanceStaircase + )); + } + else if (NoCaseCompare(*itr, "OreNests") == 0) + { + m_StructureGens.push_back(new cStructGenOreNests(Seed)); + } + else if (NoCaseCompare(*itr, "Ravines") == 0) + { + m_StructureGens.push_back(new cStructGenRavines(Seed, 128)); + } + else if (NoCaseCompare(*itr, "Trees") == 0) + { + m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)); + } + else if (NoCaseCompare(*itr, "WaterLakes") == 0) + { + int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); + m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability)); + } + else if (NoCaseCompare(*itr, "WormNestCaves") == 0) + { + m_StructureGens.push_back(new cStructGenWormNestCaves(Seed)); + } + else + { + LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str()); + } + } // for itr - Str[] +} + + + + + +void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) +{ + int Seed = m_ChunkGenerator.GetSeed(); + AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); + + AStringVector Str = StringSplitAndTrim(Structures, ","); + for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) + { + // Finishers, alpha-sorted: + if (NoCaseCompare(*itr, "BottomLava") == 0) + { + int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10; + int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); + m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel)); + } + else if (NoCaseCompare(*itr, "DeadBushes") == 0) + { + m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND)); + } + else if (NoCaseCompare(*itr, "Ice") == 0) + { + m_FinishGens.push_back(new cFinishGenIce); + } + else if (NoCaseCompare(*itr, "LavaSprings") == 0) + { + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World)); + } + else if (NoCaseCompare(*itr, "Lilypads") == 0) + { + m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER)); + } + else if (NoCaseCompare(*itr, "PreSimulator") == 0) + { + m_FinishGens.push_back(new cFinishGenPreSimulator); + } + else if (NoCaseCompare(*itr, "Snow") == 0) + { + m_FinishGens.push_back(new cFinishGenSnow); + } + else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0) + { + m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed)); + } + else if (NoCaseCompare(*itr, "WaterSprings") == 0) + { + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World)); + } + } // for itr - Str[] +} + + + + diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h new file mode 100644 index 000000000..d5e33a439 --- /dev/null +++ b/src/Generating/ComposableGenerator.h @@ -0,0 +1,181 @@ + +// ComposableGenerator.h + +// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks + +/* +Generating works by composing several algorithms: +Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage +Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others, +based on user's preferences in the world.ini. +See http://forum.mc-server.org/showthread.php?tid=409 for details. +*/ + + + + + +#pragma once + +#include "ChunkGenerator.h" +#include "ChunkDesc.h" + + + + + +// fwd: Noise3DGenerator.h +class cNoise3DComposable; + +// fwd: DistortedHeightmap.h +class cDistortedHeightmap; + + + + + +/** The interface that a biome generator must implement +A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output. +The output array is sequenced in the same way as the MapChunk packet's biome data. +*/ +class cBiomeGen +{ +public: + virtual ~cBiomeGen() {} // Force a virtual destructor in descendants + + /// Generates biomes for the given chunk + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeBiomeGen(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a terrain height generator must implement +A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk. +The output array is sequenced in the same way as the BiomeGen's biome data. +The generator may request biome information from the underlying BiomeGen, it may even request information for +other chunks than the one it's currently generating (possibly neighbors - for averaging) +*/ +class cTerrainHeightGen +{ +public: + virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants + + /// Generates heightmap for the given chunk + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeHeightGen(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a terrain composition generator must implement +Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with +the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose, +but it may request information for other chunks than the one it's currently generating from them. +*/ +class cTerrainCompositionGen +{ +public: + virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants + + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void InitializeCompoGen(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a structure generator must implement +Structures are generated after the terrain composition took place. It should modify the blocktype data to account +for whatever structures the generator is generating. +Note that ores are considered structures too, at least from the interface point of view. +Also note that a worldgenerator may contain multiple structure generators, one for each type of structure +*/ +class cStructureGen +{ +public: + virtual ~cStructureGen() {} // Force a virtual destructor in descendants + + virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::list<cStructureGen *> cStructureGenList; + + + + + +/** The interface that a finisher must implement +Finisher implements small additions after all structures have been generated. +*/ +class cFinishGen +{ +public: + virtual ~cFinishGen() {} // Force a virtual destructor in descendants + + virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::list<cFinishGen *> cFinishGenList; + + + + + +class cComposableGenerator : + public cChunkGenerator::cGenerator +{ + typedef cChunkGenerator::cGenerator super; + +public: + cComposableGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cComposableGenerator(); + + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; + +protected: + // The generation composition: + cBiomeGen * m_BiomeGen; + cTerrainHeightGen * m_HeightGen; + cTerrainCompositionGen * m_CompositionGen; + cStructureGenList m_StructureGens; + cFinishGenList m_FinishGens; + + // Generators underlying the caches: + cBiomeGen * m_UnderlyingBiomeGen; + cTerrainHeightGen * m_UnderlyingHeightGen; + cTerrainCompositionGen * m_UnderlyingCompositionGen; + + + /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly + void InitBiomeGen(cIniFile & a_IniFile); + + /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly + void InitHeightGen(cIniFile & a_IniFile); + + /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly + void InitCompositionGen(cIniFile & a_IniFile); + + /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly + void InitStructureGens(cIniFile & a_IniFile); + + /// Reads the finishers from the ini and initializes m_FinishGens accordingly + void InitFinishGens(cIniFile & a_IniFile); +} ; + + + + diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp new file mode 100644 index 000000000..6fbe7c1d4 --- /dev/null +++ b/src/Generating/DistortedHeightmap.cpp @@ -0,0 +1,444 @@ + +// DistortedHeightmap.cpp + +// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs + +#include "Globals.h" + +#include "DistortedHeightmap.h" +#include "../OSSupport/File.h" +#include "lib/inifile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +/** This table assigns a relative maximum overhang size in each direction to biomes. +Both numbers indicate a number which will multiply the noise value for each coord; +this means that you can have different-sized overhangs in each direction. +Usually you'd want to keep both numbers the same. +The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible +due to the way that noise is calculated. +*/ +const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] = +{ + /* Biome | AmpX | AmpZ */ + /* biOcean */ { 1.5f, 1.5f}, + /* biPlains */ { 0.5f, 0.5f}, + /* biDesert */ { 0.5f, 0.5f}, + /* biExtremeHills */ {16.0f, 16.0f}, + /* biForest */ { 3.0f, 3.0f}, + /* biTaiga */ { 1.5f, 1.5f}, + + /* biSwampland */ { 0.0f, 0.0f}, + /* biRiver */ { 0.0f, 0.0f}, + /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing + /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing + /* biFrozenOcean */ { 0.0f, 0.0f}, + /* biFrozenRiver */ { 0.0f, 0.0f}, + /* biIcePlains */ { 0.0f, 0.0f}, + /* biIceMountains */ { 8.0f, 8.0f}, + /* biMushroomIsland */ { 4.0f, 4.0f}, + /* biMushroomShore */ { 0.0f, 0.0f}, + /* biBeach */ { 0.0f, 0.0f}, + /* biDesertHills */ { 5.0f, 5.0f}, + /* biForestHills */ { 6.0f, 6.0f}, + /* biTaigaHills */ { 8.0f, 8.0f}, + /* biExtremeHillsEdge */ { 7.0f, 7.0f}, + /* biJungle */ { 0.0f, 0.0f}, + /* biJungleHills */ { 8.0f, 8.0f}, +} ; + + + + + +cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : + m_NoiseDistortX(a_Seed + 1000), + m_NoiseDistortZ(a_Seed + 2000), + m_OceanFloorSelect(a_Seed + 3000), + m_BiomeGen(a_BiomeGen), + m_UnderlyingHeiGen(a_Seed, a_BiomeGen), + m_HeightGen(m_UnderlyingHeiGen, 64) +{ + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); + + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); +} + + + + + +void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) +{ + if (m_IsInitialized) + { + return; + } + + // Read the params from the INI file: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); + + m_IsInitialized = true; +} + + + + + +void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ) +{ + if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ)) + { + return; + } + m_CurChunkX = a_ChunkX; + m_CurChunkZ = a_ChunkZ; + + + m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights); + UpdateDistortAmps(); + GenerateHeightArray(); +} + + + + + +void cDistortedHeightmap::GenerateHeightArray(void) +{ + // Generate distortion noise: + NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ; + + m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); + m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); + + // The distorted heightmap, before linear upscaling + NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z]; + + // Distort the heightmap using the distortion: + for (int z = 0; z < DIM_Z; z++) + { + int AmpIdx = z * DIM_X; + for (int y = 0; y < DIM_Y; y++) + { + int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X; + for (int x = 0; x < DIM_X; x++) + { + NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x]; + NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x]; + DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X); + DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z); + // Adding 0.5 helps alleviate the interpolation artifacts + DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5; + } + } + } + + // Upscale the distorted heightmap into full dimensions: + LinearUpscale3DArray( + DistHei, DIM_X, DIM_Y, DIM_Z, + m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z + ); + + // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ)); +} + + + + + +void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + PrepareState(a_ChunkX, a_ChunkZ); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int NoiseArrayIdx = x + 17 * 257 * z; + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--) + { + int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; + if (y < HeightMapHeight) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + +void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + // Frequencies for the ocean floor selecting noise: + NOISE_DATATYPE FrequencyX = 3; + NOISE_DATATYPE FrequencyZ = 3; + + // Prepare the internal state for generating this chunk: + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + // Compose: + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int NoiseArrayIdx = x + 17 * 257 * z; + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 1; y > 0; y--) + { + int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; + + if (y >= HeightMapHeight) + { + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; + } + // "ground" part: + if (y < LastAir - 4) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); + continue; + } + if (HasHadWater) + { + // Decide between clay, sand and dirt + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX; + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ; + NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); + if (Val < -0.95) + { + // Clay: + switch (LastAir - y) + { + case 0: + case 1: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY); + break; + } + case 2: + case 3: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + break; + } + case 4: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE); + break; + } + } // switch (floor depth) + } + else if (Val < 0) + { + a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT); + } + } + else + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biOcean: + case biPlains: + case biExtremeHills: + case biForest: + case biTaiga: + case biSwampland: + case biRiver: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biForestHills: + case biTaigaHills: + case biExtremeHillsEdge: + case biJungle: + case biJungleHills: + { + a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + break; + } + case biDesertHills: + case biDesert: + case biBeach: + { + a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); + break; + } + case biMushroomIsland: + case biMushroomShore: + { + a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT); + break; + } + } + } + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile) +{ + Initialize(a_IniFile); +} + + + + + +int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) +{ + int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); + int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16); + int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width); + int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width); + + // If we're withing the same chunk, return the pre-cached heightmap: + if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ)) + { + return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ); + } + + // Ask the cache: + HEIGHTTYPE res = 0; + if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res)) + { + // The height was in the cache + return res; + } + + // The height is not in the cache, generate full heightmap and get it there: + cChunkDef::HeightMap Heightmap; + m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap); + return cChunkDef::GetHeight(Heightmap, RelX, RelZ); +} + + + + + +void cDistortedHeightmap::UpdateDistortAmps(void) +{ + BiomeNeighbors Biomes; + for (int z = -1; z <= 1; z++) + { + for (int x = -1; x <= 1; x++) + { + m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]); + } // for x + } // for z + + for (int z = 0; z < DIM_Z; z++) + { + for (int x = 0; x < DIM_Z; x++) + { + GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]); + } + } +} + + + + + +void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ) +{ + // Sum up how many biomes of each type there are in the neighborhood: + int BiomeCounts[biNumBiomes]; + memset(BiomeCounts, 0, sizeof(BiomeCounts)); + int Sum = 0; + for (int z = -8; z <= 8; z++) + { + int FinalZ = a_RelZ + z + cChunkDef::Width; + int IdxZ = FinalZ / cChunkDef::Width; + int ModZ = FinalZ % cChunkDef::Width; + int WeightZ = 9 - abs(z); + for (int x = -8; x <= 8; x++) + { + int FinalX = a_RelX + x + cChunkDef::Width; + int IdxX = FinalX / cChunkDef::Width; + int ModX = FinalX % cChunkDef::Width; + EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ); + if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) + { + continue; + } + int WeightX = 9 - abs(x); + BiomeCounts[Biome] += WeightX + WeightZ; + Sum += WeightX + WeightZ; + } // for x + } // for z + + if (Sum <= 0) + { + // No known biome around? Weird. Return a bogus value: + ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); + a_DistortAmpX = 16; + a_DistortAmpZ = 16; + } + + // For each biome type that has a nonzero count, calc its amps and add it: + NOISE_DATATYPE AmpX = 0; + NOISE_DATATYPE AmpZ = 0; + for (unsigned int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) + { + AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX; + AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ; + } + a_DistortAmpX = AmpX / Sum; + a_DistortAmpZ = AmpZ / Sum; +} + + + + diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h new file mode 100644 index 000000000..6d7007375 --- /dev/null +++ b/src/Generating/DistortedHeightmap.h @@ -0,0 +1,108 @@ + +// DistortedHeightmap.h + +// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "HeiGen.h" +#include "../Noise.h" + + + + + +#define NOISE_SIZE_Y (257 + 32) + + + + + +class cDistortedHeightmap : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); + +protected: + typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; + + // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int INTERPOL_X = 8; + static const int INTERPOL_Y = 4; + static const int INTERPOL_Z = 8; + + // Linear upscaling buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + (17 / INTERPOL_X); + static const int DIM_Y = 1 + (257 / INTERPOL_Y); + static const int DIM_Z = 1 + (17 / INTERPOL_Z); + + cPerlinNoise m_NoiseDistortX; + cPerlinNoise m_NoiseDistortZ; + cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor + + int m_SeaLevel; + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + int m_CurChunkX; + int m_CurChunkZ; + NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17]; + + cBiomeGen & m_BiomeGen; + cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion) + cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen + + /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. + cChunkDef::HeightMap m_CurChunkHeights; + + // Per-biome terrain generator parameters: + struct sGenParam + { + NOISE_DATATYPE m_DistortAmpX; + NOISE_DATATYPE m_DistortAmpZ; + } ; + static const sGenParam m_GenParam[biNumBiomes]; + + // Distortion amplitudes for each direction, before linear upscaling + NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; + NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; + + /// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen) + bool m_IsInitialized; + + + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) + void PrepareState(int a_ChunkX, int a_ChunkZ); + + /// Generates the m_DistortedHeightmap array for the current chunk + void GenerateHeightArray(void); + + /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords + int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z); + + /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ + void UpdateDistortAmps(void); + + /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes + void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); + + /// Reads the settings from the ini file. Skips reading if already initialized + void Initialize(cIniFile & a_IniFile); + + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; + virtual void InitializeCompoGen(cIniFile & a_IniFile) override; +} ; diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp new file mode 100644 index 000000000..121ed4488 --- /dev/null +++ b/src/Generating/EndGen.cpp @@ -0,0 +1,217 @@ + +// EndGen.cpp + +// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen + +#include "Globals.h" +#include "EndGen.h" +#include "lib/inifile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +enum +{ + // Interpolation cell size: + INTERPOL_X = 4, + INTERPOL_Y = 4, + INTERPOL_Z = 4, + + // Size of chunk data, downscaled before interpolation: + DIM_X = 16 / INTERPOL_X + 1, + DIM_Y = 256 / INTERPOL_Y + 1, + DIM_Z = 16 / INTERPOL_Z + 1, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEndGen: + +cEndGen::cEndGen(int a_Seed) : + m_Seed(a_Seed), + m_IslandSizeX(256), + m_IslandSizeY(96), + m_IslandSizeZ(256), + m_FrequencyX(80), + m_FrequencyY(80), + m_FrequencyZ(80) +{ + m_Perlin.AddOctave(1, 1); + m_Perlin.AddOctave(2, 0.5); + m_Perlin.AddOctave(4, 0.25); +} + + + + + +void cEndGen::Initialize(cIniFile & a_IniFile) +{ + m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX); + m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY); + m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ); + + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ); + + // Recalculate the min and max chunk coords of the island + m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width; + m_MinChunkX = -m_MaxChunkX; + m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width; + m_MinChunkZ = -m_MaxChunkZ; +} + + + + + +/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) +void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function + + if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ)) + { + return; + } + + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + GenerateNoiseArray(); +} + + + + + +/// Generates the m_NoiseArray array for the current chunk +void cEndGen::GenerateNoiseArray(void) +{ + NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] + NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] + + // Generate the downscaled noise: + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY; + m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace); + + // Add distance: + int idx = 0; + for (int y = 0; y < DIM_Y; y++) + { + NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY; + ValY = ValY * ValY; + for (int z = 0; z < DIM_Z; z++) + { + NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ; + ValZ = ValZ * ValZ; + for (int x = 0; x < DIM_X; x++) + { + // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1); + NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX; + ValX = ValX * ValX; + NoiseData[idx++] += ValX + ValZ + ValY; + } // for x + } // for z + } // for y + + // Upscale into real chunk size: + LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y); +} + + + + + +/// Returns true if the chunk is outside of the island's dimensions +bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ) +{ + return ( + (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) || + (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ) + ); +} + + + + + +void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ)) + { + for (unsigned int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) + { + a_HeightMap[i] = 0; + } + return; + } + + PrepareState(a_ChunkX, a_ChunkZ); + + int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, MaxY); + for (int y = MaxY; y > 0; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ())) + { + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + return; + } + + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = MaxY; y > 0; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) + { + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); + } + else + { + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h new file mode 100644 index 000000000..4904a0e3d --- /dev/null +++ b/src/Generating/EndGen.h @@ -0,0 +1,69 @@ + +// EndGen.h + +// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cEndGen : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cEndGen(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + + /// Seed for the noise + int m_Seed; + + /// The Perlin noise used for generating + cPerlinNoise m_Perlin; + + // XYZ size of the "island", in blocks: + int m_IslandSizeX; + int m_IslandSizeY; + int m_IslandSizeZ; + + // XYZ Frequencies of the noise functions: + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all + int m_MinChunkX, m_MaxChunkX; + int m_MinChunkZ, m_MaxChunkZ; + + // Noise array for the last chunk (in the noise range) + int m_LastChunkX; + int m_LastChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y + + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) + void PrepareState(int a_ChunkX, int a_ChunkZ); + + /// Generates the m_NoiseArray array for the current chunk + void GenerateNoiseArray(void); + + /// Returns true if the chunk is outside of the island's dimensions + bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp new file mode 100644 index 000000000..8899e4bd0 --- /dev/null +++ b/src/Generating/FinishGen.cpp @@ -0,0 +1,664 @@ + +// FinishGen.cpp + +/* Implements the various finishing generators: + - cFinishGenSnow + - cFinishGenIce + - cFinishGenSprinkleFoliage +*/ + +#include "Globals.h" + +#include "FinishGen.h" +#include "../Noise.h" +#include "../BlockID.h" +#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway() +#include "../World.h" + + + + + +#define DEF_NETHER_WATER_SPRINGS "0, 1; 255, 1" +#define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0" +#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0" +#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0" +#define DEF_END_WATER_SPRINGS "0, 1; 255, 1" +#define DEF_END_LAVA_SPRINGS "0, 1; 255, 1" + + + + + +static inline bool IsWater(BLOCKTYPE a_BlockType) +{ + return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenSprinkleFoliage: + +bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ) +{ + // We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges: + if ( + (a_RelX < 1) || (a_RelX >= cChunkDef::Width - 1) || + (a_RelY < 1) || (a_RelY >= cChunkDef::Height - 2) || + (a_RelZ < 1) || (a_RelZ >= cChunkDef::Width - 1) + ) + { + return false; + } + + // Only allow dirt, grass or sand below sugarcane: + switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ)) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_SAND: + { + break; + } + default: + { + return false; + } + } + + // Water is required next to the block below the sugarcane: + if ( + !IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) && + !IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) && + !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ - 1)) && + !IsWater(a_ChunkDesc.GetBlockType(a_RelX , a_RelY, a_RelZ + 1)) + ) + { + return false; + } + + // All conditions met, place a sugarcane here: + a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE); + return true; +} + + + + + +void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Generate small foliage (1-block): + + // TODO: Update heightmap with 1-block-tall foliage + for (int z = 0; z < cChunkDef::Width; z++) + { + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z; + const float zz = (float)BlockZ; + for (int x = 0; x < cChunkDef::Width; x++) + { + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x; + if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124) + { + continue; + } + int Top = a_ChunkDesc.GetHeight(x, z); + if (Top > 250) + { + // Nothing grows above Y=250 + continue; + } + if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR) + { + // Space already taken by something else, don't grow here + // WEIRD, since we're using heightmap, so there should NOT be anything above it + continue; + } + + const float xx = (float)BlockX; + float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f ); + float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f ); + switch (a_ChunkDesc.GetBlockType(x, Top, z)) + { + case E_BLOCK_GRASS: + { + float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 ); + float val4 = m_Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 ); + if (val1 + val2 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_YELLOW_FLOWER); + } + else if (val2 + val3 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_ROSE); + } + else if (val3 + val4 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_MUSHROOM); + } + else if (val1 + val4 > 0.2f) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_BROWN_MUSHROOM); + } + else if (val1 + val2 + val3 + val4 < -0.1) + { + a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS); + } + else if (TryAddSugarcane(a_ChunkDesc, x, Top, z)) + { + ++Top; + } + else if ((val1 > 0.5) && (val2 < -0.5)) + { + a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, (int)(val3 * 8) % 4); + } + break; + } // case E_BLOCK_GRASS + + case E_BLOCK_SAND: + { + int y = Top + 1; + if ( + (x > 0) && (x < cChunkDef::Width - 1) && + (z > 0) && (z < cChunkDef::Width - 1) && + (val1 + val2 > 0.5f) && + (a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) && + (a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) && + (a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) && + (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) + ) + { + a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS); + } + else if (TryAddSugarcane(a_ChunkDesc, x, Top, z)) + { + ++Top; + } + break; + } + } // switch (TopBlock) + a_ChunkDesc.SetHeight(x, z, Top); + } // for y + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenSnow: + +void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Add a snow block in snowy biomes onto blocks that can be snowed over + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biIcePlains: + case biIceMountains: + case biTaiga: + case biTaigaHills: + case biFrozenRiver: + case biFrozenOcean: + { + int Height = a_ChunkDesc.GetHeight(x, z); + if (g_BlockIsSnowable[a_ChunkDesc.GetBlockType(x, Height, z)]) + { + a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW); + a_ChunkDesc.SetHeight(x, z, Height + 1); + } + break; + } + } + } + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenIce: + +void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Turn surface water into ice in icy biomes + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biIcePlains: + case biIceMountains: + case biTaiga: + case biTaigaHills: + case biFrozenRiver: + case biFrozenOcean: + { + int Height = a_ChunkDesc.GetHeight(x, z); + switch (a_ChunkDesc.GetBlockType(x, Height, z)) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE); + break; + } + } + break; + } + } + } + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenLilypads: + +int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap) +{ + int res = 0; + for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + if (a_BiomeMap[i] == m_Biome) + { + res++; + } + } // for i - a_BiomeMap[] + return m_Amount * res / 256; +} + + + + + +void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc) +{ + // Add Lilypads on top of water surface in Swampland + + int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap()); + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + for (int i = 0; i < NumToGen; i++) + { + int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width; + int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width; + + // Place the block at {x, z} if possible: + if (a_ChunkDesc.GetBiome(x, z) != m_Biome) + { + // Incorrect biome + continue; + } + int Height = a_ChunkDesc.GetHeight(x, z); + if (Height >= cChunkDef::Height) + { + // Too high up + continue; + } + if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR) + { + // Not an empty block + continue; + } + BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z); + if ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2)) + { + a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType); + a_ChunkDesc.SetHeight(x, z, Height + 1); + } + } // for i +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenBottomLava: + +void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc) +{ + cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes(); + for (int y = m_Level; y > 0; y--) + { + for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) + { + int Index = cChunkDef::MakeIndexNoCheck(x, y, z); + if (BlockTypes[Index] == E_BLOCK_AIR) + { + BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA; + } + } // for x, for z + } // for y +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenPreSimulator: + +cFinishGenPreSimulator::cFinishGenPreSimulator(void) +{ + // Nothing needed yet +} + + + + + +void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc) +{ + CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); + StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); + // TODO: other operations +} + + + + + +void cFinishGenPreSimulator::CollapseSandGravel( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data +) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastY = -1; + int HeightY = 0; + for (int y = 0; y < cChunkDef::Height; y++) + { + BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z); + switch (Block) + { + default: + { + // Set the last block onto which stuff can fall to this height: + LastY = y; + HeightY = y; + break; + } + case E_BLOCK_AIR: + { + // Do nothing + break; + } + case E_BLOCK_FIRE: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + // Do nothing, only remember this height as potentially highest + HeightY = y; + break; + } + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + { + if (LastY < y - 1) + { + cChunkDef::SetBlock(a_BlockTypes, x, LastY + 1, z, Block); + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + } + LastY++; + if (LastY > HeightY) + { + HeightY = LastY; + } + break; + } + } // switch (GetBlock) + } // for y + cChunkDef::SetHeight(a_HeightMap, x, z, HeightY); + } // for x + } // for z +} + + + + + +void cFinishGenPreSimulator::StationarizeFluid( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap, // Height map to read + BLOCKTYPE a_Fluid, + BLOCKTYPE a_StationaryFluid +) +{ + // Turn fluid in the middle to stationary, unless it has air or washable block next to it: + for (int z = 1; z < cChunkDef::Width - 1; z++) + { + for (int x = 1; x < cChunkDef::Width - 1; x++) + { + for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--) + { + BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z); + if ((Block != a_Fluid) && (Block != a_StationaryFluid)) + { + continue; + } + static const struct + { + int x, y, z; + } Coords[] = + { + {1, 0, 0}, + {-1, 0, 0}, + {0, 0, 1}, + {0, 0, -1}, + {0, -1, 0} + } ; + BLOCKTYPE BlockToSet = a_StationaryFluid; // By default, don't simulate this block + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + if ((y == 0) && (Coords[i].y < 0)) + { + continue; + } + BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z); + if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor)) + { + // There is an air / washable neighbor, simulate this block + BlockToSet = a_Fluid; + break; + } + } // for i - Coords[] + cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet); + } // for y + } // for x + } // for z + + // Turn fluid at the chunk edges into non-stationary fluid: + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int i = 0; i < cChunkDef::Width; i++) // i stands for both x and z here + { + if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid); + } + if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid); + } + if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid); + } + if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid) + { + cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid); + } + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenFluidSprings: + +cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) : + m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other + m_HeightDistribution(255), + m_Fluid(a_Fluid) +{ + bool IsWater = (a_Fluid == E_BLOCK_WATER); + AString SectionName = IsWater ? "WaterSprings" : "LavaSprings"; + AString DefaultHeightDistribution; + int DefaultChance; + switch (a_World.GetDimension()) + { + case dimNether: + { + DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS; + DefaultChance = IsWater ? 0 : 15; + break; + } + case dimOverworld: + { + DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS; + DefaultChance = IsWater ? 24 : 9; + break; + } + case dimEnd: + { + DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS; + DefaultChance = 0; + break; + } + default: + { + ASSERT(!"Unhandled world dimension"); + break; + } + } // switch (dimension) + AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution); + if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0)) + { + LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".", + (a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava", + DefaultHeightDistribution.c_str() + ); + m_HeightDistribution.SetDefString(DefaultHeightDistribution); + } + m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance); +} + + + + + +void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc) +{ + int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100; + if (ChanceRnd > m_Chance) + { + // Not in this chunk + return; + } + + // Get the height at which to try: + int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11; + Height %= m_HeightDistribution.GetSum(); + Height = m_HeightDistribution.MapValue(Height); + + // Try adding the spring at the height, if unsuccessful, move lower: + for (int y = Height; y > 1; y--) + { + // TODO: randomize the order in which the coords are being checked + for (int z = 1; z < cChunkDef::Width - 1; z++) + { + for (int x = 1; x < cChunkDef::Width - 1; x++) + { + switch (a_ChunkDesc.GetBlockType(x, y, z)) + { + case E_BLOCK_NETHERRACK: + case E_BLOCK_STONE: + { + if (TryPlaceSpring(a_ChunkDesc, x, y, z)) + { + // Succeeded, bail out + return; + } + } + } // switch (BlockType) + } // for x + } // for y + } // for y +} + + + + + +bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z) +{ + // In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air + // Also, its neighbor on top of it must be non-air + if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR) + { + return false; + } + + static const struct + { + int x, y, z; + } Coords[] = + { + {-1, 0, 0}, + { 1, 0, 0}, + { 0, -1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + int NumAirNeighbors = 0; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z)) + { + case E_BLOCK_AIR: + { + NumAirNeighbors += 1; + if (NumAirNeighbors > 1) + { + return false; + } + } + } + } + if (NumAirNeighbors == 0) + { + return false; + } + + // Has exactly one air neighbor, place a spring: + a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0); + return true; +} + + + + diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h new file mode 100644 index 000000000..ed7df5909 --- /dev/null +++ b/src/Generating/FinishGen.h @@ -0,0 +1,185 @@ + +// FinishGen.h + +/* Interfaces to the various finishing generators: + - cFinishGenSnow + - cFinishGenIce + - cFinishGenSprinkleFoliage + - cFinishGenLilypads + - cFinishGenBottomLava + - cFinishGenPreSimulator + - cFinishGenDeadBushes +*/ + + + + + +#include "ComposableGenerator.h" +#include "../Noise.h" +#include "../ProbabDistrib.h" + + + + + +class cFinishGenSnow : + public cFinishGen +{ +protected: + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenIce : + public cFinishGen +{ +protected: + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenSprinkleFoliage : + public cFinishGen +{ +public: + cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} + +protected: + cNoise m_Noise; + int m_Seed; + + /// Tries to place sugarcane at the coords specified, returns true if successful + bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ); + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +/** This class adds a single top block in random positions in the specified biome on top of specified allowed blocks. +Used for: +- Lilypads finisher +- DeadBushes finisher +*/ +class cFinishGenSingleBiomeSingleTopBlock : + public cFinishGen +{ +public: + cFinishGenSingleBiomeSingleTopBlock( + int a_Seed, BLOCKTYPE a_BlockType, EMCSBiome a_Biome, int a_Amount, + BLOCKTYPE a_AllowedBelow1, BLOCKTYPE a_AllowedBelow2 + ) : + m_Noise(a_Seed), + m_BlockType(a_BlockType), + m_Biome(a_Biome), + m_Amount(a_Amount), + m_AllowedBelow1(a_AllowedBelow1), + m_AllowedBelow2(a_AllowedBelow2) + { + } + +protected: + cNoise m_Noise; + BLOCKTYPE m_BlockType; + EMCSBiome m_Biome; + int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns. + BLOCKTYPE m_AllowedBelow1; ///< First of the two blocktypes that are allowed below m_BlockType + BLOCKTYPE m_AllowedBelow2; ///< Second of the two blocktypes that are allowed below m_BlockType + + int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap); + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenBottomLava : + public cFinishGen +{ +public: + cFinishGenBottomLava(int a_Level) : + m_Level(a_Level) + { + } + +protected: + int m_Level; + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenPreSimulator : + public cFinishGen +{ +public: + cFinishGenPreSimulator(void); + +protected: + // Drops hanging sand and gravel down to the ground, recalculates heightmap + void CollapseSandGravel( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data + ); + + /** For each fluid block: + - if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top) + - all fluid on the chunk's edge is made flowing + */ + void StationarizeFluid( + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::HeightMap & a_HeightMap, // Height map to read + BLOCKTYPE a_Fluid, + BLOCKTYPE a_StationaryFluid + ); + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cFinishGenFluidSprings : + public cFinishGen +{ +public: + cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World); + +protected: + + cNoise m_Noise; + cProbabDistrib m_HeightDistribution; + BLOCKTYPE m_Fluid; + int m_Chance; ///< Chance, [0..100], that a spring will be generated in a chunk + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; + + /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful + bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z); +} ; + + + + diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp new file mode 100644 index 000000000..9cc1fbbaa --- /dev/null +++ b/src/Generating/HeiGen.cpp @@ -0,0 +1,390 @@ + +// HeiGen.cpp + +// Implements the various terrain height generators + +#include "Globals.h" +#include "HeiGen.h" +#include "../LinearUpscale.h" +#include "lib/inifile/iniFile.h" + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenFlat: + +void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) + { + a_HeightMap[i] = m_Height; + } +} + + + + + +void cHeiGenFlat::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", m_Height); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenCache: + +cHeiGenCache::cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize) : + m_HeiGenToCache(a_HeiGenToCache), + m_CacheSize(a_CacheSize), + m_CacheOrder(new int[a_CacheSize]), + m_CacheData(new sCacheData[a_CacheSize]), + m_NumHits(0), + m_NumMisses(0), + m_TotalChain(0) +{ + for (int i = 0; i < m_CacheSize; i++) + { + m_CacheOrder[i] = i; + m_CacheData[i].m_ChunkX = 0x7fffffff; + m_CacheData[i].m_ChunkZ = 0x7fffffff; + } +} + + + + + +cHeiGenCache::~cHeiGenCache() +{ + delete[] m_CacheData; + delete[] m_CacheOrder; +} + + + + + +void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + /* + if (((m_NumHits + m_NumMisses) % 1024) == 10) + { + LOGD("HeiGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses)); + LOGD("HeiGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits); + } + //*/ + + for (int i = 0; i < m_CacheSize; i++) + { + if ( + (m_CacheData[m_CacheOrder[i]].m_ChunkX != a_ChunkX) || + (m_CacheData[m_CacheOrder[i]].m_ChunkZ != a_ChunkZ) + ) + { + continue; + } + // Found it in the cache + int Idx = m_CacheOrder[i]; + + // Move to front: + for (int j = i; j > 0; j--) + { + m_CacheOrder[j] = m_CacheOrder[j - 1]; + } + m_CacheOrder[0] = Idx; + + // Use the cached data: + memcpy(a_HeightMap, m_CacheData[Idx].m_HeightMap, sizeof(a_HeightMap)); + + m_NumHits++; + m_TotalChain += i; + return; + } // for i - cache + + // Not in the cache: + m_NumMisses++; + m_HeiGenToCache.GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap); + + // Insert it as the first item in the MRU order: + int Idx = m_CacheOrder[m_CacheSize - 1]; + for (int i = m_CacheSize - 1; i > 0; i--) + { + m_CacheOrder[i] = m_CacheOrder[i - 1]; + } // for i - m_CacheOrder[] + m_CacheOrder[0] = Idx; + memcpy(m_CacheData[Idx].m_HeightMap, a_HeightMap, sizeof(a_HeightMap)); + m_CacheData[Idx].m_ChunkX = a_ChunkX; + m_CacheData[Idx].m_ChunkZ = a_ChunkZ; +} + + + + + +void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeiGenToCache.InitializeHeightGen(a_IniFile); +} + + + + + +bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height) +{ + for (int i = 0; i < m_CacheSize; i++) + { + if ((m_CacheData[i].m_ChunkX == a_ChunkX) && (m_CacheData[i].m_ChunkZ == a_ChunkZ)) + { + a_Height = cChunkDef::GetHeight(m_CacheData[i].m_HeightMap, a_RelX, a_RelZ); + return true; + } + } // for i - m_CacheData[] + return false; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenClassic: + +cHeiGenClassic::cHeiGenClassic(int a_Seed) : + m_Seed(a_Seed), + m_Noise(a_Seed) +{ +} + + + + + +float cHeiGenClassic::GetNoise(float x, float y) +{ + float oct1 = m_Noise.CubicNoise2D(x * m_HeightFreq1, y * m_HeightFreq1) * m_HeightAmp1; + float oct2 = m_Noise.CubicNoise2D(x * m_HeightFreq2, y * m_HeightFreq2) * m_HeightAmp2; + float oct3 = m_Noise.CubicNoise2D(x * m_HeightFreq3, y * m_HeightFreq3) * m_HeightAmp3; + + float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f ) * 2; + + float flatness = ((m_Noise.CubicNoise2D(x * 0.5f, y * 0.5f) + 1.f) * 0.5f) * 1.1f; // 0 ... 1.5 + flatness *= flatness * flatness; + + return (oct1 + oct2 + oct3) * flatness + height; +} + + + + + +void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkZ * cChunkDef::Width + z); + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkX * cChunkDef::Width + x); + + int hei = 64 + (int)(GetNoise(xx * 0.05f, zz * 0.05f) * 16); + if (hei < 10) + { + hei = 10; + } + if (hei > 250) + { + hei = 250; + } + cChunkDef::SetHeight(a_HeightMap, x , z, hei); + } // for x + } // for z +} + + + + + +void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile) +{ + m_HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); + m_HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); + m_HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); + m_HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); + m_HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); + m_HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHeiGenBiomal: + +const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[biNumBiomes] = +{ + /* Fast-changing | Middle-changing | Slow-changing |*/ + /* Biome | Freq1 | Amp1 | Freq2 | Amp2 | Freq3 | Amp3 | BaseHeight */ + /* biOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, + /* biPlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, + /* biDesert */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, + /* biExtremeHills */ { 0.2f, 4.0f, 0.05f, 20.0f, 0.01f, 16.0f, 100}, + /* biForest */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, + /* biTaiga */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 70}, + /* biSwampland */ { 0.1f, 1.1f, 0.05f, 1.5f, 0.02f, 2.5f, 61.5}, + /* biRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56}, + /* biNether */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing + /* biSky */ { 0.1f, 0.0f, 0.01f, 0.0f, 0.01f, 0.0f, 0}, // Unused, but must be here due to indexing + /* biFrozenOcean */ { 0.1f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 40}, + /* biFrozenRiver */ { 0.2f, 0.1f, 0.05f, 0.1f, 0.01f, 0.1f, 56}, + /* biIcePlains */ { 0.1f, 1.0f, 0.05f, 1.5f, 0.01f, 4.0f, 68}, + /* biIceMountains */ { 0.2f, 2.0f, 0.05f, 10.0f, 0.01f, 8.0f, 80}, + /* biMushroomIsland */ { 0.1f, 2.0f, 0.05f, 8.0f, 0.01f, 6.0f, 80}, + /* biMushroomShore */ { 0.1f, 1.0f, 0.05f, 2.0f, 0.01f, 4.0f, 64}, + /* biBeach */ { 0.1f, 0.5f, 0.05f, 1.0f, 0.01f, 1.0f, 64}, + /* biDesertHills */ { 0.2f, 2.0f, 0.05f, 5.0f, 0.01f, 4.0f, 75}, + /* biForestHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, + /* biTaigaHills */ { 0.2f, 2.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, + /* biExtremeHillsEdge */ { 0.2f, 3.0f, 0.05f, 16.0f, 0.01f, 12.0f, 80}, + /* biJungle */ { 0.1f, 3.0f, 0.05f, 6.0f, 0.01f, 6.0f, 70}, + /* biJungleHills */ { 0.2f, 3.0f, 0.05f, 12.0f, 0.01f, 10.0f, 80}, +} ; + + + + + +void cHeiGenBiomal::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + // Generate a 3x3 chunk area of biomes around this chunk: + BiomeNeighbors Biomes; + for (int z = -1; z <= 1; z++) + { + for (int x = -1; x <= 1; x++) + { + m_BiomeGen.GenBiomes(a_ChunkX + x, a_ChunkZ + z, Biomes[x + 1][z + 1]); + } // for x + } // for z + + /* + _X 2013_04_22: + There's no point in precalculating the entire perlin noise arrays, too many values are calculated uselessly, + resulting in speed DEcrease. + */ + + //* + // Linearly interpolate 4x4 blocks of heightmap: + // Must be done on a floating point datatype, else the results are ugly! + const int STEPZ = 4; // Must be a divisor of 16 + const int STEPX = 4; // Must be a divisor of 16 + NOISE_DATATYPE Height[17 * 17]; + for (int z = 0; z < 17; z += STEPZ) + { + for (int x = 0; x < 17; x += STEPX) + { + Height[x + 17 * z] = GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes); + } + } + LinearUpscale2DArrayInPlace(Height, 17, 17, STEPX, STEPZ); + + // Copy into the heightmap + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, (int)Height[x + 17 * z]); + } + } + //*/ + + /* + // For each height, go through neighboring biomes and add up their idea of height: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, GetHeightAt(x, z, a_ChunkX, a_ChunkZ, Biomes)); + } // for x + } + //*/ +} + + + + + +void cHeiGenBiomal::InitializeHeightGen(cIniFile & a_IniFile) +{ + // No user-settable params +} + + + + + +NOISE_DATATYPE cHeiGenBiomal::GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const cHeiGenBiomal::BiomeNeighbors & a_BiomeNeighbors) +{ + // Sum up how many biomes of each type there are in the neighborhood: + int BiomeCounts[biNumBiomes]; + memset(BiomeCounts, 0, sizeof(BiomeCounts)); + int Sum = 0; + for (int z = -8; z <= 8; z++) + { + int FinalZ = a_RelZ + z + cChunkDef::Width; + int IdxZ = FinalZ / cChunkDef::Width; + int ModZ = FinalZ % cChunkDef::Width; + int WeightZ = 9 - abs(z); + for (int x = -8; x <= 8; x++) + { + int FinalX = a_RelX + x + cChunkDef::Width; + int IdxX = FinalX / cChunkDef::Width; + int ModX = FinalX % cChunkDef::Width; + EMCSBiome Biome = cChunkDef::GetBiome(a_BiomeNeighbors[IdxX][IdxZ], ModX, ModZ); + if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) + { + continue; + } + int WeightX = 9 - abs(x); + BiomeCounts[Biome] += WeightX + WeightZ; + Sum += WeightX + WeightZ; + } // for x + } // for z + + // For each biome type that has a nonzero count, calc its height and add it: + if (Sum > 0) + { + NOISE_DATATYPE Height = 0; + int BlockX = a_ChunkX * cChunkDef::Width + a_RelX; + int BlockZ = a_ChunkZ * cChunkDef::Width + a_RelZ; + for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) + { + if (BiomeCounts[i] == 0) + { + continue; + } + NOISE_DATATYPE oct1 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq1, BlockZ * m_GenParam[i].m_HeightFreq1) * m_GenParam[i].m_HeightAmp1; + NOISE_DATATYPE oct2 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq2, BlockZ * m_GenParam[i].m_HeightFreq2) * m_GenParam[i].m_HeightAmp2; + NOISE_DATATYPE oct3 = m_Noise.CubicNoise2D(BlockX * m_GenParam[i].m_HeightFreq3, BlockZ * m_GenParam[i].m_HeightFreq3) * m_GenParam[i].m_HeightAmp3; + Height += BiomeCounts[i] * (m_GenParam[i].m_BaseHeight + oct1 + oct2 + oct3); + } + NOISE_DATATYPE res = Height / Sum; + return std::min((NOISE_DATATYPE)250, std::max(res, (NOISE_DATATYPE)5)); + } + + // No known biome around? Weird. Return a bogus value: + ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); + return 5; +} + + + + + diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h new file mode 100644 index 000000000..1b246c70a --- /dev/null +++ b/src/Generating/HeiGen.h @@ -0,0 +1,145 @@ + +// HeiGen.h + +/* +Interfaces to the various height generators: + - cHeiGenFlat + - cHeiGenClassic + - cHeiGenBiomal +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cHeiGenFlat : + public cTerrainHeightGen +{ +public: + cHeiGenFlat(void) : m_Height(5) {} + +protected: + + int m_Height; + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; +} ; + + + + + +/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation +class cHeiGenCache : + public cTerrainHeightGen +{ +public: + cHeiGenCache(cTerrainHeightGen & a_HeiGenToCache, int a_CacheSize); // Doesn't take ownership of a_HeiGenToCache + ~cHeiGenCache(); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; + + /// Retrieves height at the specified point in the cache, returns true if found, false if not found + bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height); + +protected: + + cTerrainHeightGen & m_HeiGenToCache; + + struct sCacheData + { + int m_ChunkX; + int m_ChunkZ; + cChunkDef::HeightMap m_HeightMap; + } ; + + // To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data + int m_CacheSize; + int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array + sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used + + // Cache statistics + int m_NumHits; + int m_NumMisses; + int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits) +} ; + + + + + +class cHeiGenClassic : + public cTerrainHeightGen +{ +public: + cHeiGenClassic(int a_Seed); + +protected: + + int m_Seed; + cNoise m_Noise; + float m_HeightFreq1, m_HeightAmp1; + float m_HeightFreq2, m_HeightAmp2; + float m_HeightFreq3, m_HeightAmp3; + + float GetNoise(float x, float y); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; +} ; + + + + + +class cHeiGenBiomal : + public cTerrainHeightGen +{ +public: + cHeiGenBiomal(int a_Seed, cBiomeGen & a_BiomeGen) : + m_Noise(a_Seed), + m_BiomeGen(a_BiomeGen) + { + } + +protected: + + typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; + + cNoise m_Noise; + cBiomeGen & m_BiomeGen; + + // Per-biome terrain generator parameters: + struct sGenParam + { + float m_HeightFreq1, m_HeightAmp1; + float m_HeightFreq2, m_HeightAmp2; + float m_HeightFreq3, m_HeightAmp3; + float m_BaseHeight; + } ; + static const sGenParam m_GenParam[biNumBiomes]; + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; + + NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors); +} ; + + + + diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp new file mode 100644 index 000000000..f42240e55 --- /dev/null +++ b/src/Generating/MineShafts.cpp @@ -0,0 +1,1424 @@ + +// MineShafts.cpp + +// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts + +/* +Algorithm: +The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft +classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made. +The cMineShaft class is a base class for each mineshaft structure. +Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to +other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure +is facing. + +The generation starts with the central dirt room, from there corridors, crossings and staircases are added +in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level +*/ + +#include "Globals.h" +#include "MineShafts.h" +#include "../Cuboid.h" +#include "../BlockEntities/ChestEntity.h" + + + + + +static const int NEIGHBORHOOD_SIZE = 3; + + + + + +class cMineShaft abstract +{ +public: + enum eKind + { + mskDirtRoom, + mskCorridor, + mskCrossing, + mskStaircase, + } ; + + + enum eDirection + { + dirXP, + dirZP, + dirXM, + dirZM, + } ; + + + cStructGenMineShafts::cMineShaftSystem & m_ParentSystem; + eKind m_Kind; + cCuboid m_BoundingBox; + + + cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) : + m_ParentSystem(a_ParentSystem), + m_Kind(a_Kind) + { + } + + cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) : + m_ParentSystem(a_ParentSystem), + m_Kind(a_Kind), + m_BoundingBox(a_BoundingBox) + { + } + + /// Returns true if this mineshaft intersects the specified cuboid + bool DoesIntersect(const cCuboid & a_Other) + { + return m_BoundingBox.DoesIntersect(a_Other); + } + + /** If recursion level is not too large, appends more branches to the parent system, + using exit points specific to this class. + */ + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0; + + /// Imprints this shape into the specified chunk's data + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::vector<cMineShaft *> cMineShafts; + + + + + +class cMineShaftDirtRoom : + public cMineShaft +{ + typedef cMineShaft super; + +public: + cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cMineShaftCorridor : + public cMineShaft +{ + typedef cMineShaft super; + +public: + /** Creates a new Corridor attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + static const int MAX_SEGMENTS = 5; + + int m_NumSegments; + eDirection m_Direction; + bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block) + int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction + int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction + bool m_HasTracks; ///< If true, random tracks will be placed on the floor + + cMineShaftCorridor( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, + cNoise & a_Noise + ); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; + + /// Places a chest, if the corridor has one + void PlaceChest(cChunkDesc & a_ChunkDesc); + + /// If this corridor has tracks, places them randomly + void PlaceTracks(cChunkDesc & a_ChunkDesc); + + /// If this corridor has a spawner, places the spawner + void PlaceSpawner(cChunkDesc & a_ChunkDesc); + + /// Randomly places torches around the central beam block + void PlaceTorches(cChunkDesc & a_ChunkDesc); +} ; + + + + + +class cMineShaftCrossing : + public cMineShaft +{ + typedef cMineShaft super; + +public: + /** Creates a new Crossing attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cMineShaftStaircase : + public cMineShaft +{ + typedef cMineShaft super; + +public: + enum eSlope + { + sUp, + sDown, + } ; + + /** Creates a new Staircase attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + eDirection m_Direction; + eSlope m_Slope; + + + cMineShaftStaircase( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, + eDirection a_Direction, + eSlope a_Slope + ); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenMineShafts::cMineShaftSystem +{ +public: + int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated + int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction + int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion) + int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor + int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor + int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing + int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it + int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it + int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam + cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system + cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit + + /// Creates and generates the entire system + cMineShaftSystem( + int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, + int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase + ); + + ~cMineShaftSystem(); + + /// Carves the system into the chunk data + void ProcessChunk(cChunkDesc & a_Chunk); + + /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction, + if it fits, appends it to the list and calls its AppendBranches() + */ + void AppendBranch( + int a_BlockX, int a_BlockY, int a_BlockZ, + cMineShaft::eDirection a_Direction, cNoise & a_Noise, + int a_RecursionLevel + ); + + /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid + bool CanAppend(const cCuboid & a_BoundingBox); +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMineShafts::cMineShaftSystem: + +cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem( + int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, + int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase +) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_GridSize(a_GridSize), + m_MaxRecursion(8), // TODO: settable + m_ProbLevelCorridor(a_ProbLevelCorridor), + m_ProbLevelCrossing(a_ProbLevelCrossing), + m_ProbLevelStaircase(a_ProbLevelStaircase + 1), + m_ChanceChest(12), // TODO: settable + m_ChanceSpawner(12), // TODO: settable + m_ChanceTorch(1000) // TODO: settable +{ + m_MineShafts.reserve(100); + + cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise); + m_MineShafts.push_back(Start); + + m_BoundingBox.Assign( + Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2, + Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2 + ); + + Start->AppendBranches(0, a_Noise); + + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + ASSERT((*itr)->m_BoundingBox.IsSorted()); + } // for itr - m_MineShafts[] +} + + + + + +cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem() +{ + for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_MineShafts[] + m_MineShafts.clear(); +} + + + + + +void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk) +{ + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(a_Chunk); + } // for itr - m_MineShafts[] +} + + + + + +void cStructGenMineShafts::cMineShaftSystem::AppendBranch( + int a_PivotX, int a_PivotY, int a_PivotZ, + cMineShaft::eDirection a_Direction, cNoise & a_Noise, + int a_RecursionLevel +) +{ + if (a_RecursionLevel > m_MaxRecursion) + { + return; + } + + cMineShaft * Next = NULL; + int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase; + if (rnd < m_ProbLevelCorridor) + { + Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + else if (rnd < m_ProbLevelCrossing) + { + Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + else + { + Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + if (Next == NULL) + { + return; + } + m_MineShafts.push_back(Next); + Next->AppendBranches(a_RecursionLevel + 1, a_Noise); +} + + + + + +bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox) +{ + if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox)) + { + // Too far away, or too low / too high + return false; + } + + // Check intersections: + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + if ((*itr)->DoesIntersect(a_BoundingBox)) + { + return false; + } + } // for itr - m_MineShafts[] + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftDirtRoom: + +cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) : + super(a_Parent, mskDirtRoom) +{ + // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18: + int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7; + int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; + rnd >>= 12; + int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; + rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11; + m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX; + m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8); + rnd >>= 4; + m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ; + m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8); + rnd >>= 4; + m_BoundingBox.p1.y = 20; + m_BoundingBox.p2.y = 24 + rnd % 8; +} + + + + + +void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int Height = m_BoundingBox.DifY() - 3; + for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4) + { + int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7; + m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + rnd >>= 4; + m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + + for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13; + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel); + rnd >>= 4; + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel); + } +} + + + + + +void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if ( + (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) || + (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) || + (m_BoundingBox.p2.x < BlockX) || + (m_BoundingBox.p2.z < BlockZ) + ) + { + // Early bailout - cannot intersect this chunk + return; + } + + // Chunk-relative coords of the boundaries: + int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX; + int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX; + int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ; + int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ; + + // Carve the room out: + for (int z = MinZ; z < MaxZ; z++) + { + for (int x = MinX; x < MaxX; x++) + { + for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT); + } + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftCorridor: + +cMineShaftCorridor::cMineShaftCorridor( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, + cNoise & a_Noise +) : + super(a_ParentSystem, mskCorridor, a_BoundingBox), + m_NumSegments(a_NumSegments), + m_Direction(a_Direction), + m_ChestPosition(-1), + m_SpawnerPosition(-1) +{ + int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7; + for (int i = 0; i < a_NumSegments; i++) + { + m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam + rnd >>= 2; + } + m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks + + rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7; + int ChestCheck = rnd % 250; + rnd >>= 8; + int SpawnerCheck = rnd % 250; + rnd >>= 8; + if (ChestCheck < a_ParentSystem.m_ChanceChest) + { + m_ChestPosition = rnd % (a_NumSegments * 5); + } + if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner)) + { + m_SpawnerPosition = rnd % (a_NumSegments * 5); + } +} + + + + + +cMineShaft * cMineShaftCorridor::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); + BoundingBox.p2.y += 3; + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS + switch (a_Direction) + { + case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; + case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; + case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; + case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; + } + if (!a_ParentSystem.CanAppend(BoundingBox)) + { + return NULL; + } + return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise); +} + + + + + +void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7; + // Prefer the same height, but allow for up to one block height displacement: + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + switch (m_Direction) + { + case dirXM: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirXP: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirZM: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirZP: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); + } + break; + } + } // switch (m_Direction) +} + + + + + +void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid RelBoundingBox(m_BoundingBox); + RelBoundingBox.Move(-BlockX, 0, -BlockZ); + RelBoundingBox.p1.y += 1; + RelBoundingBox.p2.y -= 1; + cCuboid Top(RelBoundingBox); + Top.p2.y += 1; + Top.p1.y = Top.p2.y; + a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0); + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, (BlockX ^ (BlockZ + BlockX)), 8000); + if (m_SpawnerPosition >= 0) + { + // Cobwebs around the spider spawner + a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, (BlockX ^ (BlockZ + BlockZ)), 8000); + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, (BlockX ^ (BlockZ + BlockX)), 5000); + } + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, (BlockX ^ (BlockZ + BlockX + 10)), 500); + RelBoundingBox.p1.y = m_BoundingBox.p1.y; + RelBoundingBox.p2.y = m_BoundingBox.p1.y; + a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0); + switch (m_Direction) + { + case dirXM: + case dirXP: + { + int y1 = m_BoundingBox.p1.y + 1; + int y2 = m_BoundingBox.p1.y + 2; + int y3 = m_BoundingBox.p1.y + 3; + int z1 = m_BoundingBox.p1.z - BlockZ; + int z2 = m_BoundingBox.p2.z - BlockZ; + for (int i = 0; i < m_NumSegments; i++) + { + int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX; + if ((x < 0) || (x >= cChunkDef::Width)) + { + continue; + } + if ((z1 >= 0) && (z1 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0); + } + if ((z2 >= 0) && (z2 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0); + } + if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) + { + a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0); + } + } // for i - NumSegments + break; + } + + case dirZM: + case dirZP: + { + int y1 = m_BoundingBox.p1.y + 1; + int y2 = m_BoundingBox.p1.y + 2; + int y3 = m_BoundingBox.p1.y + 3; + int x1 = m_BoundingBox.p1.x - BlockX; + int x2 = m_BoundingBox.p2.x - BlockX; + for (int i = 0; i < m_NumSegments; i++) + { + int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ; + if ((z < 0) || (z >= cChunkDef::Width)) + { + continue; + } + if ((x1 >= 0) && (x1 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0); + } + if ((x2 >= 0) && (x2 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0); + } + if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) + { + a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0); + } + } // for i - NumSegments + break; + } // case dirZ? + } // for i + + PlaceChest(a_ChunkDesc); + PlaceTracks(a_ChunkDesc); + PlaceSpawner(a_ChunkDesc); // (must be after Tracks!) + PlaceTorches(a_ChunkDesc); +} + + + + + +void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc) +{ + static const cLootProbab LootProbab[] = + { + // Item, MinAmount, MaxAmount, Weight + { cItem(E_ITEM_IRON), 1, 5, 10 }, + { cItem(E_ITEM_GOLD), 1, 3, 5 }, + { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 }, + { cItem(E_ITEM_DIAMOND), 1, 2, 3 }, + { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye + { cItem(E_ITEM_COAL), 3, 8, 10 }, + { cItem(E_ITEM_BREAD), 1, 3, 15 }, + { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 }, + { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 }, + { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 }, + { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 }, + } ; + + if (m_ChestPosition < 0) + { + return; + } + + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + int x, z; + NIBBLETYPE Meta = 0; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + x = m_BoundingBox.p1.x + m_ChestPosition - BlockX; + z = m_BoundingBox.p1.z - BlockZ; + Meta = E_META_CHEST_FACING_ZP; + break; + } + + case dirZM: + case dirZP: + default: + { + x = m_BoundingBox.p1.x - BlockX; + z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ; + Meta = E_META_CHEST_FACING_XP; + break; + } + } // switch (Dir) + + if ( + (x >= 0) && (x < cChunkDef::Width) && + (z >= 0) && (z < cChunkDef::Width) + ) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta); + cChestEntity * ChestEntity = (cChestEntity *)a_ChunkDesc.GetBlockEntity(x, m_BoundingBox.p1.y + 1, z); + ASSERT((ChestEntity != NULL) && (ChestEntity->GetBlockType() == E_BLOCK_CHEST)); + cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ()); + int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4); + int Seed = Noise.IntNoise2DInt(x, z); + ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed); + } +} + + + + + +void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc) +{ + if (!m_HasTracks) + { + return; + } + cCuboid Box(m_BoundingBox); + Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width); + Box.p2.y = Box.p1.y; + Box.p1.x += 1; + Box.p2.x -= 1; + Box.p1.z += 1; + Box.p2.z -= 1; + NIBBLETYPE Meta = 0; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + Meta = E_META_TRACKS_X; + break; + } + + case dirZM: + case dirZP: + { + Meta = E_META_TRACKS_Z; + break; + } + } // switch (direction) + a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000); +} + + + + + +void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc) +{ + if (m_SpawnerPosition < 0) + { + // No spawner in this corridor + return; + } + int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + SpawnerRelX += m_SpawnerPosition - 1; + break; + } + case dirZM: + case dirZP: + { + SpawnerRelZ += m_SpawnerPosition - 1; + break; + } + } + if ( + (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) && + (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width) + ) + { + a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0); + // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented + } +} + + + + + +void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc) +{ + cNoise Noise(m_BoundingBox.p1.x); + switch (m_Direction) + { + case dirXM: + case dirXP: + { + int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if ((z < 0) || (z >= cChunkDef::Width)) + { + return; + } + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + for (int i = 0; i < m_NumSegments; i++) + { + if (!m_HasFullBeam[i]) + { + continue; + } + int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX; + if ((x >= 0) && (x < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP); + } + } + x += 2; + if ((x >= 0) && (x < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM); + } + } + } // for i + break; + } + + case dirZM: + case dirZP: + { + int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; + if ((x < 0) || (x >= cChunkDef::Width)) + { + return; + } + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + for (int i = 0; i < m_NumSegments; i++) + { + if (!m_HasFullBeam[i]) + { + continue; + } + int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ; + if ((z >= 0) && (z < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP); + } + } + z += 2; + if ((z >= 0) && (z < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM); + } + } + } // for i + break; + } + } // switch (direction) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftCrossing: + +cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) : + super(a_ParentSystem, mskCrossing, a_BoundingBox) +{ +} + + + + + +cMineShaft * cMineShaftCrossing::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + BoundingBox.p2.y += 3; + if ((rnd % 4) < 2) + { + // 2-level crossing: + BoundingBox.p2.y += 4; + rnd >>= 2; + if ((rnd % 4) < 2) + { + // This is the higher level: + BoundingBox.p1.y -= 4; + BoundingBox.p2.y -= 4; + } + } + rnd >>= 2; + switch (a_Direction) + { + case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; + case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; + case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; + case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; + } + if (!a_ParentSystem.CanAppend(BoundingBox)) + { + return NULL; + } + return new cMineShaftCrossing(a_ParentSystem, BoundingBox); +} + + + + + +void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + struct + { + int x, y, z; + eDirection dir; + } Exits[] = + { + // Bottom level: + {-1, 1, 2, dirXM}, + { 2, 1, -1, dirZM}, + { 5, 1, 2, dirXP}, + { 2, 1, 5, dirZP}, + // Top level: + {-1, 5, 2, dirXM}, + { 2, 5, -1, dirZM}, + { 5, 5, 2, dirXP}, + { 2, 5, 5, dirZP}, + } ; + for (unsigned int i = 0; i < ARRAYCOUNT(Exits); i++) + { + if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y) + { + // This exit is not available (two-level exit on a one-level crossing) + continue; + } + + int Height = m_BoundingBox.p1.y + Exits[i].y; + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel); + } // for i +} + + + + + +void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid box(m_BoundingBox); + box.Move(-BlockX, 0, -BlockZ); + if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width)) + { + // Does not intersect this chunk + return; + } + int Floor = box.p1.y + 1; + int Ceil = box.p2.y; + + // The supports: + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); + + // The air in between: + a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0); + + // The air on the edges: + int Mid = Floor + 2; + a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); + Mid += 2; + if (Mid < Ceil) + { + a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); + } + + // The floor, if needed: + box.p2.y = box.p1.y; + a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftStaircase: + +cMineShaftStaircase::cMineShaftStaircase( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, + eDirection a_Direction, + eSlope a_Slope +) : + super(a_ParentSystem, mskStaircase, a_BoundingBox), + m_Direction(a_Direction), + m_Slope(a_Slope) +{ +} + + + + + +cMineShaft * cMineShaftStaircase::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + cCuboid Box; + switch (a_Direction) + { + case dirXM: + { + Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1); + break; + } + case dirXP: + { + Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1); + break; + } + case dirZM: + { + Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ); + break; + } + case dirZP: + { + Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7); + break; + } + } + eSlope Slope = sUp; + if ((rnd % 4) < 2) // 50 % + { + Slope = sDown; + Box.Move(0, -4, 0); + } + if (!a_ParentSystem.CanAppend(Box)) + { + return NULL; + } + return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope); +} + + + + + +void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5); + switch (m_Direction) + { + case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break; + case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break; + case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break; + case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break; + } +} + + + + + +void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid RelB(m_BoundingBox); + RelB.Move(-BlockX, 0, -BlockZ); + if ( + (RelB.p1.x >= cChunkDef::Width) || + (RelB.p1.z >= cChunkDef::Width) || + (RelB.p2.x < 0) || + (RelB.p2.z < 0) + ) + { + // No intersection between this staircase and this chunk + return; + } + + int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1); + int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5); + int Add = (m_Slope == sDown) ? -1 : 1; + int InitAdd = (m_Slope == sDown) ? -1 : 0; + cCuboid Box; + switch (m_Direction) + { + case dirXM: + { + a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(-1, Add, 0); + } + break; + } + + case dirXP: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(1, Add, 0); + } + break; + } + + case dirZM: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(0, Add, -1); + } + break; + } + + case dirZP: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(0, Add, 1); + } + break; + } + + } // switch (m_Direction) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMineShafts: + +cStructGenMineShafts::cStructGenMineShafts( + int a_Seed, int a_GridSize, int a_MaxSystemSize, + int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase +) : + m_Noise(a_Seed), + m_GridSize(a_GridSize), + m_MaxSystemSize(a_MaxSystemSize), + m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)), + m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)), + m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase)) +{ +} + + + + + +cStructGenMineShafts::~cStructGenMineShafts() +{ + ClearCache(); +} + + + + + +void cStructGenMineShafts::ClearCache(void) +{ + for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenMineShafts::GetMineShaftSystemsForChunk( + int a_ChunkX, int a_ChunkZ, + cStructGenMineShafts::cMineShaftSystems & a_MineShafts +) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= NEIGHBORHOOD_SIZE / 2; + BaseZ -= NEIGHBORHOOD_SIZE / 2; + + // Walk the cache, move each cave system that we want into a_Caves: + int StartX = BaseX * m_GridSize; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize; + int StartZ = BaseZ * m_GridSize; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize; + for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_MineShafts.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_GridSize; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_GridSize; + bool Found = false; + for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } // for itr - a_Mineshafts + if (!Found) + { + a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase)); + } + } // for z + } // for x + + // Copy a_MineShafts into m_Cache to the beginning: + cMineShaftSystems MineShaftsCopy(a_MineShafts); + m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cMineShaftSystems::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } +} + + + + + + +void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cMineShaftSystems MineShafts; + GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts); + for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr) + { + (*itr)->ProcessChunk(a_ChunkDesc); + } // for itr - MineShafts[] +} + + + + diff --git a/src/Generating/MineShafts.h b/src/Generating/MineShafts.h new file mode 100644 index 000000000..c53d3bc53 --- /dev/null +++ b/src/Generating/MineShafts.h @@ -0,0 +1,61 @@ + +// MineShafts.h + +// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenMineShafts : + public cStructureGen +{ +public: + cStructGenMineShafts( + int a_Seed, int a_GridSize, int a_MaxSystemSize, + int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase + ); + + virtual ~cStructGenMineShafts(); + +protected: + friend class cMineShaft; + friend class cMineShaftDirtRoom; + friend class cMineShaftCorridor; + friend class cMineShaftCrossing; + friend class cMineShaftStaircase; + class cMineShaftSystem; // fwd: MineShafts.cpp + typedef std::list<cMineShaftSystem *> cMineShaftSystems; + + cNoise m_Noise; + int m_GridSize; ///< Average spacing of the systems + int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system + int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor + int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor + int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing + cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used. + + /// Clears everything from the cache + void ClearCache(void); + + /** Returns all systems that *may* intersect the given chunk. + All the systems are valid until the next call to this function (which may delete some of the pointers). + */ + void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems); + + // cStructureGen overrides: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp new file mode 100644 index 000000000..9760ca4ca --- /dev/null +++ b/src/Generating/Noise3DGenerator.cpp @@ -0,0 +1,576 @@ + +// Nosie3DGenerator.cpp + +// Generates terrain using 3D noise, rather than composing. Is a test. + +#include "Globals.h" +#include "Noise3DGenerator.h" +#include "../OSSupport/File.h" +#include "lib/inifile/iniFile.h" +#include "../LinearInterpolation.h" +#include "../LinearUpscale.h" + + + + + +/* +// Perform an automatic test of upscaling upon program start (use breakpoints to debug): + +class Test +{ +public: + Test(void) + { + DoTest1(); + DoTest2(); + } + + + void DoTest1(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug3DNoise(In, 3, 3, 3, "Upscale3D in"); + float Out[17 * 33 * 35]; + LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17); + Debug3DNoise(Out, 17, 33, 35, "Upscale3D test"); + } + + + void DoTest2(void) + { + float In[3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug2DNoise(In, 3, 3, "Upscale2D in"); + float Out[17 * 33]; + LinearUpscale2DArray(In, 3, 3, Out, 8, 16); + Debug2DNoise(Out, 17, 33, "Upscale2D test"); + } + +} gTest; +//*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DGenerator: + +cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : + super(a_ChunkGenerator), + m_Perlin(1000), + m_Cubic(1000) +{ + m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5); + m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1); + m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2); + + #if 0 + // DEBUG: Test the noise generation: + // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size + // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M + m_SeaLevel = 62; + m_HeightAmplification = 0; + m_MidPoint = 75; + m_FrequencyX = 4; + m_FrequencyY = 4; + m_FrequencyZ = 4; + m_AirThreshold = 0.5; + + const int NumChunks = 4; + NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height]; + for (int x = 0; x < NumChunks; x++) + { + GenerateNoiseArray(x, 5, Noise[x]); + } + + // Save in XY cuts: + cFile f1; + if (f1.Open("Test_XY.grab", cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f1.Write(buf, cChunkDef::Width); + } + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open("Test_XZ.grab", cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f2.Write(buf, cChunkDef::Width); + } + } // for z + } // for y + } // if (XZ file open) + #endif // 0 +} + + + + + +cNoise3DGenerator::~cNoise3DGenerator() +{ + // Nothing needed yet +} + + + + + +void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + m_World = a_World; + + // Params: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); + m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); + m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8); + m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); +} + + + + + +void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (unsigned int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + a_BiomeMap[i] = biExtremeHills; + } +} + + + + + +void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) +{ + NOISE_DATATYPE Noise[17 * 257 * 17]; + GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); + + // Output noise into chunk: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + int idx = z * 17 * 257 + y * 17; + for (int x = 0; x < cChunkDef::Width; x++) + { + NOISE_DATATYPE n = Noise[idx++]; + BLOCKTYPE BlockType; + if (n > m_AirThreshold) + { + BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; + } + else + { + BlockType = E_BLOCK_STONE; + } + a_ChunkDesc.SetBlockType(x, y, z, BlockType); + } + } + } + + UpdateHeightmap(a_ChunkDesc); + ComposeTerrain (a_ChunkDesc); +} + + + + + +void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) +{ + NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise + NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash + + // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed" + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; + + m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); + + // Precalculate a "height" array: + NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source") + m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); + for (unsigned int i = 0; i < ARRAYCOUNT(Height); i++) + { + Height[i] = abs(Height[i]) * m_HeightAmplification + 1; + } + + // Modify the noise by height data: + for (int y = 0; y < DIM_Y; y++) + { + NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; + for (int z = 0; z < DIM_Z; z++) + { + NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]); + for (int x = 0; x < DIM_X; x++) + { + CurRow[x] += AddHeight / Height[x + DIM_X * z]; + } + } + } + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ)); + + // Upscale the Perlin noise into full-blown chunk dimensions: + LinearUpscale3DArray( + NoiseO, DIM_X, DIM_Y, DIM_Z, + a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z + ); + + // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); +} + + + + + +void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = cChunkDef::Height - 1; y > 0; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetHeight(x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 1; y > 0; y--) + { + switch (a_ChunkDesc.GetBlockType(x, y, z)) + { + case E_BLOCK_AIR: + { + LastAir = y; + break; + } + case E_BLOCK_STONE: + { + if (LastAir - y > 3) + { + break; + } + if (HasHadWater) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + } + break; + } + case E_BLOCK_STATIONARY_WATER: + { + LastAir = y; + HasHadWater = true; + break; + } + } // switch (GetBlockType()) + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DComposable: + +cNoise3DComposable::cNoise3DComposable(int a_Seed) : + m_Noise1(a_Seed + 1000), + m_Noise2(a_Seed + 2000), + m_Noise3(a_Seed + 3000) +{ +} + + + + + +void cNoise3DComposable::Initialize(cIniFile & a_IniFile) +{ + // Params: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); + m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); + m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10); + m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); +} + + + + + +void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) +{ + if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ)) + { + // The noise for this chunk is already generated in m_Noise + return; + } + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + // Upscaling parameters: + const int UPSCALE_X = 8; + const int UPSCALE_Y = 4; + const int UPSCALE_Z = 8; + + // Precalculate a "height" array: + NOISE_DATATYPE Height[17 * 17]; // x + 17 * z + for (int z = 0; z < 17; z += UPSCALE_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += UPSCALE_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1; + Height[x + 17 * z] = val * val * val; + } + } + + for (int y = 0; y < 257; y += UPSCALE_Y) + { + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; + NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; + NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); + for (int z = 0; z < 17; z += UPSCALE_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += UPSCALE_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + CurFloor[x + 17 * z] = + m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 + + m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) + + m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 + + AddHeight / Height[x + 17 * z]; + } + } + // Linear-interpolate this XZ floor: + LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); + } + + // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis + for (int y = 1; y < cChunkDef::Height; y++) + { + if ((y % UPSCALE_Y) == 0) + { + // This is the interpolation source floor, already calculated + continue; + } + int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y; + int HiFloorY = LoFloorY + UPSCALE_Y; + NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]); + NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]); + NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); + NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y; + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio; + idx += 1; + } + idx += 1; // Skipping one X column + } + } + + // The noise array is now fully interpolated + /* + // DEBUG: Output two images of the array, sliced by XY and XZ: + cFile f1; + if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + int idx = y * 17 * 17 + z * 17; + unsigned char buf[16]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); + } + f1.Write(buf, 16); + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + int idx = y * 17 * 17 + z * 17; + unsigned char buf[16]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); + } + f2.Write(buf, 16); + } // for z + } // for y + } // if (XZ file open) + */ +} + + + + + +void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir; y < m_SeaLevel; y++) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + } + for (int y = LastAir - 1; y > 0; y--) + { + if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold) + { + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; + } + // "ground" part: + if (LastAir - y > 4) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); + continue; + } + if (HasHadWater) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + } + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h new file mode 100644 index 000000000..0d211cddc --- /dev/null +++ b/src/Generating/Noise3DGenerator.h @@ -0,0 +1,106 @@ + +// Noise3DGenerator.h + +// Generates terrain using 3D noise, rather than composing. Is a test. + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cNoise3DGenerator : + public cChunkGenerator::cGenerator +{ + typedef cChunkGenerator::cGenerator super; + +public: + cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cNoise3DGenerator(); + + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; + +protected: + // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int UPSCALE_X = 8; + static const int UPSCALE_Y = 4; + static const int UPSCALE_Z = 8; + + // Linear interpolation buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; + static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; + static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; + + cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition + cCubicNoise m_Cubic; // The noise used for heightmap directing + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size + void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise); + + /// Updates heightmap based on the chunk's contents + void UpdateHeightmap(cChunkDesc & a_ChunkDesc); + + /// Composes terrain - adds dirt, grass and sand + void ComposeTerrain(cChunkDesc & a_ChunkDesc); +} ; + + + + + +class cNoise3DComposable : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cNoise3DComposable(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + int m_LastChunkX; + int m_LastChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y + + + /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given + void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/Ravines.cpp b/src/Generating/Ravines.cpp new file mode 100644 index 000000000..6413b963b --- /dev/null +++ b/src/Generating/Ravines.cpp @@ -0,0 +1,531 @@ + +// Ravines.cpp + +// Implements the cStructGenRavines class representing the ravine structure generator + +#include "Globals.h" +#include "Ravines.h" + + + + +/// How many ravines in each direction are generated for a given chunk. Must be an even number +static const int NEIGHBORHOOD_SIZE = 8; + +static const int NUM_RAVINE_POINTS = 4; + + + + + +struct cRavDefPoint +{ + int m_BlockX; + int m_BlockZ; + int m_Radius; + int m_Top; + int m_Bottom; + + cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_Radius(a_Radius), + m_Top (a_Top), + m_Bottom(a_Bottom) + { + } +} ; + +typedef std::vector<cRavDefPoint> cRavDefPoints; + + + + + +class cStructGenRavines::cRavine +{ + cRavDefPoints m_Points; + + /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise + void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); + + /// Refines (adds and smooths) defpoints from a_Src into a_Dst + void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst); + + /// Does one round of smoothing, two passes of RefineDefPoints() + void Smooth(void); + + /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block + void FinishLinear(void); + +public: + // Coords for which the ravine was generated (not necessarily the center) + int m_BlockX; + int m_BlockZ; + + cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); + + /// Carves the ravine into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + /// Exports itself as a SVG line definition + AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const; + #endif // _DEBUG +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenRavines: + +cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) : + m_Noise(a_Seed), + m_Size(a_Size) +{ +} + + + + + +cStructGenRavines::~cStructGenRavines() +{ + ClearCache(); +} + + + + + +void cStructGenRavines::ClearCache(void) +{ + for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cRavines Ravines; + GetRavinesForChunk(ChunkX, ChunkZ, Ravines); + for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + } // for itr - Ravines[] +} + + + + + +void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_Size; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= 4; + BaseZ -= 4; + + // Walk the cache, move each ravine that we want into a_Ravines: + int StartX = BaseX * m_Size; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size; + int StartZ = BaseZ * m_Size; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size; + for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_Ravines.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_Size; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_Size; + bool Found = false; + for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } + if (!Found) + { + a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise)); + } + } + } + + // Copy a_Ravines into m_Cache to the beginning: + cRavines RavinesCopy(a_Ravines); + m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cRavines::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } + + /* + #ifdef _DEBUG + // DEBUG: Export as SVG into a file specific for the chunk, for visual verification: + AString SVG; + SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n"); + for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) + { + SVG.append((*itr)->ExportAsSVG(0, 512, 512)); + } + SVG.append("</svg>\n"); + + AString fnam; + Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); + cFile File(fnam, cFile::fmWrite); + File.Write(SVG.c_str(), SVG.size()); + #endif // _DEBUG + //*/ +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenRavines::cRavine + +cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ) +{ + // Calculate the ravine shape-defining points: + GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise); + + // Smooth the ravine. A two passes are needed: + Smooth(); + Smooth(); + + // Linearly interpolate the neighbors so that they're close enough together: + FinishLinear(); +} + + + + + +void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) +{ + // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): + a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; + + // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction + int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2; + int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2; + int CenterX = a_BlockX + OffsetX; + int CenterZ = a_BlockZ + OffsetZ; + + // Get the base angle in which the ravine "axis" goes: + float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653); + float xc = sin(Angle); + float zc = cos(Angle); + + // Calculate the definition points and radii: + int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16)); + int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32); + int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32); + int Mid = (Top + Bottom) / 2; + int PointX = CenterX - (int)(xc * a_Size / 2); + int PointZ = CenterZ - (int)(zc * a_Size / 2); + m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2)); + for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++) + { + int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); + int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); + // Amplitude is the amount of blocks that this point is away from the ravine "axis" + int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size; + Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4] + int PointX = LineX + (int)(zc * Amplitude); + int PointZ = LineZ - (int)(xc * Amplitude); + int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function + int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4; + int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4; + m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom)); + } // for i - m_Points[] + PointX = CenterX + (int)(xc * a_Size / 2); + PointZ = CenterZ + (int)(zc * a_Size / 2); + m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid)); +} + + + + + +void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst) +{ + // Smoothing: for each line segment, add points on its 1/4 lengths + int Num = a_Src.size() - 2; // this many intermediary points + a_Dst.clear(); + a_Dst.reserve(Num * 2 + 2); + cRavDefPoints::const_iterator itr = a_Src.begin() + 1; + a_Dst.push_back(a_Src.front()); + int PrevX = a_Src.front().m_BlockX; + int PrevZ = a_Src.front().m_BlockZ; + int PrevR = a_Src.front().m_Radius; + int PrevT = a_Src.front().m_Top; + int PrevB = a_Src.front().m_Bottom; + for (int i = 0; i <= Num; ++i, ++itr) + { + int dx = itr->m_BlockX - PrevX; + int dz = itr->m_BlockZ - PrevZ; + if (abs(dx) + abs(dz) < 4) + { + // Too short a segment to smooth-subdivide into quarters + continue; + } + int dr = itr->m_Radius - PrevR; + int dt = itr->m_Top - PrevT; + int db = itr->m_Bottom - PrevB; + int Rad1 = std::max(PrevR + 1 * dr / 4, 1); + int Rad2 = std::max(PrevR + 3 * dr / 4, 1); + a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4)); + a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4)); + PrevX = itr->m_BlockX; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + PrevT = itr->m_Top; + PrevB = itr->m_Bottom; + } + a_Dst.push_back(a_Src.back()); +} + + + + + +void cStructGenRavines::cRavine::Smooth(void) +{ + cRavDefPoints Pts; + RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts + RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points +} + + + + + +void cStructGenRavines::cRavine::FinishLinear(void) +{ + // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints + // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point) + // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points + + cRavDefPoints Pts; + std::swap(Pts, m_Points); + + m_Points.reserve(Pts.size() * 3); + int PrevX = Pts.front().m_BlockX; + int PrevZ = Pts.front().m_BlockZ; + for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) + { + int x1 = itr->m_BlockX; + int z1 = itr->m_BlockZ; + int dx = abs(x1 - PrevX); + int dz = abs(z1 - PrevZ); + int sx = (PrevX < x1) ? 1 : -1; + int sz = (PrevZ < z1) ? 1 : -1; + int err = dx - dz; + int R = itr->m_Radius; + int T = itr->m_Top; + int B = itr->m_Bottom; + while (true) + { + m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B)); + if ((PrevX == x1) && (PrevZ == z1)) + { + break; + } + int e2 = 2 * err; + if (e2 > -dz) + { + err -= dz; + PrevX += sx; + } + if (e2 < dx) + { + err += dx; + PrevZ += sz; + } + } // while (true) + } // for itr +} + + + + + +#ifdef _DEBUG +AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color); + char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L" + for (cRavDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr) + { + AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ); + Prefix = 'L'; + } + SVG.append("\"/>\n"); + + // Base point highlight: + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n", + a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ + ); + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 + ); + + // A gray line from the base point to the first point of the ravine, for identification: + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ + ); + + // Offset guides: + if (a_OffsetX > 0) + { + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n", + a_OffsetX, a_OffsetX + ); + } + if (a_OffsetZ > 0) + { + AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n", + a_OffsetZ, a_OffsetZ + ); + } + return SVG; +} +#endif // _DEBUG + + + + + +void cStructGenRavines::cRavine::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + int BlockStartX = a_ChunkX * cChunkDef::Width; + int BlockStartZ = a_ChunkZ * cChunkDef::Width; + int BlockEndX = BlockStartX + cChunkDef::Width; + int BlockEndZ = BlockStartZ + cChunkDef::Width; + for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + if ( + (itr->m_BlockX + itr->m_Radius < BlockStartX) || + (itr->m_BlockX - itr->m_Radius > BlockEndX) || + (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || + (itr->m_BlockZ - itr->m_Radius > BlockEndZ) + ) + { + // Cannot intersect, bail out early + continue; + } + + // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top: + int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius + int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc + int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + #ifdef _DEBUG + // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on) + if ((DifX + x == 0) && (DifZ + z == 0)) + { + cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE); + } + #endif // _DEBUG + + int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z); + if (DistSq <= RadiusSq) + { + int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast + for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + // Only carve out these specific block types + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_GRAVEL: + case E_BLOCK_SAND: + case E_BLOCK_SANDSTONE: + case E_BLOCK_NETHERRACK: + case E_BLOCK_COAL_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + break; + } + default: break; + } + } + } + } // for x, z - a_BlockTypes + } // for itr - m_Points[] +} + + + + diff --git a/src/Generating/Ravines.h b/src/Generating/Ravines.h new file mode 100644 index 000000000..05164a5b2 --- /dev/null +++ b/src/Generating/Ravines.h @@ -0,0 +1,46 @@ + +// Ravines.h + +// Interfaces to the cStructGenRavines class representing the ravine structure generator + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenRavines : + public cStructureGen +{ +public: + cStructGenRavines(int a_Seed, int a_Size); + ~cStructGenRavines(); + +protected: + class cRavine; // fwd: Ravines.cpp + typedef std::list<cRavine *> cRavines; + + cNoise m_Noise; + int m_Size; // Max size, in blocks, of the ravines generated + cRavines m_Cache; + + /// Clears everything from the cache + void ClearCache(void); + + /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function. + void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines); + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp new file mode 100644 index 000000000..2180261aa --- /dev/null +++ b/src/Generating/StructGen.cpp @@ -0,0 +1,675 @@ + +// StructGen.h + +#include "Globals.h" +#include "StructGen.h" +#include "../BlockID.h" +#include "Trees.h" +#include "../BlockArea.h" +#include "../LinearUpscale.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenOreNests configuration: + +const int MAX_HEIGHT_COAL = 127; +const int NUM_NESTS_COAL = 50; +const int NEST_SIZE_COAL = 10; + +const int MAX_HEIGHT_IRON = 64; +const int NUM_NESTS_IRON = 14; +const int NEST_SIZE_IRON = 6; + +const int MAX_HEIGHT_REDSTONE = 16; +const int NUM_NESTS_REDSTONE = 4; +const int NEST_SIZE_REDSTONE = 6; + +const int MAX_HEIGHT_GOLD = 32; +const int NUM_NESTS_GOLD = 2; +const int NEST_SIZE_GOLD = 6; + +const int MAX_HEIGHT_DIAMOND = 15; +const int NUM_NESTS_DIAMOND = 1; +const int NEST_SIZE_DIAMOND = 4; + +const int MAX_HEIGHT_LAPIS = 30; +const int NUM_NESTS_LAPIS = 2; +const int NEST_SIZE_LAPIS = 5; + +const int MAX_HEIGHT_DIRT = 127; +const int NUM_NESTS_DIRT = 20; +const int NEST_SIZE_DIRT = 32; + +const int MAX_HEIGHT_GRAVEL = 70; +const int NUM_NESTS_GRAVEL = 15; +const int NEST_SIZE_GRAVEL = 32; + + + + + +template <typename T> T Clamp(T a_Value, T a_Min, T a_Max) +{ + return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); +} + + + + + +static bool SortTreeBlocks(const sSetBlock & a_First, const sSetBlock & a_Second) +{ + return (a_First.BlockType == E_BLOCK_LOG) && (a_Second.BlockType != E_BLOCK_LOG); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenTrees: + +void cStructGenTrees::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + cChunkDesc WorkerDesc(ChunkX, ChunkZ); + + // Generate trees: + for (int x = 0; x <= 2; x++) + { + int BaseX = ChunkX + x - 1; + for (int z = 0; z <= 2; z++) + { + int BaseZ = ChunkZ + z - 1; + + cChunkDesc * Dest; + + if ((x != 1) || (z != 1)) + { + Dest = &WorkerDesc; + WorkerDesc.SetChunkCoords(BaseX, BaseZ); + + m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap()); + m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap()); + m_CompositionGen->ComposeTerrain(WorkerDesc); + // TODO: Free the entity lists + } + else + { + Dest = &a_ChunkDesc; + } + + int NumTrees = GetNumTrees(BaseX, BaseZ, Dest->GetBiomeMap()); + + sSetBlockVector OutsideLogs, OutsideOther; + for (int i = 0; i < NumTrees; i++) + { + GenerateSingleTree(BaseX, BaseZ, i, *Dest, OutsideLogs, OutsideOther); + } + + sSetBlockVector IgnoredOverflow; + IgnoredOverflow.reserve(OutsideOther.size()); + ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideOther, IgnoredOverflow); + IgnoredOverflow.clear(); + IgnoredOverflow.reserve(OutsideLogs.size()); + ApplyTreeImage(ChunkX, ChunkZ, a_ChunkDesc, OutsideLogs, IgnoredOverflow); + } // for z + } // for x + + // Update the heightmap: + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = cChunkDef::Height - 1; y >= 0; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetHeight(x, z, y); + break; + } + } // for y + } // for z + } // for x +} + + + + + +void cStructGenTrees::GenerateSingleTree( + int a_ChunkX, int a_ChunkZ, int a_Seq, + cChunkDesc & a_ChunkDesc, + sSetBlockVector & a_OutsideLogs, + sSetBlockVector & a_OutsideOther +) +{ + int x = (m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) / 19) % cChunkDef::Width; + int z = (m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) / 19) % cChunkDef::Width; + + int Height = a_ChunkDesc.GetHeight(x, z); + + if ((Height <= 0) || (Height > 240)) + { + return; + } + + // Check the block underneath the tree: + BLOCKTYPE TopBlock = a_ChunkDesc.GetBlockType(x, Height, z); + if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_FARMLAND)) + { + return; + } + + sSetBlockVector TreeLogs, TreeOther; + GetTreeImageByBiome( + a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z, + m_Noise, a_Seq, + a_ChunkDesc.GetBiome(x, z), + TreeLogs, TreeOther + ); + + // Check if the generated image fits the terrain. Only the logs are checked: + for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr) + { + if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ)) + { + // Outside the chunk + continue; + } + + BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z); + switch (Block) + { + CASE_TREE_ALLOWED_BLOCKS: + { + break; + } + default: + { + // There's something in the way, abort this tree altogether + return; + } + } + } + + ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeOther, a_OutsideOther); + ApplyTreeImage(a_ChunkX, a_ChunkZ, a_ChunkDesc, TreeLogs, a_OutsideLogs); +} + + + + + +void cStructGenTrees::ApplyTreeImage( + int a_ChunkX, int a_ChunkZ, + cChunkDesc & a_ChunkDesc, + const sSetBlockVector & a_Image, + sSetBlockVector & a_Overflow +) +{ + // Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks + for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr) + { + if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ)) + { + // Inside this chunk, integrate into a_ChunkDesc: + switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z)) + { + case E_BLOCK_LEAVES: + { + if (itr->BlockType != E_BLOCK_LOG) + { + break; + } + // fallthrough: + } + CASE_TREE_OVERWRITTEN_BLOCKS: + { + a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + break; + } + + } // switch (GetBlock()) + continue; + } + + // Outside the chunk, push into a_Overflow. + // Don't check if already present there, by separating logs and others we don't need the checks anymore: + a_Overflow.push_back(*itr); + } +} + + + + + +int cStructGenTrees::GetNumTrees( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BiomeMap & a_Biomes +) +{ + int NumTrees = 0; + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + int Add = 0; + switch (cChunkDef::GetBiome(a_Biomes, x, z)) + { + case biPlains: Add = 1; break; + case biExtremeHills: Add = 3; break; + case biForest: Add = 30; break; + case biTaiga: Add = 30; break; + case biSwampland: Add = 8; break; + case biIcePlains: Add = 1; break; + case biIceMountains: Add = 1; break; + case biMushroomIsland: Add = 3; break; + case biMushroomShore: Add = 3; break; + case biForestHills: Add = 20; break; + case biTaigaHills: Add = 20; break; + case biExtremeHillsEdge: Add = 5; break; + case biJungle: Add = 120; break; + case biJungleHills: Add = 90; break; + } + NumTrees += Add; + } + return NumTrees / 1024; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenOreNests: + +void cStructGenOreNests::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes(); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_COAL_ORE, MAX_HEIGHT_COAL, NUM_NESTS_COAL, NEST_SIZE_COAL, BlockTypes, 1); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_IRON_ORE, MAX_HEIGHT_IRON, NUM_NESTS_IRON, NEST_SIZE_IRON, BlockTypes, 2); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, BlockTypes, 3); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_GOLD_ORE, MAX_HEIGHT_GOLD, NUM_NESTS_GOLD, NEST_SIZE_GOLD, BlockTypes, 4); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIAMOND_ORE, MAX_HEIGHT_DIAMOND, NUM_NESTS_DIAMOND, NEST_SIZE_DIAMOND, BlockTypes, 5); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_LAPIS_ORE, MAX_HEIGHT_LAPIS, NUM_NESTS_LAPIS, NEST_SIZE_LAPIS, BlockTypes, 6); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_DIRT, MAX_HEIGHT_DIRT, NUM_NESTS_DIRT, NEST_SIZE_DIRT, BlockTypes, 10); + GenerateOre(ChunkX, ChunkZ, E_BLOCK_GRAVEL, MAX_HEIGHT_GRAVEL, NUM_NESTS_GRAVEL, NEST_SIZE_GRAVEL, BlockTypes, 11); +} + + + + + +void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq) +{ + // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other. + // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes + // Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified). + + for (int i = 0; i < a_NumNests; i++) + { + int rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8; + int BaseX = rnd % cChunkDef::Width; + rnd /= cChunkDef::Width; + int BaseZ = rnd % cChunkDef::Width; + rnd /= cChunkDef::Width; + int BaseY = rnd % a_MaxHeight; + rnd /= a_MaxHeight; + int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger + int Num = 0; + while (Num < NestSize) + { + // Put a cuboid around [BaseX, BaseY, BaseZ] + int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8; + int xsize = rnd % 2; + int ysize = (rnd / 4) % 2; + int zsize = (rnd / 16) % 2; + rnd >>= 8; + for (int x = xsize; x >= 0; --x) + { + int BlockX = BaseX + x; + if ((BlockX < 0) || (BlockX >= cChunkDef::Width)) + { + Num++; // So that the cycle finishes even if the base coords wander away from the chunk + continue; + } + for (int y = ysize; y >= 0; --y) + { + int BlockY = BaseY + y; + if ((BlockY < 0) || (BlockY >= cChunkDef::Height)) + { + Num++; // So that the cycle finishes even if the base coords wander away from the chunk + continue; + } + for (int z = zsize; z >= 0; --z) + { + int BlockZ = BaseZ + z; + if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width)) + { + Num++; // So that the cycle finishes even if the base coords wander away from the chunk + continue; + } + + int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ); + if (a_BlockTypes[Index] == E_BLOCK_STONE) + { + a_BlockTypes[Index] = a_OreType; + } + Num++; + } // for z + } // for y + } // for x + + // Move the base to a neighbor voxel + switch (rnd % 4) + { + case 0: BaseX--; break; + case 1: BaseX++; break; + } + switch ((rnd >> 3) % 4) + { + case 0: BaseY--; break; + case 1: BaseY++; break; + } + switch ((rnd >> 6) % 4) + { + case 0: BaseZ--; break; + case 1: BaseZ++; break; + } + } // while (Num < NumBlocks) + } // for i - NumNests +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenLakes: + +void cStructGenLakes::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + + for (int z = -1; z < 2; z++) for (int x = -1; x < 2; x++) + { + if (((m_Noise.IntNoise2DInt(ChunkX + x, ChunkZ + z) / 17) % 100) > m_Probability) + { + continue; + } + + cBlockArea Lake; + CreateLakeImage(ChunkX + x, ChunkZ + z, Lake); + + int OfsX = Lake.GetOriginX() + x * cChunkDef::Width; + int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width; + + // Merge the lake into the current data + a_ChunkDesc.WriteBlockArea(Lake, OfsX, Lake.GetOriginY(), OfsZ, cBlockArea::msLake); + } // for x, z - neighbor chunks +} + + + + + +void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake) +{ + a_Lake.Create(16, 8, 16); + a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy + + // Find the minimum height in this chunk: + cChunkDef::HeightMap HeightMap; + m_HeiGen.GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap); + HEIGHTTYPE MinHeight = HeightMap[0]; + for (int i = 1; i < ARRAYCOUNT(HeightMap); i++) + { + if (HeightMap[i] < MinHeight) + { + MinHeight = HeightMap[i]; + } + } + + // Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6 + MinHeight = std::max(MinHeight - 6, 2); + int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11; + // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range + int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8; + Rnd >>= 12; + // Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range + int OffsetZ = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8; + Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 512, a_ChunkZ) / 13; + // Random height [1 .. MinHeight] with preference to center heights + int HeightY = 1 + (((Rnd & 0x1ff) % MinHeight) + (((Rnd >> 9) & 0x1ff) % MinHeight)) / 2; + + a_Lake.SetOrigin(OffsetX, HeightY, OffsetZ); + + // Hollow out a few bubbles inside the blockarea: + int NumBubbles = 4 + ((Rnd >> 18) & 0x03); // 4 .. 7 bubbles + BLOCKTYPE * BlockTypes = a_Lake.GetBlockTypes(); + for (int i = 0; i < NumBubbles; i++) + { + int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, i, a_ChunkZ) / 13; + const int BubbleR = 2 + (Rnd & 0x03); // 2 .. 5 + const int Range = 16 - 2 * BubbleR; + const int BubbleX = BubbleR + (Rnd % Range); + Rnd >>= 4; + const int BubbleY = 4 + (Rnd & 0x01); // 4 .. 5 + Rnd >>= 1; + const int BubbleZ = BubbleR + (Rnd % Range); + Rnd >>= 4; + const int HalfR = BubbleR / 2; // 1 .. 2 + const int RSquared = BubbleR * BubbleR; + for (int y = -HalfR; y <= HalfR; y++) + { + // BubbleY + y is in the [0, 7] bounds + int DistY = 4 * y * y / 3; + int IdxY = (BubbleY + y) * 16 * 16; + for (int z = -BubbleR; z <= BubbleR; z++) + { + int DistYZ = DistY + z * z; + if (DistYZ >= RSquared) + { + continue; + } + int IdxYZ = BubbleX + IdxY + (BubbleZ + z) * 16; + for (int x = -BubbleR; x <= BubbleR; x++) + { + if (x * x + DistYZ < RSquared) + { + BlockTypes[x + IdxYZ] = E_BLOCK_AIR; + } + } // for x + } // for z + } // for y + } // for i - bubbles + + // Turn air in the bottom half into liquid: + for (int y = 0; y < 4; y++) + { + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + if (BlockTypes[x + z * 16 + y * 16 * 16] == E_BLOCK_AIR) + { + BlockTypes[x + z * 16 + y * 16 * 16] = m_Fluid; + } + } // for z, x + } // for y + + // TODO: Turn sponge next to lava into stone + + // a_Lake.SaveToSchematicFile(Printf("Lake_%d_%d.schematic", a_ChunkX, a_ChunkZ)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDirectOverhangs: + +cStructGenDirectOverhangs::cStructGenDirectOverhangs(int a_Seed) : + m_Noise1(a_Seed), + m_Noise2(a_Seed + 1000) +{ +} + + + + + +void cStructGenDirectOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) +{ + // If there is no column of the wanted biome, bail out: + if (!HasWantedBiome(a_ChunkDesc)) + { + return; + } + + HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); + + const int SEGMENT_HEIGHT = 8; + const int INTERPOL_X = 16; // Must be a divisor of 16 + const int INTERPOL_Z = 16; // Must be a divisor of 16 + // Interpolate the chunk in 16 * SEGMENT_HEIGHT * 16 "segments", each SEGMENT_HEIGHT blocks high and each linearly interpolated separately. + // Have two buffers, one for the lowest floor and one for the highest floor, so that Y-interpolation can be done between them + // Then swap the buffers and use the previously-top one as the current-bottom, without recalculating it. + + int FloorBuf1[17 * 17]; + int FloorBuf2[17 * 17]; + int * FloorHi = FloorBuf1; + int * FloorLo = FloorBuf2; + int BaseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BaseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + int BaseY = 63; + + // Interpolate the lowest floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, BaseY, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorLo, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate segments: + for (int Segment = BaseY; Segment < MaxHeight; Segment += SEGMENT_HEIGHT) + { + // First update the high floor: + for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++) + { + FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] = + m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) * + m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / + 256; + } // for x, z - FloorLo[] + LinearUpscale2DArrayInPlace(FloorHi, 17, 17, INTERPOL_X, INTERPOL_Z); + + // Interpolate between FloorLo and FloorHi: + for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biExtremeHills: + case biExtremeHillsEdge: + { + int Lo = FloorLo[x + 17 * z] / 256; + int Hi = FloorHi[x + 17 * z] / 256; + for (int y = 0; y < SEGMENT_HEIGHT; y++) + { + int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; + if (Val < 0) + { + a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR); + } + } // for y + break; + } + } // switch (biome) + } // for z, x + + // Swap the floors: + std::swap(FloorLo, FloorHi); + } +} + + + + + +bool cStructGenDirectOverhangs::HasWantedBiome(cChunkDesc & a_ChunkDesc) const +{ + cChunkDef::BiomeMap & Biomes = a_ChunkDesc.GetBiomeMap(); + for (int i = 0; i < ARRAYCOUNT(Biomes); i++) + { + switch (Biomes[i]) + { + case biExtremeHills: + case biExtremeHillsEdge: + { + return true; + } + } + } // for i + return false; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDistortedMembraneOverhangs: + +cStructGenDistortedMembraneOverhangs::cStructGenDistortedMembraneOverhangs(int a_Seed) : + m_NoiseX(a_Seed + 1000), + m_NoiseY(a_Seed + 2000), + m_NoiseZ(a_Seed + 3000), + m_NoiseH(a_Seed + 4000) +{ +} + + + + + +void cStructGenDistortedMembraneOverhangs::GenStructures(cChunkDesc & a_ChunkDesc) +{ + const NOISE_DATATYPE Frequency = (NOISE_DATATYPE)16; + const NOISE_DATATYPE Amount = (NOISE_DATATYPE)1; + for (int y = 50; y < 128; y++) + { + NOISE_DATATYPE NoiseY = (NOISE_DATATYPE)y / 32; + // TODO: proper water level - where to get? + BLOCKTYPE ReplacementBlock = (y > 62) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; + for (int z = 0; z < cChunkDef::Width; z++) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z)) / Frequency; + for (int x = 0; x < cChunkDef::Width; x++) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x)) / Frequency; + NOISE_DATATYPE DistortX = m_NoiseX.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; + NOISE_DATATYPE DistortY = m_NoiseY.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; + NOISE_DATATYPE DistortZ = m_NoiseZ.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * Amount; + int MembraneHeight = 96 - (int)((DistortY + m_NoiseH.CubicNoise2D(NoiseX + DistortX, NoiseZ + DistortZ)) * 30); + if (MembraneHeight < y) + { + a_ChunkDesc.SetBlockType(x, y, z, ReplacementBlock); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/src/Generating/StructGen.h b/src/Generating/StructGen.h new file mode 100644 index 000000000..853748bb8 --- /dev/null +++ b/src/Generating/StructGen.h @@ -0,0 +1,165 @@ + +// StructGen.h + +/* Interfaces to the various structure generators: + - cStructGenTrees + - cStructGenMarbleCaves + - cStructGenOres +*/ + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenTrees : + public cStructureGen +{ +public: + cStructGenTrees(int a_Seed, cBiomeGen * a_BiomeGen, cTerrainHeightGen * a_HeightGen, cTerrainCompositionGen * a_CompositionGen) : + m_Seed(a_Seed), + m_Noise(a_Seed), + m_BiomeGen(a_BiomeGen), + m_HeightGen(a_HeightGen), + m_CompositionGen(a_CompositionGen) + {} + +protected: + + int m_Seed; + cNoise m_Noise; + cBiomeGen * m_BiomeGen; + cTerrainHeightGen * m_HeightGen; + cTerrainCompositionGen * m_CompositionGen; + + /** Generates and applies an image of a single tree. + Parts of the tree inside the chunk are applied to a_BlockX. + Parts of the tree outside the chunk are stored in a_OutsideX + */ + void GenerateSingleTree( + int a_ChunkX, int a_ChunkZ, int a_Seq, + cChunkDesc & a_ChunkDesc, + sSetBlockVector & a_OutsideLogs, + sSetBlockVector & a_OutsideOther + ) ; + + /// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow + void ApplyTreeImage( + int a_ChunkX, int a_ChunkZ, + cChunkDesc & a_ChunkDesc, + const sSetBlockVector & a_Image, + sSetBlockVector & a_Overflow + ); + + int GetNumTrees( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BiomeMap & a_Biomes + ); + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenOreNests : + public cStructureGen +{ +public: + cStructGenOreNests(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} + +protected: + cNoise m_Noise; + int m_Seed; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; + + void GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq); +} ; + + + + + +class cStructGenLakes : + public cStructureGen +{ +public: + cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGen & a_HeiGen, int a_Probability) : + m_Noise(a_Seed), + m_Seed(a_Seed), + m_Fluid(a_Fluid), + m_HeiGen(a_HeiGen), + m_Probability(a_Probability) + { + } + +protected: + cNoise m_Noise; + int m_Seed; + BLOCKTYPE m_Fluid; + cTerrainHeightGen & m_HeiGen; + int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; + + /// Creates a lake image for the specified chunk into a_Lake + void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake); +} ; + + + + + + +class cStructGenDirectOverhangs : + public cStructureGen +{ +public: + cStructGenDirectOverhangs(int a_Seed); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; + + bool HasWantedBiome(cChunkDesc & a_ChunkDesc) const; +} ; + + + + + +class cStructGenDistortedMembraneOverhangs : + public cStructureGen +{ +public: + cStructGenDistortedMembraneOverhangs(int a_Seed); + +protected: + cNoise m_NoiseX; + cNoise m_NoiseY; + cNoise m_NoiseZ; + cNoise m_NoiseH; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp new file mode 100644 index 000000000..7ca30c60f --- /dev/null +++ b/src/Generating/Trees.cpp @@ -0,0 +1,684 @@ + +// Trees.cpp + +// Implements helper functions used for generating trees + +#include "Globals.h" +#include "Trees.h" +#include "../BlockID.h" + + + + +// DEBUG: +int gTotalLargeJungleTrees = 0; +int gOversizeLargeJungleTrees = 0; + + + + + +typedef struct +{ + int x, z; +} sCoords; + +typedef struct +{ + int x, z; + NIBBLETYPE Meta; +} sMetaCoords; + +static const sCoords Corners[] = +{ + {-1, -1}, + {-1, 1}, + {1, -1}, + {1, 1}, +} ; + +// BigO = a big ring of blocks, used for generating horz slices of treetops, the number indicates the radius + +static const sCoords BigO1[] = +{ + {0, -1}, + {-1, 0}, {1, 0}, + {0, 1}, +} ; + +static const sCoords BigO2[] = +{ + {-1, -2}, {0, -2}, {1, -2}, + {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, + {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, + {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, + {-1, 2}, {0, 2}, {1, 2}, +} ; + +static const sCoords BigO3[] = +{ + {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, + {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, + {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, + {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, + {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, + {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, + {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, +} ; + +static const sCoords BigO4[] = // Part of Big Jungle tree +{ + {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4}, + {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3}, + {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2}, + {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1}, + {-4, 0}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, + {-4, 1}, {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, + {-4, 2}, {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, + {-3, 3}, {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, + {-2, 4}, {-1, 4}, {0, 4}, {1, 4}, {2, 4}, +} ; + + + + + +typedef struct +{ + const sCoords * Coords; + size_t Count; +} sCoordsArr; + +static const sCoordsArr BigOs[] = +{ + {BigO1, ARRAYCOUNT(BigO1)}, + {BigO2, ARRAYCOUNT(BigO2)}, + {BigO3, ARRAYCOUNT(BigO3)}, + {BigO4, ARRAYCOUNT(BigO4)}, +} ; + + + + + +/// Pushes a specified layer of blocks of the same type around (x, h, z) into a_Blocks +inline void PushCoordBlocks(int a_BlockX, int a_Height, int a_BlockZ, sSetBlockVector & a_Blocks, const sCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +{ + for (size_t i = 0; i < a_NumCoords; i++) + { + a_Blocks.push_back(sSetBlock(a_BlockX + a_Coords[i].x, a_Height, a_BlockZ + a_Coords[i].z, a_BlockType, a_Meta)); + } +} + + + + +inline void PushCornerBlocks(int a_BlockX, int a_Height, int a_BlockZ, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, int a_CornersDist, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +{ + for (size_t i = 0; i < ARRAYCOUNT(Corners); i++) + { + int x = a_BlockX + Corners[i].x; + int z = a_BlockZ + Corners[i].z; + if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height, z + 64 * a_Seq) <= a_Chance) + { + a_Blocks.push_back(sSetBlock(x, a_Height, z, a_BlockType, a_Meta)); + } + } // for i - Corners[] +} + + + + + +inline void PushSomeColumns(int a_BlockX, int a_Height, int a_BlockZ, int a_ColumnHeight, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, const sMetaCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType) +{ + for (size_t i = 0; i < a_NumCoords; i++) + { + int x = a_BlockX + a_Coords[i].x; + int z = a_BlockZ + a_Coords[i].z; + if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height + i, z + 64 * a_Seq) <= a_Chance) + { + for (int j = 0; j < a_ColumnHeight; j++) + { + a_Blocks.push_back(sSetBlock(x, a_Height - j, z, a_BlockType, a_Coords[i].Meta)); + } + } + } // for i - a_Coords[] +} + + + + + +void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + switch (a_Biome) + { + case biPlains: + case biExtremeHills: + case biExtremeHillsEdge: + case biForest: + case biMushroomIsland: + case biMushroomShore: + case biForestHills: + { + // Apple or birch trees: + if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff) + { + GetAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + break; + } + + case biTaiga: + case biIcePlains: + case biIceMountains: + case biTaigaHills: + { + // Conifers + GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + break; + } + + case biSwampland: + { + // Swamp trees: + GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + break; + } + + case biJungle: + case biJungleHills: + { + // Apple bushes, large jungle trees, small jungle trees + if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x6fffffff) + { + GetAppleBushImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + } + } +} + + + + + +void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000) + { + GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetLargeAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } +} + + + + + +void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + /* Small apple tree has: + - a top plus (no log) + - optional BigO1 + random corners (log) + - 2 layers of BigO2 + random corners (log) + - 1 to 3 blocks of trunk + */ + + int Random = a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) >> 3; + + int Heights[] = {1, 2, 2, 3} ; + int Height = 1 + Heights[Random & 3]; + Random >>= 2; + + // Pre-alloc so that we don't realloc too often later: + a_LogBlocks.reserve(Height + 5); + a_OtherBlocks.reserve(ARRAYCOUNT(BigO2) * 2 + ARRAYCOUNT(BigO1) + ARRAYCOUNT(Corners) * 3 + 3 + 5); + + // Trunk: + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + } + int Hei = a_BlockY + Height; + + // 2 BigO2 + corners layers: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x5000000 - i * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + Hei++; + } // for i - 2* + + // Optional BigO1 + corners layer: + if ((Random & 1) == 0) + { + PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x6000000, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + Hei++; + } + + // Top plus: + PushCoordBlocks(a_BlockX, Hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); +} + + + + + +void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // TODO +} + + + + + +void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3); + + // Prealloc, so that we don't realloc too often later: + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(80); + + // The entire trunk, out of logs: + for (int i = Height - 1; i >= 0; --i) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH)); + } + int h = a_BlockY + Height; + + // Top layer - just the Plus: + PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer + h--; + + // Second layer - log, Plus and maybe Corners: + PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + h--; + + // Third and fourth layers - BigO2 and maybe 2*Corners: + for (int Row = 0; Row < 2; Row++) + { + PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH); + h--; + } // for Row - 2* +} + + + + + +void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Half chance for a spruce, half for a pine: + if (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) < 0x40000000) + { + GetSpruceTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetPineTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } +} + + + + + +void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Spruces have a top section with layer sizes of (0, 1, 0) or only (1, 0), + // then 1 - 3 sections of ascending sizes (1, 2) [most often], (1, 3) or (1, 2, 3) + // and an optional bottom section of size 1, followed by 1 - 3 clear trunk blocks + + // We'll use bits from this number as partial random numbers; but the noise function has mod8 irregularities + // (each of the mod8 remainders has a very different chance of occurrence) - that's why we divide by 8 + int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) / 8; + + static const int sHeights[] = {1, 2, 2, 3}; + int Height = sHeights[MyRandom & 3]; + MyRandom >>= 2; + + // Prealloc, so that we don't realloc too often later: + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(180); + + // Clear trunk blocks: + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + } + Height += a_BlockY; + + // Optional size-1 bottom leaves layer: + if ((MyRandom & 1) == 0) + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height++; + } + MyRandom >>= 1; + + // 1 to 3 sections of leaves layers: + static const int sNumSections[] = {1, 2, 2, 3}; + int NumSections = sNumSections[MyRandom & 3]; + MyRandom >>= 2; + for (int i = 0; i < NumSections; i++) + { + switch (MyRandom & 3) // SectionType; (1, 2) twice as often as the other two + { + case 0: + case 1: + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height += 2; + break; + } + case 2: + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height += 2; + break; + } + case 3: + { + PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + PushCoordBlocks(a_BlockX, Height + 2, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + Height += 3; + break; + } + } // switch (SectionType) + MyRandom >>= 2; + } // for i - Sections + + if ((MyRandom & 1) == 0) + { + // (0, 1, 0) top: + a_LogBlocks.push_back (sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + } + else + { + // (1, 0) top: + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + } +} + + + + + +void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Tall, little leaves on top. The top leaves are arranged in a shape of two cones joined by their bases. + // There can be one or two layers representing the cone bases (SameSizeMax) + + int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8; + int TrunkHeight = 8 + (MyRandom % 3); + int SameSizeMax = ((MyRandom & 8) == 0) ? 1 : 0; + MyRandom >>= 3; + int NumLeavesLayers = 2 + (MyRandom % 3); // Number of layers that have leaves in them + if (NumLeavesLayers == 2) + { + SameSizeMax = 0; + } + + // Pre-allocate the vector: + a_LogBlocks.reserve(TrunkHeight); + a_OtherBlocks.reserve(NumLeavesLayers * 25); + + // The entire trunk, out of logs: + for (int i = TrunkHeight; i >= 0; --i) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER)); + } + int h = a_BlockY + TrunkHeight + 2; + + // Top layer - just a single leaves block: + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + h--; + + // One more layer is above the trunk, push the central leaves: + a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER)); + + // Layers expanding in size, then collapsing again: + // LOGD("Generating %d layers of pine leaves, SameSizeMax = %d", NumLeavesLayers, SameSizeMax); + for (int i = 0; i < NumLeavesLayers; ++i) + { + int LayerSize = std::min(i, NumLeavesLayers - i + SameSizeMax - 1); + // LOGD("LayerSize %d: %d", i, LayerSize); + if (LayerSize < 0) + { + break; + } + ASSERT(LayerSize < ARRAYCOUNT(BigOs)); + PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); + h--; + } +} + + + + + +void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Vines are around the BigO3, but not in the corners; need proper meta for direction + static const sMetaCoords Vines[] = + { + {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face + {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face + {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face + {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, {-4, 2, 8}, // West face + } ; + + int Height = 3 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8) % 3; + + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO2) + 2 * ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 20); + + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE)); + } + int hei = a_BlockY + Height - 2; + + // Put vines around the lowermost leaves layer: + PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); + + // The lower two leaves layers are BigO3 with log in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + hei++; + } // for i - 2* + + // The upper two leaves layers are BigO2 with leaves in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + hei++; + } // for i - 2* +} + + + + + +void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + a_OtherBlocks.reserve(3 + ARRAYCOUNT(BigO2) + ARRAYCOUNT(BigO1)); + + int hei = a_BlockY; + a_LogBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + hei++; + + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + hei++; + + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); +} + + + + + +void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000) + { + GetSmallJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + else + { + GetLargeJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } +} + + + + + +void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // TODO: Generate proper jungle trees with branches + + // Vines are around the BigO4, but not in the corners; need proper meta for direction + static const sMetaCoords Vines[] = + { + {-2, -5, 1}, {-1, -5, 1}, {0, -5, 1}, {1, -5, 1}, {2, -5, 1}, // North face + {-2, 5, 4}, {-1, 5, 4}, {0, 5, 4}, {1, 5, 4}, {2, 5, 4}, // South face + {5, -2, 2}, {5, -1, 2}, {5, 0, 2}, {5, 1, 2}, {5, 2, 2}, // East face + {-5, -2, 8}, {-5, -1, 8}, {-5, 0, 8}, {-5, 1, 8}, {-5, 2, 8}, // West face + // TODO: vines around the trunk, proper metas and height + } ; + + int Height = 24 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 11) % 24; + + a_LogBlocks.reserve(Height * 4); + a_OtherBlocks.reserve(2 * ARRAYCOUNT(BigO4) + ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 50); + + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + a_LogBlocks.push_back(sSetBlock(a_BlockX + 1, a_BlockY + i, a_BlockZ + 1, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + } + int hei = a_BlockY + Height - 2; + + // Put vines around the lowermost leaves layer: + PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); + + // The lower two leaves layers are BigO4 with log in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO4, ARRAYCOUNT(BigO4), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + hei++; + } // for i - 2* + + // The top leaves layer is a BigO3 with leaves in the middle and possibly corners: + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE)); +} + + + + + +void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks) +{ + // Vines are around the BigO3, but not in the corners; need proper meta for direction + static const sMetaCoords Vines[] = + { + {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face + {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face + {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face + {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, // West face + // TODO: proper metas and height: {0, 1, 1}, {0, -1, 4}, {-1, 0, 2}, {1, 1, 8}, // Around the tunk + } ; + + int Height = 7 + (a_Noise.IntNoise3DInt(a_BlockX + 5 * a_Seq, a_BlockY, a_BlockZ + 5 * a_Seq) / 5) % 3; + + a_LogBlocks.reserve(Height); + a_OtherBlocks.reserve( + 2 * ARRAYCOUNT(BigO3) + // O3 layer, 2x + 2 * ARRAYCOUNT(BigO2) + // O2 layer, 2x + ARRAYCOUNT(BigO1) + 1 + // Plus on the top + Height * ARRAYCOUNT(Vines) + // Vines + 50 // some safety + ); + + for (int i = 0; i < Height; i++) + { + a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE)); + } + int hei = a_BlockY + Height - 3; + + // Put vines around the lowermost leaves layer: + PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_OtherBlocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES); + + // The lower two leaves layers are BigO3 with log in the middle and possibly corners: + for (int i = 0; i < 2; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + hei++; + } // for i - 2* + + // Two layers of BigO2 leaves, possibly with corners: + for (int i = 0; i < 1; i++) + { + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + hei++; + } // for i - 2* + + // Top plus, all leaves: + PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE); + a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE)); +} + + + + diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h new file mode 100644 index 000000000..f5148ad6f --- /dev/null +++ b/src/Generating/Trees.h @@ -0,0 +1,93 @@ + +// Trees.h + +// Interfaces to helper functions used for generating trees + +/* +Note that all of these functions must generate the same tree image for the same input (x, y, z, seq) + - cStructGenTrees depends on this +To generate a random image for the (x, y, z) coords, pass an arbitrary value as (seq). +Each function returns two arrays of blocks, "logs" and "other". The point is that logs are of higher priority, +logs can overwrite others(leaves), but others shouldn't overwrite logs. This is an optimization for the generator. +*/ + + + + + +#pragma once + +#include "../ChunkDef.h" +#include "../Noise.h" + + + + + +// Blocks that don't block tree growth: +#define CASE_TREE_ALLOWED_BLOCKS \ + case E_BLOCK_AIR: \ + case E_BLOCK_LEAVES: \ + case E_BLOCK_SNOW: \ + case E_BLOCK_TALL_GRASS: \ + case E_BLOCK_DEAD_BUSH: \ + case E_BLOCK_SAPLING: \ + case E_BLOCK_VINES + +// Blocks that a tree may overwrite when growing: +#define CASE_TREE_OVERWRITTEN_BLOCKS \ + case E_BLOCK_AIR: \ + /* case E_BLOCK_LEAVES: LEAVES are a special case, they can be overwritten only by log. Handled in cChunkMap::ReplaceTreeBlocks(). */ \ + case E_BLOCK_SNOW: \ + case E_BLOCK_TALL_GRASS: \ + case E_BLOCK_DEAD_BUSH: \ + case E_BLOCK_SAPLING: \ + case E_BLOCK_VINES + + + + + +/// Generates an image of a tree at the specified coords (lowest trunk block) in the specified biome +void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random apple tree +void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a small (nonbranching) apple tree +void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a large (branching) apple tree +void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random birch tree +void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random conifer tree +void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random spruce (short conifer, two layers of leaves) +void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random pine (tall conifer, little leaves at top) +void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random swampland tree +void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random apple bush (for jungles) +void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a random jungle tree +void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a large jungle tree (2x2 trunk) +void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + +/// Generates an image of a small jungle tree (1x1 trunk) +void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks); + + + + + diff --git a/src/Globals.cpp b/src/Globals.cpp new file mode 100644 index 000000000..13c6ae709 --- /dev/null +++ b/src/Globals.cpp @@ -0,0 +1,10 @@ + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/src/Globals.h b/src/Globals.h new file mode 100644 index 000000000..ef79e4cf1 --- /dev/null +++ b/src/Globals.h @@ -0,0 +1,227 @@ + +// Globals.h + +// This file gets included from every module in the project, so that global symbols may be introduced easily +// Also used for precompiled header generation in MSVC environments + + + + + +// Compiler-dependent stuff: +#if defined(_MSC_VER) + // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether + #pragma warning(disable:4481) + + // Disable some warnings that we don't care about: + #pragma warning(disable:4100) + + #define OBSOLETE __declspec(deprecated) + + // No alignment needed in MSVC + #define ALIGN_8 + #define ALIGN_16 + +#elif defined(__GNUC__) + + // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? + #define abstract + + // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + #define OBSOLETE __attribute__((deprecated)) + + #define ALIGN_8 __attribute__((aligned(8))) + #define ALIGN_16 __attribute__((aligned(16))) + + // Some portability macros :) + #define stricmp strcasecmp + +#else + + #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" + + /* + // Copy and uncomment this into another #elif section based on your compiler identification + + // Explicitly mark classes as abstract (no instances can be created) + #define abstract + + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + // Mark functions as obsolete, so that their usage results in a compile-time warning + #define OBSOLETE + + // Mark types / variables for alignment. Do the platforms need it? + #define ALIGN_8 + #define ALIGN_16 + */ + +#endif + + + + + +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; + + + + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for any class that shouldn't allow copying itself +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &); \ + void operator=(const TypeName &) + +// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc +#define UNUSED(X) (void)(X) + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + + #define _WIN32_WINNT 0x501 // We want to target WinXP and higher + + #include <Windows.h> + #include <winsock2.h> + #include <Ws2tcpip.h> // IPv6 stuff + + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage + #undef min + #undef max + + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant + #ifdef GetFreeSpace + #undef GetFreeSpace + #endif // GetFreeSpace +#else + #include <sys/types.h> + #include <sys/time.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <time.h> + #include <dirent.h> + #include <errno.h> + #include <iostream> + + #include <cstdio> + #include <cstring> + #include <pthread.h> + #include <semaphore.h> + #include <errno.h> + #include <fcntl.h> +#if !defined(ANDROID_NDK) + #include <tr1/memory> +#endif +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include <sys/stat.h> +#include <assert.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> + + + + + +// STL stuff: +#include <vector> +#include <list> +#include <deque> +#include <string> +#include <map> +#include <algorithm> +#include <memory> +#include <set> +#include <queue> + + + + + +// Common headers (part 1, without macros): +#include "StringUtils.h" +#include "OSSupport/Sleep.h" +#include "OSSupport/CriticalSection.h" +#include "OSSupport/Semaphore.h" +#include "OSSupport/Event.h" +#include "OSSupport/Thread.h" +#include "OSSupport/File.h" +#include "MCLogger.h" + + + + + +// Common definitions: + +/// Evaluates to the number of elements in an array (compile-time!) +#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) + +/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" ) +#define KiB * 1024 +#define MiB * 1024 * 1024 + +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div ) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div)) + +// Own version of assert() that writes failed assertions to the log for review +#ifdef _DEBUG + #define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) ) +#else + #define ASSERT(x) ((void)0) +#endif + +// Pretty much the same as ASSERT() but stays in Release builds +#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) + + + + + +/// A generic interface used mainly in ForEach() functions +template <typename Type> class cItemCallback +{ +public: + /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating + virtual bool Item(Type * a_Type) = 0; +} ; + + + + + +// Common headers (part 2, with macros): +#include "ChunkDef.h" +#include "BlockID.h" + + + + diff --git a/src/Group.cpp b/src/Group.cpp new file mode 100644 index 000000000..448d29d87 --- /dev/null +++ b/src/Group.cpp @@ -0,0 +1,37 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Group.h" + +void cGroup::AddCommand( std::string a_Command ) +{ + m_Commands[ a_Command ] = true; +} + +void cGroup::AddPermission( std::string a_Permission ) +{ + m_Permissions[ a_Permission ] = true; +} + +bool cGroup::HasCommand( std::string a_Command ) +{ + if( m_Commands.find("*") != m_Commands.end() ) return true; + + CommandMap::iterator itr = m_Commands.find( a_Command ); + if( itr != m_Commands.end() ) + { + if( itr->second ) return true; + } + + for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + +void cGroup::InheritFrom( cGroup* a_Group ) +{ + m_Inherits.remove( a_Group ); + m_Inherits.push_back( a_Group ); +}
\ No newline at end of file diff --git a/src/Group.h b/src/Group.h new file mode 100644 index 000000000..65ee1a60a --- /dev/null +++ b/src/Group.h @@ -0,0 +1,40 @@ + +#pragma once + + + + + +class cGroup // tolua_export +{ // tolua_export +public: // tolua_export + cGroup() {} + ~cGroup() {} + + void SetName( std::string a_Name ) { m_Name = a_Name; } // tolua_export + const std::string & GetName() const { return m_Name; } // tolua_export + void SetColor( std::string a_Color ) { m_Color = a_Color; } // tolua_export + void AddCommand( std::string a_Command ); // tolua_export + void AddPermission( std::string a_Permission ); // tolua_export + void InheritFrom( cGroup* a_Group ); // tolua_export + + bool HasCommand( std::string a_Command ); // tolua_export + + typedef std::map< std::string, bool > PermissionMap; + const PermissionMap & GetPermissions() const { return m_Permissions; } + + typedef std::map< std::string, bool > CommandMap; + const CommandMap & GetCommands() const { return m_Commands; } + + const AString & GetColor() const { return m_Color; } // tolua_export + + typedef std::list< cGroup* > GroupList; + const GroupList & GetInherits() const { return m_Inherits; } +private: + std::string m_Name; + std::string m_Color; + + PermissionMap m_Permissions; + CommandMap m_Commands; + GroupList m_Inherits; +};// tolua_export diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp new file mode 100644 index 000000000..df609f05b --- /dev/null +++ b/src/GroupManager.cpp @@ -0,0 +1,122 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "GroupManager.h" +#include "Group.h" +#include "inifile/iniFile.h" +#include "ChatColor.h" +#include "Root.h" + + + + + +typedef std::map< AString, cGroup* > GroupMap; + + + + + +struct cGroupManager::sGroupManagerState +{ + GroupMap Groups; +}; + + + + + +cGroupManager::~cGroupManager() +{ + for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr ) + { + delete itr->second; + } + m_pState->Groups.clear(); + + delete m_pState; +} + + + + + +cGroupManager::cGroupManager() + : m_pState( new sGroupManagerState ) +{ + LOGD("-- Loading Groups --"); + cIniFile IniFile; + if (!IniFile.ReadFile("groups.ini")) + { + LOGWARNING("groups.ini inaccessible, no groups are defined"); + return; + } + + unsigned int NumKeys = IniFile.GetNumKeys(); + for( unsigned int i = 0; i < NumKeys; i++ ) + { + std::string KeyName = IniFile.GetKeyName( i ); + cGroup* Group = GetGroup( KeyName.c_str() ); + + LOGD("Loading group: %s", KeyName.c_str() ); + + Group->SetName( KeyName ); + char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; + if( Color != '-' ) + Group->SetColor( cChatColor::MakeColor(Color) ); + else + Group->SetColor( cChatColor::White ); + + std::string Commands = IniFile.GetValue( KeyName, "Commands", "" ); + if( Commands.size() > 0 ) + { + AStringVector Split = StringSplit( Commands, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->AddCommand( Split[i] ); + } + } + + std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); + if( Permissions.size() > 0 ) + { + AStringVector Split = StringSplit( Permissions, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->AddPermission( Split[i] ); + } + } + + std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" ); + if( Groups.size() > 0 ) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->InheritFrom( GetGroup( Split[i].c_str() ) ); + } + } + } + LOGD("-- Groups Successfully Loaded --"); +} + + + + + +cGroup* cGroupManager::GetGroup( const AString & a_Name ) +{ + GroupMap::iterator itr = m_pState->Groups.find( a_Name ); + if( itr != m_pState->Groups.end() ) + { + return itr->second; + } + + cGroup* Group = new cGroup(); + m_pState->Groups[a_Name] = Group; + + return Group; +} + + + + diff --git a/src/GroupManager.h b/src/GroupManager.h new file mode 100644 index 000000000..d911f976c --- /dev/null +++ b/src/GroupManager.h @@ -0,0 +1,30 @@ + +#pragma once + + + + + +class cGroup; + + + + + +class cGroupManager +{ +public: + cGroup * GetGroup(const AString & a_Name); + +private: + friend class cRoot; + cGroupManager(); + ~cGroupManager(); + + struct sGroupManagerState; + sGroupManagerState * m_pState; +} ; + + + + diff --git a/src/HTTPServer/EnvelopeParser.cpp b/src/HTTPServer/EnvelopeParser.cpp new file mode 100644 index 000000000..8dbe05f14 --- /dev/null +++ b/src/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/src/HTTPServer/EnvelopeParser.h b/src/HTTPServer/EnvelopeParser.h new file mode 100644 index 000000000..6430fbebf --- /dev/null +++ b/src/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/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp new file mode 100644 index 000000000..68afdfc11 --- /dev/null +++ b/src/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/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h new file mode 100644 index 000000000..14603bb70 --- /dev/null +++ b/src/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/src/HTTPServer/HTTPFormParser.cpp b/src/HTTPServer/HTTPFormParser.cpp new file mode 100644 index 000000000..596db424e --- /dev/null +++ b/src/HTTPServer/HTTPFormParser.cpp @@ -0,0 +1,290 @@ + +// 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; +} + + + + + +cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks) : + m_Callbacks(a_Callbacks), + m_Kind(a_Kind), + m_IsValid(true) +{ + Parse(a_Data, a_Size); +} + + + + + +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/src/HTTPServer/HTTPFormParser.h b/src/HTTPServer/HTTPFormParser.h new file mode 100644 index 000000000..a554ca5a4 --- /dev/null +++ b/src/HTTPServer/HTTPFormParser.h @@ -0,0 +1,112 @@ + +// 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: + 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" + } ; + + 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; + } ; + + + /// Creates a parser that is tied to a request and notifies of various events using a callback mechanism + cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks); + + /// Creates a parser with the specified content type that reads data from a string + cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, 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: + + /// 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/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp new file mode 100644 index 000000000..ab23866e6 --- /dev/null +++ b/src/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/src/HTTPServer/HTTPMessage.h b/src/HTTPServer/HTTPMessage.h new file mode 100644 index 000000000..f5284c535 --- /dev/null +++ b/src/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/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp new file mode 100644 index 000000000..f6f5b0f8b --- /dev/null +++ b/src/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/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h new file mode 100644 index 000000000..ed7a03519 --- /dev/null +++ b/src/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 "lib/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/src/HTTPServer/MultipartParser.cpp b/src/HTTPServer/MultipartParser.cpp new file mode 100644 index 000000000..b49f6ec07 --- /dev/null +++ b/src/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/src/HTTPServer/MultipartParser.h b/src/HTTPServer/MultipartParser.h new file mode 100644 index 000000000..d853929ed --- /dev/null +++ b/src/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/src/HTTPServer/NameValueParser.cpp b/src/HTTPServer/NameValueParser.cpp new file mode 100644 index 000000000..a27f07d19 --- /dev/null +++ b/src/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/src/HTTPServer/NameValueParser.h b/src/HTTPServer/NameValueParser.h new file mode 100644 index 000000000..07dc0b942 --- /dev/null +++ b/src/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/src/Inventory.cpp b/src/Inventory.cpp new file mode 100644 index 000000000..b9c7f4aaf --- /dev/null +++ b/src/Inventory.cpp @@ -0,0 +1,682 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Inventory.h" +#include "Entities/Player.h" +#include "ClientHandle.h" +#include "UI/Window.h" +#include "Item.h" +#include "Root.h" +#include "World.h" + +#include "jsoncpp/include/json/json.h" + +#include "Items/ItemHandler.h" + + + + + +cInventory::cInventory(cPlayer & a_Owner) : + m_ArmorSlots (1, 4), // 1 x 4 slots + m_InventorySlots(9, 3), // 9 x 3 slots + m_HotbarSlots (9, 1), // 9 x 1 slots + m_Owner(a_Owner) +{ + // Ask each ItemGrid to report changes to us: + m_ArmorSlots.AddListener(*this); + m_InventorySlots.AddListener(*this); + m_HotbarSlots.AddListener(*this); + + SetEquippedSlotNum(0); +} + + + + + +void cInventory::Clear(void) +{ + m_ArmorSlots.Clear(); + m_InventorySlots.Clear(); + m_HotbarSlots.Clear(); +} + + + + + +int cInventory::HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots) +{ + return HowManyCanFit(a_ItemStack, 0, invNumSlots - 1, a_ConsiderEmptySlots); +} + + + + + +int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots) +{ + if ((a_BeginSlotNum < 0) || (a_BeginSlotNum >= invNumSlots)) + { + LOGWARNING("%s: Bad BeginSlotNum, got %d, there are %d slots; correcting to 0.", __FUNCTION__, a_BeginSlotNum, invNumSlots - 1); + a_BeginSlotNum = 0; + } + if ((a_EndSlotNum < 0) || (a_EndSlotNum >= invNumSlots)) + { + LOGWARNING("%s: Bad EndSlotNum, got %d, there are %d slots; correcting to %d.", __FUNCTION__, a_BeginSlotNum, invNumSlots, invNumSlots - 1); + a_EndSlotNum = invNumSlots - 1; + } + if (a_BeginSlotNum > a_EndSlotNum) + { + std::swap(a_BeginSlotNum, a_EndSlotNum); + } + + char NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + for (int i = a_BeginSlotNum; i <= a_EndSlotNum; i++) + { + const cItem & Slot = GetSlot(i); + if (Slot.IsEmpty()) + { + NumLeft -= MaxStack; + } + else if (Slot.IsStackableWith(a_ItemStack)) + { + NumLeft -= MaxStack - Slot.m_ItemCount; + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return a_ItemStack.m_ItemCount - NumLeft; +} + + + + + +int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst) +{ + cItem ToAdd(a_Item); + int res = 0; + if (ItemCategory::IsArmor(a_Item.m_ItemType)) + { + res = m_ArmorSlots.AddItem(ToAdd, a_AllowNewStacks); + ToAdd.m_ItemCount -= res; + if (ToAdd.m_ItemCount == 0) + { + return res; + } + } + + res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks, a_tryToFillEquippedFirst ? m_EquippedSlotNum : -1); + ToAdd.m_ItemCount = a_Item.m_ItemCount - res; + if (ToAdd.m_ItemCount == 0) + { + return res; + } + + res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks); + return res; +} + + + + + +int cInventory::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst) +{ + int TotalAdded = 0; + for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) + { + int NumAdded = AddItem(*itr, a_AllowNewStacks, a_tryToFillEquippedFirst); + if (itr->m_ItemCount == NumAdded) + { + itr = a_ItemStackList.erase(itr); + } + else + { + itr->m_ItemCount -= NumAdded; + ++itr; + } + TotalAdded += NumAdded; + } + return TotalAdded; +} + + + + + +bool cInventory::RemoveOneEquippedItem(void) +{ + if (m_HotbarSlots.GetSlot(m_EquippedSlotNum).IsEmpty()) + { + return false; + } + + m_HotbarSlots.ChangeSlotCount(m_EquippedSlotNum, -1); + return true; +} + + + + + +int cInventory::HowManyItems(const cItem & a_Item) +{ + return + m_ArmorSlots.HowManyItems(a_Item) + + m_InventorySlots.HowManyItems(a_Item) + + m_HotbarSlots.HowManyItems(a_Item); +} + + + + + +bool cInventory::HasItems(const cItem & a_ItemStack) +{ + int CurrentlyHave = HowManyItems(a_ItemStack); + return (CurrentlyHave >= a_ItemStack.m_ItemCount); +} + + + + + +void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Ignoring.", __FUNCTION__, a_SlotNum, invNumSlots - 1); + return; + } + + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + LOGWARNING("%s(%d): requesting an invalid itemgrid. Ignoring.", __FUNCTION__, a_SlotNum); + return; + } + Grid->SetSlot(GridSlotNum, a_Item); +} + + + + + +void cInventory::SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item) +{ + m_ArmorSlots.SetSlot(a_ArmorSlotNum, a_Item); +} + + + + + +void cInventory::SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item) +{ + m_InventorySlots.SetSlot(a_InventorySlotNum, a_Item); +} + + + + + +void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item) +{ + m_HotbarSlots.SetSlot(a_HotBarSlotNum, a_Item); +} + + + + + +const cItem & cInventory::GetSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first inventory slot instead.", __FUNCTION__, a_SlotNum, invNumSlots - 1); + return m_InventorySlots.GetSlot(0); + } + int GridSlotNum = 0; + const cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + // Something went wrong, but we don't know what. We must return a value, so return the first inventory slot + LOGWARNING("%s(%d): requesting an invalid ItemGrid, returning the first inventory slot instead.", __FUNCTION__, a_SlotNum); + return m_InventorySlots.GetSlot(0); + } + return Grid->GetSlot(GridSlotNum); +} + + + + + +const cItem & cInventory::GetArmorSlot(int a_ArmorSlotNum) const +{ + if ((a_ArmorSlotNum < 0) || (a_ArmorSlotNum >= invArmorCount)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_ArmorSlotNum, invArmorCount - 1); + return m_ArmorSlots.GetSlot(0); + } + return m_ArmorSlots.GetSlot(a_ArmorSlotNum); +} + + + + + +const cItem & cInventory::GetInventorySlot(int a_InventorySlotNum) const +{ + if ((a_InventorySlotNum < 0) || (a_InventorySlotNum >= invInventoryCount)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_InventorySlotNum, invInventoryCount - 1); + return m_InventorySlots.GetSlot(0); + } + return m_InventorySlots.GetSlot(a_InventorySlotNum); +} + + + + + +const cItem & cInventory::GetHotbarSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_SlotNum, invHotbarCount - 1); + return m_HotbarSlots.GetSlot(0); + } + return m_HotbarSlots.GetSlot(a_SlotNum); +} + + + + + +const cItem & cInventory::GetEquippedItem(void) const +{ + return GetHotbarSlot(m_EquippedSlotNum); +} + + + + + +void cInventory::SetEquippedSlotNum(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount)) + { + LOGWARNING("%s: requesting invalid slot index: %d out of %d. Setting 0 instead.", __FUNCTION__, a_SlotNum, invHotbarCount - 1); + m_EquippedSlotNum = 0; + } + else + { + m_EquippedSlotNum = a_SlotNum; + } +} + + + + + +bool cInventory::DamageEquippedItem(short a_Amount) +{ + return DamageItem(invHotbarOffset + m_EquippedSlotNum, a_Amount); +} + + + + + +int cInventory::ChangeSlotCount(int a_SlotNum, int a_AddToCount) +{ + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + LOGWARNING("%s: invalid slot number, expected 0 .. %d, got %d; ignoring", __FUNCTION__, invNumSlots, a_SlotNum); + return -1; + } + return Grid->ChangeSlotCount(GridSlotNum, a_AddToCount); +} + + + + + +bool cInventory::DamageItem(int a_SlotNum, short a_Amount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) + { + LOGWARNING("%s: requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, invNumSlots - 1); + return false; + } + + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum); + if (Grid == NULL) + { + LOGWARNING("%s(%d, %d): requesting an invalid grid, ignoring.", __FUNCTION__, a_SlotNum, a_Amount); + return false; + } + if (!Grid->DamageItem(GridSlotNum, a_Amount)) + { + // The item has been damaged, but did not break yet + return false; + } + + // The item has broken, remove it: + Grid->EmptySlot(GridSlotNum); + return true; +} + + + + + +void cInventory::CopyToItems(cItems & a_Items) +{ + m_ArmorSlots.CopyToItems(a_Items); + m_InventorySlots.CopyToItems(a_Items); + m_HotbarSlots.CopyToItems(a_Items); +} + + + + + +void cInventory::SendSlot(int a_SlotNum) +{ + cItem Item(GetSlot(a_SlotNum)); + if (Item.IsEmpty()) + { + // Sanitize items that are not completely empty (ie. count == 0, but type != empty) + Item.Empty(); + } + m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum + 5, Item); // Slots in the client are numbered "+ 5" because of crafting grid and result +} + + + + + +/* +int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot) +{ + int res = 0; + for (int i = a_BeginSlot; i <= a_EndSlot; i++) + { + if ( + m_Slots[i].IsEmpty() || + ((m_Slots[i].m_ItemType == a_ItemType) && (m_Slots[i].m_ItemDamage == a_ItemDamage)) + ) + { + int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); + ASSERT(m_Slots[i].m_ItemCount <= MaxCount); + int NumToMove = std::min(a_Count, MaxCount - m_Slots[i].m_ItemCount); + m_Slots[i].m_ItemCount += NumToMove; + m_Slots[i].m_ItemDamage = a_ItemDamage; + m_Slots[i].m_ItemType = a_ItemType; + SendSlot(i); + res += NumToMove; + a_Count -= NumToMove; + if (a_Count <= 0) + { + // No more items to distribute + return res; + } + } + } // for i - m_Slots[] + // No more space to distribute to + return res; +} +*/ + + + + + +int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum) +{ + switch (a_ArmorSlotNum) + { + case 0: return 4; // Helmet + case 1: return 3; // Chestplate + case 2: return 2; // Leggings + case 3: return 1; // Boots + } + LOGWARN("%s: invalid armor slot number: %d", __FUNCTION__, a_ArmorSlotNum); + return 0; +} + + + + + +#if 0 +bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ ) +{ + // Fill already present stacks + if( a_Mode < 2 ) + { + int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize(); + for(int i = 0; i < a_Size; i++) + { + if( m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage ) + { + int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount; + if( NumFree >= a_Item.m_ItemCount ) + { + + //printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree ); + m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount; + a_Item.m_ItemCount = 0; + a_bChangedSlots[i + a_Offset] = true; + break; + } + else + { + //printf("2. Adding %i items\n", NumFree ); + m_Slots[i + a_Offset].m_ItemCount += (char)NumFree; + a_Item.m_ItemCount -= (char)NumFree; + a_bChangedSlots[i + a_Offset] = true; + } + } + } + } + + if( a_Mode > 0 ) + { + // If we got more left, find first empty slot + for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++) + { + if( m_Slots[i + a_Offset].m_ItemType == -1 ) + { + m_Slots[i + a_Offset] = a_Item; + a_Item.m_ItemCount = 0; + a_bChangedSlots[i + a_Offset] = true; + } + } + } + + return true; +} +#endif + + + + + +void cInventory::SaveToJson(Json::Value & a_Value) +{ + // The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too: + cItem EmptyItem; + Json::Value EmptyItemJson; + EmptyItem.GetJson(EmptyItemJson); + for (int i = 0; i < 5; i++) + { + a_Value.append(EmptyItemJson); + } + + // The 4 armor slots follow: + for (int i = 0; i < invArmorCount; i++) + { + Json::Value JSON_Item; + m_ArmorSlots.GetSlot(i).GetJson(JSON_Item); + a_Value.append(JSON_Item); + } + + // Next comes the main inventory: + for (int i = 0; i < invInventoryCount; i++) + { + Json::Value JSON_Item; + m_InventorySlots.GetSlot(i).GetJson(JSON_Item); + a_Value.append(JSON_Item); + } + + // The hotbar is the last: + for (int i = 0; i < invHotbarCount; i++) + { + Json::Value JSON_Item; + m_HotbarSlots.GetSlot(i).GetJson(JSON_Item); + a_Value.append(JSON_Item); + } +} + + + + + +bool cInventory::LoadFromJson(Json::Value & a_Value) +{ + int SlotIdx = 0; + + for (Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr, SlotIdx++) + { + cItem Item; + Item.FromJson(*itr); + + // The JSON originally included the 4 crafting slots and the result slot, so we need to skip the first 5 items: + if (SlotIdx < 5) + { + continue; + } + + // If we loaded all the slots, stop now, even if the JSON has more: + if (SlotIdx - 5 >= invNumSlots) + { + break; + } + + int GridSlotNum = 0; + cItemGrid * Grid = GetGridForSlotNum(SlotIdx - 5, GridSlotNum); + ASSERT(Grid != NULL); + Grid->SetSlot(GridSlotNum, Item); + } // for itr - a_Value[] + return true; +} + + + + + +const cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const +{ + ASSERT(a_SlotNum >= 0); + + if (a_SlotNum < invArmorCount) + { + a_GridSlotNum = a_SlotNum; + return &m_ArmorSlots; + } + a_SlotNum -= invArmorCount; + if (a_SlotNum < invInventoryCount) + { + a_GridSlotNum = a_SlotNum; + return &m_InventorySlots; + } + a_GridSlotNum = a_SlotNum - invInventoryCount; + return &m_HotbarSlots; +} + + + + + +cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) +{ + ASSERT(a_SlotNum >= 0); + + if (a_SlotNum < invArmorCount) + { + a_GridSlotNum = a_SlotNum; + return &m_ArmorSlots; + } + a_SlotNum -= invArmorCount; + if (a_SlotNum < invInventoryCount) + { + a_GridSlotNum = a_SlotNum; + return &m_InventorySlots; + } + a_GridSlotNum = a_SlotNum - invInventoryCount; + return &m_HotbarSlots; +} + + + + + +void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + // Send the neccessary updates to whoever needs them + + if (m_Owner.IsDestroyed()) + { + // Owner is not (yet) valid, skip for now + return; + } + + // Armor update needs broadcast to other players: + cWorld * World = m_Owner.GetWorld(); + if ((a_ItemGrid == &m_ArmorSlots) && (World != NULL)) + { + World->BroadcastEntityEquipment( + m_Owner, ArmorSlotNumToEntityEquipmentID(a_SlotNum), + m_ArmorSlots.GetSlot(a_SlotNum), m_Owner.GetClientHandle() + ); + } + + // Convert the grid-local a_SlotNum to our global SlotNum: + int Base = 0; + if (a_ItemGrid == &m_ArmorSlots) + { + Base = invArmorOffset; + } + else if (a_ItemGrid == &m_InventorySlots) + { + Base = invInventoryOffset; + } + else if (a_ItemGrid == &m_HotbarSlots) + { + Base = invHotbarOffset; + } + else + { + ASSERT(!"Unknown ItemGrid calling OnSlotChanged()"); + return; + } + + SendSlot(Base + a_SlotNum); +} + + + + diff --git a/src/Inventory.h b/src/Inventory.h new file mode 100644 index 000000000..3c6a19de8 --- /dev/null +++ b/src/Inventory.h @@ -0,0 +1,182 @@ + +#pragma once + +#include "ItemGrid.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cPlayer; + + + + +// tolua_begin + +/** This class represents the player's inventory +The slots are divided into three areas: +- armor slots (1 x 4) +- inventory slots (9 x 3) +- hotbar slots (9 x 1) +The generic GetSlot(), SetSlot() and HowManyCanFit() functions take the index of the slots, +as if armor slots, inventory slots and then hotbar slots were put one after another. +You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants. +*/ + +class cInventory : + public cItemGrid::cListener +{ +public: + + // Counts and offsets to individual parts of the inventory, as used by GetSlot() / SetSlot() / HowManyCanFit(): + enum + { + invArmorCount = 4, + invInventoryCount = 9 * 3, + invHotbarCount = 9, + + invArmorOffset = 0, + invInventoryOffset = invArmorOffset + invArmorCount, + invHotbarOffset = invInventoryOffset + invInventoryCount, + invNumSlots = invHotbarOffset + invHotbarCount + } ; + + // tolua_end + + cInventory(cPlayer & a_Owner); + + // tolua_begin + + /// Removes all items from the entire inventory + void Clear(void); + + /// Returns number of items out of a_ItemStack that can fit in the storage + int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots); + + /// Returns how many items of the specified type would fit into the slot range specified + int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots); + + /** Adds as many items out of a_ItemStack as can fit. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or + compatible with added items) + if a_tryToFillEquippedFirst is set to false, the regular order applies. + Returns the number of items that fit. + */ + int AddItem(const cItem & a_ItemStack, bool a_AllowNewStacks = true, bool a_tryToFillEquippedFirst = false); + + /** Same as AddItem, but works on an entire list of item stacks. + The a_ItemStackList is modified to reflect the leftover items. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_tryToFillEquippedFirst is set to true, the currently equipped slot will be used first (if empty or + compatible with added items) + if a_tryToFillEquippedFirst is set to false, the regular order applies. + Returns the total number of items that fit. + */ + int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst); + + /// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed + bool RemoveOneEquippedItem(void); + + /// Returns the number of items of type a_Item that are stored + int HowManyItems(const cItem & a_Item); + + /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack + bool HasItems(const cItem & a_ItemStack); + + /// Returns the cItemGrid object representing the armor slots + cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; } + + /// Returns the cItemGrid object representing the main inventory slots + cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; } + + /// Returns the cItemGrid object representing the hotbar slots + cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; } + + /// Returns the player associated with this inventory + cPlayer & GetOwner(void) { return m_Owner; } + + /// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents + void CopyToItems(cItems & a_Items); + + // tolua_end + + /// Returns the player associated with this inventory (const version) + const cPlayer & GetOwner(void) const { return m_Owner; } + + // tolua_begin + + const cItem & GetSlot(int a_SlotNum) const; + const cItem & GetArmorSlot(int a_ArmorSlotNum) const; + const cItem & GetInventorySlot(int a_InventorySlotNum) const; + const cItem & GetHotbarSlot(int a_HotBarSlotNum) const; + const cItem & GetEquippedItem(void) const; + void SetSlot(int a_SlotNum, const cItem & a_Item); + void SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item); + void SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item); + void SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item); + + void SetEquippedSlotNum(int a_SlotNum); + int GetEquippedSlotNum(void) { return m_EquippedSlotNum; } + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count, or -1 if the slot number is invalid. + */ + int ChangeSlotCount(int a_SlotNum, int a_AddToCount); + + /// Adds the specified damage to the specified item; deletes the item and returns true if the item broke. + bool DamageItem(int a_SlotNum, short a_Amount); + + /// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke. + bool DamageEquippedItem(short a_Amount = 1); + + const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); } + const cItem & GetEquippedChestplate(void) const { return m_ArmorSlots.GetSlot(1); } + const cItem & GetEquippedLeggings (void) const { return m_ArmorSlots.GetSlot(2); } + const cItem & GetEquippedBoots (void) const { return m_ArmorSlots.GetSlot(3); } + + // tolua_end + + /// Sends the slot contents to the owner + void SendSlot(int a_SlotNum); + + /// Converts an armor slot number into the ID for the EntityEquipment packet + static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum); + + void SaveToJson(Json::Value & a_Value); + bool LoadFromJson(Json::Value & a_Value); + +protected: + bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 ); + + cItemGrid m_ArmorSlots; + cItemGrid m_InventorySlots; + cItemGrid m_HotbarSlots; + + int m_EquippedSlotNum; + + cPlayer & m_Owner; + + /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum + const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const; + + /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum + cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum); + + // cItemGrid::cListener override: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +}; // tolua_export + + + + diff --git a/src/Item.cpp b/src/Item.cpp new file mode 100644 index 000000000..37277e8c8 --- /dev/null +++ b/src/Item.cpp @@ -0,0 +1,261 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Item.h" +#include "jsoncpp/include/json/json.h" +#include "Items/ItemHandler.h" + + + + + +cItem cItem::CopyOne(void) const +{ + cItem res(*this); + res.m_ItemCount = 1; + return res; +} + + + + + +cItem & cItem::AddCount(char a_AmountToAdd) +{ + m_ItemCount += a_AmountToAdd; + if (m_ItemCount <= 0) + { + Empty(); + } + return *this; +} + + + + + +short cItem::GetMaxDamage(void) const +{ + switch (m_ItemType) + { + case E_ITEM_BOW: return 384; + case E_ITEM_DIAMOND_AXE: return 1563; + case E_ITEM_DIAMOND_HOE: return 1563; + case E_ITEM_DIAMOND_PICKAXE: return 1563; + case E_ITEM_DIAMOND_SHOVEL: return 1563; + case E_ITEM_DIAMOND_SWORD: return 1563; + case E_ITEM_FLINT_AND_STEEL: return 65; + case E_ITEM_GOLD_AXE: return 32; + case E_ITEM_GOLD_HOE: return 32; + case E_ITEM_GOLD_PICKAXE: return 32; + case E_ITEM_GOLD_SHOVEL: return 32; + case E_ITEM_GOLD_SWORD: return 32; + case E_ITEM_IRON_AXE: return 251; + case E_ITEM_IRON_HOE: return 251; + case E_ITEM_IRON_PICKAXE: return 251; + case E_ITEM_IRON_SHOVEL: return 251; + case E_ITEM_IRON_SWORD: return 251; + case E_ITEM_SHEARS: return 251; + case E_ITEM_STONE_AXE: return 132; + case E_ITEM_STONE_HOE: return 132; + case E_ITEM_STONE_PICKAXE: return 132; + case E_ITEM_STONE_SHOVEL: return 132; + case E_ITEM_STONE_SWORD: return 132; + case E_ITEM_WOODEN_AXE: return 60; + case E_ITEM_WOODEN_HOE: return 60; + case E_ITEM_WOODEN_PICKAXE: return 60; + case E_ITEM_WOODEN_SHOVEL: return 60; + case E_ITEM_WOODEN_SWORD: return 60; + } + return 0; +} + + + + + +bool cItem::DamageItem(short a_Amount) +{ + short MaxDamage = GetMaxDamage(); + if (MaxDamage == 0) + { + // Item doesn't have damage + return false; + } + + m_ItemDamage += a_Amount; + return (m_ItemDamage >= MaxDamage); +} + + + + + +bool cItem::IsStackableWith(const cItem & a_OtherStack) const +{ + if (a_OtherStack.m_ItemType != m_ItemType) + { + return false; + } + if (a_OtherStack.m_ItemDamage != m_ItemDamage) + { + return false; + } + if (a_OtherStack.m_Enchantments != m_Enchantments) + { + return false; + } + + return true; +} + + + + + +bool cItem::IsFullStack(void) const +{ + return (m_ItemCount >= ItemHandler(m_ItemType)->GetMaxStackSize()); +} + + + + + +char cItem::GetMaxStackSize(void) const +{ + return ItemHandler(m_ItemType)->GetMaxStackSize(); +} + + + + + +/// Returns the cItemHandler responsible for this item type +cItemHandler * cItem::GetHandler(void) const +{ + return ItemHandler(m_ItemType); +} + + + + + +void cItem::GetJson(Json::Value & a_OutValue) const +{ + a_OutValue["ID"] = m_ItemType; + if (m_ItemType > 0) + { + a_OutValue["Count"] = m_ItemCount; + a_OutValue["Health"] = m_ItemDamage; + AString Enchantments(m_Enchantments.ToString()); + if (!Enchantments.empty()) + { + a_OutValue["ench"] = Enchantments; + } + } +} + + + + + +void cItem::FromJson(const Json::Value & a_Value) +{ + m_ItemType = (ENUM_ITEM_ID)a_Value.get("ID", -1 ).asInt(); + if (m_ItemType > 0) + { + m_ItemCount = (char)a_Value.get("Count", -1 ).asInt(); + m_ItemDamage = (short)a_Value.get("Health", -1 ).asInt(); + m_Enchantments.Clear(); + m_Enchantments.AddFromString(a_Value.get("ench", "").asString()); + } +} + + + + + +bool cItem::IsEnchantable(short item) +{ + if ((item >= 256) && (item <= 259)) + return true; + if ((item >= 267) && (item <= 279)) + return true; + if ((item >= 283) && (item <= 286)) + return true; + if ((item >= 290) && (item <= 294)) + return true; + if ((item >= 298) && (item <= 317)) + return true; + if ((item >= 290) && (item <= 294)) + return true; + + if ((item == 346) || (item == 359) || (item == 261)) + return true; + + return false; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cItems: + +cItem * cItems::Get(int a_Idx) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to get an out-of-bounds item at index %d; there are currently %d items. Returning a nil.", a_Idx, size()); + return NULL; + } + return &at(a_Idx); +} + + + + + +void cItems::Set(int a_Idx, const cItem & a_Item) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size()); + return; + } + at(a_Idx) = a_Item; +} + + + + + +void cItems::Delete(int a_Idx) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to delete an item at an out-of-bounds index %d; there are currently %d items. Ignoring.", a_Idx, size()); + return; + } + erase(begin() + a_Idx); +} + + + + + +void cItems::Set(int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + if ((a_Idx < 0) || (a_Idx >= (int)size())) + { + LOGWARNING("cItems: Attempt to set an item at an out-of-bounds index %d; there are currently %d items. Not setting.", a_Idx, size()); + return; + } + at(a_Idx) = cItem(a_ItemType, a_ItemCount, a_ItemDamage); +} + + + + diff --git a/src/Item.h b/src/Item.h new file mode 100644 index 000000000..c60d0542c --- /dev/null +++ b/src/Item.h @@ -0,0 +1,210 @@ + +// Item.h + +// Declares the cItem class representing an item (in the inventory sense) + + + + + +#pragma once + +#include "Defines.h" +#include "Enchantments.h" + + + + + +// fwd: +class cItemHandler; + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin +class cItem +{ +public: + /// Creates an empty item + cItem(void) : + m_ItemType(E_ITEM_EMPTY), + m_ItemCount(0), + m_ItemDamage(0) + { + } + + + /// Creates an item of the specified type, by default 1 piece with no damage and no enchantments + cItem( + short a_ItemType, + char a_ItemCount = 1, + short a_ItemDamage = 0, + const AString & a_Enchantments = "" + ) : + m_ItemType (a_ItemType), + m_ItemCount (a_ItemCount), + m_ItemDamage (a_ItemDamage), + m_Enchantments(a_Enchantments) + { + if (!IsValidItem(m_ItemType)) + { + if (m_ItemType != E_BLOCK_AIR) + { + LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType); + } + Empty(); + } + } + + + /// Creates an exact copy of the item + cItem(const cItem & a_CopyFrom) : + m_ItemType (a_CopyFrom.m_ItemType), + m_ItemCount (a_CopyFrom.m_ItemCount), + m_ItemDamage (a_CopyFrom.m_ItemDamage), + m_Enchantments(a_CopyFrom.m_Enchantments) + { + } + + + void Empty(void) + { + m_ItemType = E_ITEM_EMPTY; + m_ItemCount = 0; + m_ItemDamage = 0; + m_Enchantments.Clear(); + } + + + void Clear(void) + { + m_ItemType = E_ITEM_EMPTY; + m_ItemCount = 0; + m_ItemDamage = 0; + } + + + bool IsEmpty(void) const + { + return ((m_ItemType <= 0) || (m_ItemCount <= 0)); + } + + + bool IsEqual(const cItem & a_Item) const + { + return ( + IsSameType(a_Item) && + (m_ItemDamage == a_Item.m_ItemDamage) && + (m_Enchantments == a_Item.m_Enchantments) + ); + } + + + bool IsSameType(const cItem & a_Item) const + { + return (m_ItemType == a_Item.m_ItemType) || (IsEmpty() && a_Item.IsEmpty()); + } + + + /// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items + cItem CopyOne(void) const; + + /// Adds the specified count to this object and returns the reference to self (useful for chaining) + cItem & AddCount(char a_AmountToAdd); + + /// Returns the maximum damage value that this item can have; zero if damage is not applied + short GetMaxDamage(void) const; + + /// Damages a weapon / tool. Returns true when damage reaches max value and the item should be destroyed + bool DamageItem(short a_Amount = 1); + + inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); } + + /// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored! + bool IsStackableWith(const cItem & a_OtherStack) const; + + /// Returns true if the item is stacked up to its maximum stacking. + bool IsFullStack(void) const; + + /// Returns the maximum amount of stacked items of this type. + char GetMaxStackSize(void) const; + + // tolua_end + + /// Returns the cItemHandler responsible for this item type + cItemHandler * GetHandler(void) const; + + /// Saves the item data into JSON representation + void GetJson(Json::Value & a_OutValue) const; + + /// Loads the item data from JSON representation + void FromJson(const Json::Value & a_Value); + + /// Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) + static bool IsEnchantable(short a_ItemType); + + // tolua_begin + + short m_ItemType; + char m_ItemCount; + short m_ItemDamage; + cEnchantments m_Enchantments; +}; +// tolua_end + + + + + +/** This class bridges a vector of cItem for safe access via Lua. It checks boundaries for all accesses +Note that this class is zero-indexed! +*/ +class cItems // tolua_export + : public std::vector<cItem> +{ // tolua_export +public: + // tolua_begin + + /// Need a Lua-accessible constructor + cItems(void) {} + + cItem * Get (int a_Idx); + void Set (int a_Idx, const cItem & a_Item); + void Add (const cItem & a_Item) {push_back(a_Item); } + void Delete(int a_Idx); + void Clear (void) {clear(); } + int Size (void) {return size(); } + void Set (int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage); + + void Add (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage) + { + push_back(cItem(a_ItemType, a_ItemCount, a_ItemDamage)); + } + + // tolua_end +} ; // tolua_export + + + + + +/// Used to store loot probability tables +class cLootProbab +{ +public: + cItem m_Item; + int m_MinAmount; + int m_MaxAmount; + int m_Weight; +} ; + + + + diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp new file mode 100644 index 000000000..e9b86173e --- /dev/null +++ b/src/ItemGrid.cpp @@ -0,0 +1,665 @@ + +// ItemGrid.cpp + +// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) + +#include "Globals.h" +#include "ItemGrid.h" +#include "Items/ItemHandler.h" +#include "Noise.h" + + + + + +cItemGrid::cItemGrid(int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_NumSlots(a_Width * a_Height), + m_Slots(new cItem[a_Width * a_Height]), + m_IsInTriggerListeners(false) +{ +} + + + + + +cItemGrid::~cItemGrid() +{ + delete[] m_Slots; +} + + + + + +int cItemGrid::GetSlotNum(int a_X, int a_Y) const +{ + if ( + (a_X < 0) || (a_X >= m_Width) || + (a_Y < 0) || (a_Y >= m_Height) + ) + { + LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)", + __FUNCTION__, a_X, a_Y, m_Width, m_Height + ); + return -1; + } + return a_X + m_Width * a_Y; +} + + + + + +void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: SlotNum out of range: %d in grid of range %d", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + a_X = -1; + a_Y = -1; + return; + } + a_X = a_SlotNum % m_Width; + a_Y = a_SlotNum / m_Width; +} + + + + + +const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const +{ + return GetSlot(GetSlotNum(a_X, a_Y)); +} + + + + + +const cItem & cItemGrid::GetSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number, %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return m_Slots[0]; + } + return m_Slots[a_SlotNum]; +} + + + + + +void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item) +{ + SetSlot(GetSlotNum(a_X, a_Y), a_Item); +} + + + + + +void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage)); +} + + + + + +void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return; + } + m_Slots[a_SlotNum] = a_Item; + TriggerListeners(a_SlotNum); +} + + + + + +void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage)); +} + + + + + +void cItemGrid::EmptySlot(int a_X, int a_Y) +{ + EmptySlot(GetSlotNum(a_X, a_Y)); +} + + + + + +void cItemGrid::EmptySlot(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return; + } + + // Check if already empty: + if (m_Slots[a_SlotNum].IsEmpty()) + { + return; + } + + // Empty and notify + m_Slots[a_SlotNum].Empty(); + TriggerListeners(a_SlotNum); +} + + + + + +bool cItemGrid::IsSlotEmpty(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return true; + } + return m_Slots[a_SlotNum].IsEmpty(); +} + + + + + +bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const +{ + return IsSlotEmpty(GetSlotNum(a_X, a_Y)); +} + + + + + +void cItemGrid::Clear(void) +{ + for (int i = 0; i < m_NumSlots; i++) + { + m_Slots[i].Empty(); + TriggerListeners(i); + } +} + + + + + +int cItemGrid::HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks) +{ + char NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + if (a_AllowNewStacks) + { + NumLeft -= MaxStack; + } + } + else if (m_Slots[i].IsStackableWith(a_ItemStack)) + { + NumLeft -= MaxStack - m_Slots[i].m_ItemCount; + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return a_ItemStack.m_ItemCount - NumLeft; +} + + + + + +int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack) +{ + int PrevCount = 0; + if (m_Slots[a_Slot].IsEmpty()) + { + m_Slots[a_Slot] = a_ItemStack; + PrevCount = 0; + } + else + { + PrevCount = m_Slots[a_Slot].m_ItemCount; + } + m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num); + int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount; + TriggerListeners(a_Slot); + return toReturn; +} + + + + + +int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot) +{ + int NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + + // Try prioritarySlot first: + if ( + (a_PrioritarySlot != -1) && + ( + m_Slots[a_PrioritarySlot].IsEmpty() || + m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack) + ) + ) + { + NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack); + } + + // Scan existing stacks: + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsStackableWith(a_ItemStack)) + { + NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + + if (!a_AllowNewStacks) + { + return (a_ItemStack.m_ItemCount - NumLeft); + } + + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return (a_ItemStack.m_ItemCount - NumLeft); +} + + + + + +int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot) +{ + int TotalAdded = 0; + for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) + { + int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot); + if (itr->m_ItemCount == NumAdded) + { + itr = a_ItemStackList.erase(itr); + } + else + { + itr->m_ItemCount -= NumAdded; + ++itr; + } + TotalAdded += NumAdded; + } + return TotalAdded; +} + + + + + +int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return -1; + } + + if (m_Slots[a_SlotNum].IsEmpty()) + { + // The item is empty, it's not gonna change + return 0; + } + + if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount) + { + // Trying to remove more items than there already are, make the item empty + m_Slots[a_SlotNum].Empty(); + TriggerListeners(a_SlotNum); + return 0; + } + + m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; + TriggerListeners(a_SlotNum); + return m_Slots[a_SlotNum].m_ItemCount; +} + + + + + +int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount) +{ + return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount); +} + + + + + +cItem cItemGrid::RemoveOneItem(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return cItem(); + } + + // If the slot is empty, return an empty item + if (m_Slots[a_SlotNum].IsEmpty()) + { + return cItem(); + } + + // Make a copy of the item in slot, set count to 1 and remove one from the slot + cItem res = m_Slots[a_SlotNum]; + res.m_ItemCount = 1; + m_Slots[a_SlotNum].m_ItemCount -= 1; + + // Emptying the slot correctly if appropriate + if (m_Slots[a_SlotNum].m_ItemCount == 0) + { + m_Slots[a_SlotNum].Empty(); + } + + // Notify everyone of the change + TriggerListeners(a_SlotNum); + + // Return the stored one item + return res; +} + + + + + +cItem cItemGrid::RemoveOneItem(int a_X, int a_Y) +{ + return RemoveOneItem(GetSlotNum(a_X, a_Y)); +} + + + + + +int cItemGrid::HowManyItems(const cItem & a_Item) +{ + int res = 0; + for (int i = 0; i < m_NumSlots; i++) + { + if (m_Slots[i].IsStackableWith(a_Item)) + { + res += m_Slots[i].m_ItemCount; + } + } + return res; +} + + + + + +bool cItemGrid::HasItems(const cItem & a_ItemStack) +{ + int CurrentlyHave = HowManyItems(a_ItemStack); + return (CurrentlyHave >= a_ItemStack.m_ItemCount); +} + + + + + +int cItemGrid::GetFirstEmptySlot(void) const +{ + return GetNextEmptySlot(-1); +} + + + + + +int cItemGrid::GetFirstUsedSlot(void) const +{ + return GetNextUsedSlot(-1); +} + + + + + +int cItemGrid::GetLastEmptySlot(void) const +{ + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetLastUsedSlot(void) const +{ + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (!m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetNextEmptySlot(int a_StartFrom) const +{ + for (int i = a_StartFrom + 1; i < m_NumSlots; i++) + { + if (m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetNextUsedSlot(int a_StartFrom) const +{ + for (int i = a_StartFrom + 1; i < m_NumSlots; i++) + { + if (!m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +void cItemGrid::CopyToItems(cItems & a_Items) const +{ + for (int i = 0; i < m_NumSlots; i++) + { + if (!m_Slots[i].IsEmpty()) + { + a_Items.push_back(m_Slots[i]); + } + } // for i - m_Slots[] +} + + + + + +bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots); + return false; + } + return m_Slots[a_SlotNum].DamageItem(a_Amount); +} + + + + + +bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount) +{ + return DamageItem(GetSlotNum(a_X, a_Y), a_Amount); +} + + + + + +void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed) +{ + // Calculate the total weight: + int TotalProbab = 1; + for (int i = 0; i < a_CountLootProbabs; i++) + { + TotalProbab += a_LootProbabs[i].m_Weight; + } + + // Pick the loot items: + cNoise Noise(a_Seed); + for (int i = 0; i < a_NumSlots; i++) + { + int Rnd = (Noise.IntNoise1DInt(i) / 7); + int LootRnd = Rnd % TotalProbab; + Rnd >>= 8; + cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment + for (int j = 0; j < a_CountLootProbabs; j++) + { + LootRnd -= a_LootProbabs[i].m_Weight; + if (LootRnd < 0) + { + CurrentLoot = a_LootProbabs[i].m_Item; + CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount)); + Rnd >>= 8; + break; + } + } // for j - a_LootProbabs[] + SetSlot(Rnd % m_NumSlots, CurrentLoot); + } // for i - NumSlots +} + + + + + +void cItemGrid::AddListener(cListener & a_Listener) +{ + cCSLock Lock(m_CSListeners); + ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() + m_Listeners.push_back(&a_Listener); +} + + + + + +void cItemGrid::RemoveListener(cListener & a_Listener) +{ + cCSLock Lock(m_CSListeners); + ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() + for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr) + { + if (*itr == &a_Listener) + { + m_Listeners.erase(itr); + return; + } + } // for itr - m_Listeners[] +} + + + + + +void cItemGrid::TriggerListeners(int a_SlotNum) +{ + cListeners Listeners; + { + cCSLock Lock(m_CSListeners); + m_IsInTriggerListeners = true; + Listeners = m_Listeners; + } + for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr) + { + (*itr)->OnSlotChanged(this, a_SlotNum); + } // for itr - m_Listeners[] + m_IsInTriggerListeners = false; +} + + + + diff --git a/src/ItemGrid.h b/src/ItemGrid.h new file mode 100644 index 000000000..a4af523cf --- /dev/null +++ b/src/ItemGrid.h @@ -0,0 +1,191 @@ + +// ItemGrid.h + +// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) + + + + +#pragma once + +#include "Item.h" + + + + + +// tolua_begin +class cItemGrid +{ +public: + // tolua_end + + /// This class is used as a callback for when a slot changes + class cListener + { + public: + /// Called whenever a slot changes + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0; + } ; + typedef std::vector<cListener *> cListeners; + + cItemGrid(int a_Width, int a_Height); + + ~cItemGrid(); + + // tolua_begin + int GetWidth (void) const { return m_Width; } + int GetHeight (void) const { return m_Height; } + int GetNumSlots(void) const { return m_NumSlots; } + + /// Converts XY coords into slot number; returns -1 on invalid coords + int GetSlotNum(int a_X, int a_Y) const; + + // tolua_end + + /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp + void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const; + + // tolua_begin + + // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum + const cItem & GetSlot(int a_X, int a_Y) const; + const cItem & GetSlot(int a_SlotNum) const; + + // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum + void SetSlot(int a_X, int a_Y, const cItem & a_Item); + void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage); + void SetSlot(int a_SlotNum, const cItem & a_Item); + void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage); + + // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum + void EmptySlot(int a_X, int a_Y); + void EmptySlot(int a_SlotNum); + + /// Returns true if the specified slot is empty or the slot doesn't exist + bool IsSlotEmpty(int a_SlotNum) const; + + /// Returns true if the specified slot is empty or the slot doesn't exist + bool IsSlotEmpty(int a_X, int a_Y) const; + + /// Sets all items as empty + void Clear(void); + + /// Returns number of items out of a_ItemStack that can fit in the storage + int HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks = true); + + /** Adds as many items out of a_ItemStack as can fit. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in + first (if empty or compatible with added items) + if a_PrioritarySlot is set to -1, regular order apply + Returns the number of items that fit. + */ + int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); + + /** Same as AddItem, but works on an entire list of item stacks. + The a_ItemStackList is modified to reflect the leftover items. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in + first (if empty or compatible with added items) + if a_PrioritarySlot is set to -1, regular order apply + Returns the total number of items that fit. + */ + int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count. + */ + int ChangeSlotCount(int a_SlotNum, int a_AddToCount); + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count. + */ + int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount); + + /** Removes one item from the stack in the specified slot, and returns it. + If the slot was empty, returns an empty item + */ + cItem RemoveOneItem(int a_SlotNum); + + /** Removes one item from the stack in the specified slot, and returns it. + If the slot was empty, returns an empty item + */ + cItem RemoveOneItem(int a_X, int a_Y); + + /// Returns the number of items of type a_Item that are stored + int HowManyItems(const cItem & a_Item); + + /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack + bool HasItems(const cItem & a_ItemStack); + + /// Returns the index of the first empty slot; -1 if all full + int GetFirstEmptySlot(void) const; + + /// Returns the index of the first non-empty slot; -1 if all empty + int GetFirstUsedSlot(void) const; + + /// Returns the index of the last empty slot; -1 if all full + int GetLastEmptySlot(void) const; + + /// Returns the index of the last used slot; -1 if all empty + int GetLastUsedSlot(void) const; + + /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked) + int GetNextEmptySlot(int a_StartFrom) const; + + /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked) + int GetNextUsedSlot(int a_StartFrom) const; + + /// Copies the contents into a cItems object; preserves the original a_Items contents + void CopyToItems(cItems & a_Items) const; + + /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) + bool DamageItem(int a_SlotNum, short a_Amount); + + /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) + bool DamageItem(int a_X, int a_Y, short a_Amount); + + // tolua_end + + + /** Generates random loot from the specified loot probability table, with a chance of enchanted books added. + A total of a_NumSlots are taken by the loot. + Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs + */ + void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed); + + /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback! + void AddListener(cListener & a_Listener); + + /// Removes a slot-change-callback. Must not be called from within the listener callback! + void RemoveListener(cListener & a_Listener); + + // tolua_begin + +protected: + int m_Width; + int m_Height; + int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions + cItem * m_Slots; // x + m_Width * y + + cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object + cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access + bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring + + /// Calls all m_Listeners for the specified slot number + void TriggerListeners(int a_SlotNum); + + /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot + Returns the number of items that did fit. + */ + int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack); +} ; +// tolua_end + + + diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h new file mode 100644 index 000000000..ab4182eea --- /dev/null +++ b/src/Items/ItemBed.h @@ -0,0 +1,56 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Blocks/BlockBed.h" + + + + + +class cItemBedHandler : + public cItemHandler +{ +public: + cItemBedHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace != BLOCK_FACE_TOP) + { + // Can only be placed on the floor + return false; + } + + a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation()); + + // Check if there is empty space for the foot section: + Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); + if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) + { + return false; + } + + a_BlockType = E_BLOCK_BED; + return true; + } +} ; + + + + diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h new file mode 100644 index 000000000..6e3395f1d --- /dev/null +++ b/src/Items/ItemBoat.h @@ -0,0 +1,54 @@ + +// ItemBoat.h + +// Declares the various boat ItemHandlers + + + + + +#pragma once + +#include "../Entities/Boat.h" + + + + + +class cItemBoatHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBoatHandler(int a_ItemType) : + super(a_ItemType) + { + } + + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_Dir < 0) + { + return false; + } + + double x = (double)a_BlockX + 0.5; + double y = (double)a_BlockY + 0.5; + double z = (double)a_BlockZ + 0.5; + + cBoat * Boat = NULL; + + Boat = new cBoat (x, y, z); + Boat->Initialize(a_World); + + return true; + } + +} ; + + + + diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h new file mode 100644 index 000000000..d533c21fd --- /dev/null +++ b/src/Items/ItemBow.h @@ -0,0 +1,87 @@ + +// ItemBow.h + +// Declares the cItemBowHandler class representing the itemhandler for bows + + + + + +#pragma once + +#include "../Entities/ProjectileEntity.h" + + + + + +class cItemBowHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBowHandler(void) : + super(E_ITEM_BOW) + { + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + ASSERT(a_Player != NULL); + + // Check if the player has an arrow in the inventory, or is in Creative: + if (!(a_Player->IsGameModeCreative() || a_Player->GetInventory().HasItems(cItem(E_ITEM_ARROW)))) + { + return false; + } + + a_Player->StartChargingBow(); + return true; + } + + + virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + // Actual shot - produce the arrow with speed based on the ticks that the bow was charged + ASSERT(a_Player != NULL); + + int BowCharge = a_Player->FinishChargingBow(); + double Force = (double)BowCharge / 20; + Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client + if (Force < 0.1) + { + // Too little force, ignore the shot + return; + } + if (Force > 1) + { + Force = 1; + } + + // Create the arrow entity: + cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2); + if (Arrow == NULL) + { + return; + } + if (!Arrow->Initialize(a_Player->GetWorld())) + { + delete Arrow; + return; + } + a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow); + a_Player->GetWorld()->BroadcastSoundEffect("random.bow", (int)a_Player->GetPosX() * 8, (int)a_Player->GetPosY() * 8, (int)a_Player->GetPosZ() * 8, 0.5, (float)Force); + + if (!a_Player->IsGameModeCreative()) + { + a_Player->UseEquippedItem(); + } + } +} ; + + + + + diff --git a/src/Items/ItemBrewingStand.h b/src/Items/ItemBrewingStand.h new file mode 100644 index 000000000..4ff14d4b4 --- /dev/null +++ b/src/Items/ItemBrewingStand.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemBrewingStandHandler : + public cItemHandler +{ +public: + cItemBrewingStandHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_BREWING_STAND; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h new file mode 100644 index 000000000..fa3d48da1 --- /dev/null +++ b/src/Items/ItemBucket.h @@ -0,0 +1,160 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Simulator/FluidSimulator.h" +#include "../Blocks/BlockHandler.h" + + + + + +class cItemBucketHandler : + public cItemHandler +{ +public: + cItemBucketHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + switch (m_ItemType) + { + case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir); + case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA); + case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER); + default: + { + ASSERT(!"Unhandled ItemType"); + return false; + } + } + } + + + + bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + if (a_BlockFace < 0) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta); + LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta); + if (ClickedMeta != 0) + { + // Not a source block + return false; + } + + if (a_Player->GetGameMode() == gmCreative) + { + // In creative mode don't modify the inventory, just remove the fluid: + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + return true; + } + + ENUM_ITEM_ID NewItem = E_ITEM_EMPTY; + switch (ClickedBlock) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + NewItem = E_ITEM_WATER_BUCKET; + break; + } + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + NewItem = E_ITEM_LAVA_BUCKET; + break; + } + + default: return false; + } + + // Remove the bucket from the inventory + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?"); + ASSERT(!"Inventory bucket mismatch"); + return true; + } + + // Give new bucket, filled with fluid: + cItem Item(NewItem, 1); + a_Player->GetInventory().AddItem(Item, true, true); + + // Remove water / lava block + a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + return true; + } + + + bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock) + { + if (a_BlockFace < 0) + { + return false; + } + + BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock); + if (!CanWashAway) + { + // The block pointed at cannot be washed away, so put fluid on top of it / on its sides + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + } + if ( + !CanWashAway && + (CurrentBlock != E_BLOCK_AIR) && + (CurrentBlock != E_BLOCK_WATER) && + (CurrentBlock != E_BLOCK_STATIONARY_WATER) && + (CurrentBlock != E_BLOCK_LAVA) && + (CurrentBlock != E_BLOCK_STATIONARY_LAVA) + ) + { + // Cannot place water here + return false; + } + + if (a_Player->GetGameMode() != gmCreative) + { + // Remove fluid bucket, add empty bucket: + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?"); + ASSERT(!"Inventory bucket mismatch"); + return false; + } + cItem Item(E_ITEM_BUCKET, 1); + if (!a_Player->GetInventory().AddItem(Item,true,true)) + { + return false; + } + } + + // Wash away anything that was there prior to placing: + if (CanWashAway) + { + cBlockHandler * Handler = BlockHandler(CurrentBlock); + if (Handler->DoesDropOnUnsuitable()) + { + Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + } + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0); + + return true; + } + +}; diff --git a/src/Items/ItemCauldron.h b/src/Items/ItemCauldron.h new file mode 100644 index 000000000..8b2ddc29f --- /dev/null +++ b/src/Items/ItemCauldron.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemCauldronHandler : + public cItemHandler +{ +public: + cItemCauldronHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_CAULDRON; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemCloth.h b/src/Items/ItemCloth.h new file mode 100644 index 000000000..aca27a299 --- /dev/null +++ b/src/Items/ItemCloth.h @@ -0,0 +1,23 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemClothHandler : + public cItemHandler +{ +public: + cItemClothHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } +} ; + + + + diff --git a/src/Items/ItemComparator.h b/src/Items/ItemComparator.h new file mode 100644 index 000000000..3fbb7603d --- /dev/null +++ b/src/Items/ItemComparator.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../Blocks/BlockRedstoneRepeater.h" + + + + + +class cItemComparatorHandler : + public cItemHandler +{ +public: + cItemComparatorHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_INACTIVE_COMPARATOR; + a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; + } +} ; + + + + diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h new file mode 100644 index 000000000..72ea0beed --- /dev/null +++ b/src/Items/ItemDoor.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemDoorHandler : + public cItemHandler +{ +public: + cItemDoorHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR; + return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + } +} ; + + + + diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h new file mode 100644 index 000000000..99b8d2543 --- /dev/null +++ b/src/Items/ItemDye.h @@ -0,0 +1,44 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemDyeHandler : + public cItemHandler +{ +public: + cItemDyeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe) + + // Handle growing the plants: + if (a_Item.m_ItemDamage == E_META_DYE_WHITE) + { + if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true)) + { + if (a_Player->GetGameMode() != gmCreative) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + return true; + } + } + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemFlowerPot.h b/src/Items/ItemFlowerPot.h new file mode 100644 index 000000000..befa2ff21 --- /dev/null +++ b/src/Items/ItemFlowerPot.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemFlowerPotHandler : + public cItemHandler +{ +public: + cItemFlowerPotHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_FLOWER_POT; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemFood.h b/src/Items/ItemFood.h new file mode 100644 index 000000000..2ae572331 --- /dev/null +++ b/src/Items/ItemFood.h @@ -0,0 +1,63 @@ +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemFoodHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemFoodHandler(int a_ItemType) + : super(a_ItemType) + { + } + + + virtual bool IsFood(void) override + { + return true; + } + + + virtual FoodInfo GetFoodInfo(void) override + { + switch(m_ItemType) + { + // Please keep alpha-sorted. + case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2); + case E_ITEM_BREAD: return FoodInfo(5, 6); + case E_ITEM_CARROT: return FoodInfo(4, 4.8); + case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2); + case E_ITEM_COOKED_FISH: return FoodInfo(5, 6); + case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8); + case E_ITEM_COOKIE: return FoodInfo(2, 0.4); + case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6); + case E_ITEM_GOLDEN_CARROT: return FoodInfo(6, 14.4); + case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2); + case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2, 60); + case E_ITEM_POTATO: return FoodInfo(1, 0.6); + case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8); + case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8); + case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30); + case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2); + case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8); + case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); + case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80); + case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100); + case E_ITEM_STEAK: return FoodInfo(8, 12.8); + case E_ITEM_MUSHROOM_SOUP: return FoodInfo(6, 7.2); + } + LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType); + return FoodInfo(0, 0.f); + } + +}; + + + + diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp new file mode 100644 index 000000000..92ba94999 --- /dev/null +++ b/src/Items/ItemHandler.cpp @@ -0,0 +1,511 @@ + +#include "Globals.h" +#include "ItemHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../FastRandom.h" + +// Handlers: +#include "ItemBed.h" +#include "ItemBoat.h" +#include "ItemBow.h" +#include "ItemBrewingStand.h" +#include "ItemBucket.h" +#include "ItemCauldron.h" +#include "ItemCloth.h" +#include "ItemComparator.h" +#include "ItemDoor.h" +#include "ItemDye.h" +#include "ItemFlowerPot.h" +#include "ItemFood.h" +#include "ItemHoe.h" +#include "ItemLeaves.h" +#include "ItemLighter.h" +#include "ItemMinecart.h" +#include "ItemPickaxe.h" +#include "ItemThrowable.h" +#include "ItemRedstoneDust.h" +#include "ItemRedstoneRepeater.h" +#include "ItemSapling.h" +#include "ItemSeeds.h" +#include "ItemShears.h" +#include "ItemShovel.h" +#include "ItemSign.h" +#include "ItemSpawnEgg.h" +#include "ItemSugarcane.h" +#include "ItemSword.h" + +#include "../Blocks/BlockHandler.h" + + + + + +bool cItemHandler::m_HandlerInitialized = false; +cItemHandler * cItemHandler::m_ItemHandler[2268]; + + + + + +cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) +{ + if (a_ItemType < 0) + { + // Either nothing (-1), or bad value, both cases should return the air handler + if (a_ItemType < -1) + { + ASSERT(!"Bad item type"); + } + a_ItemType = 0; + } + + if (!m_HandlerInitialized) + { + // We need to initialize + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); + m_HandlerInitialized = true; + } + if (m_ItemHandler[a_ItemType] == NULL) + { + m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType); + } + return m_ItemHandler[a_ItemType]; +} + + + + + +cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) +{ + switch(a_ItemType) + { + default: return new cItemHandler(a_ItemType); + + // Single item per handler, alphabetically sorted: + case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); + case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); + case E_ITEM_BED: return new cItemBedHandler(a_ItemType); + case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); + case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler(); + case E_ITEM_BOW: return new cItemBowHandler; + case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); + case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); + case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); + case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); + case E_ITEM_EGG: return new cItemEggHandler(); + case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); + case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); + case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); + case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); + case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); + case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); + case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); + case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); + case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); + case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); + + case E_ITEM_WOODEN_HOE: + case E_ITEM_STONE_HOE: + case E_ITEM_IRON_HOE: + case E_ITEM_GOLD_HOE: + case E_ITEM_DIAMOND_HOE: + { + return new cItemHoeHandler(a_ItemType); + } + + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_STONE_PICKAXE: + case E_ITEM_IRON_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + case E_ITEM_DIAMOND_PICKAXE: + { + return new cItemPickaxeHandler(a_ItemType); + } + + case E_ITEM_WOODEN_SHOVEL: + case E_ITEM_STONE_SHOVEL: + case E_ITEM_IRON_SHOVEL: + case E_ITEM_GOLD_SHOVEL: + case E_ITEM_DIAMOND_SHOVEL: + { + return new cItemShovelHandler(a_ItemType); + } + + case E_ITEM_WOODEN_SWORD: + case E_ITEM_STONE_SWORD: + case E_ITEM_IRON_SWORD: + case E_ITEM_GOLD_SWORD: + case E_ITEM_DIAMOND_SWORD: + { + return new cItemSwordHandler(a_ItemType); + } + + case E_ITEM_BUCKET: + case E_ITEM_WATER_BUCKET: + case E_ITEM_LAVA_BUCKET: + { + return new cItemBucketHandler(a_ItemType); + } + + case E_ITEM_CARROT: + case E_ITEM_MELON_SEEDS: + case E_ITEM_POTATO: + case E_ITEM_PUMPKIN_SEEDS: + case E_ITEM_SEEDS: + { + return new cItemSeedsHandler(a_ItemType); + } + + case E_ITEM_IRON_DOOR: + case E_ITEM_WOODEN_DOOR: + { + return new cItemDoorHandler(a_ItemType); + } + + case E_ITEM_MINECART: + case E_ITEM_CHEST_MINECART: + case E_ITEM_FURNACE_MINECART: + case E_ITEM_MINECART_WITH_TNT: + case E_ITEM_MINECART_WITH_HOPPER: + { + return new cItemMinecartHandler(a_ItemType); + } + + // Food: + case E_ITEM_BREAD: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_RAW_BEEF: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_STEAK: + case E_ITEM_COOKED_PORKCHOP: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_MUSHROOM_SOUP: + case E_ITEM_SPIDER_EYE: + { + return new cItemFoodHandler(a_ItemType); + } + } +} + + + + + +void cItemHandler::Deinit() +{ + for(int i = 0; i < 2267; i++) + { + delete m_ItemHandler[i]; + } + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case + m_HandlerInitialized = false; +} + + + + + +cItemHandler::cItemHandler(int a_ItemType) +{ + m_ItemType = a_ItemType; +} + + + + + +bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) +{ + return false; +} + + + + + +bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) +{ + return false; +} + + + + + +void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); + + if (a_Player->IsGameModeSurvival()) + { + if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) + { + Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + } + + a_Player->UseEquippedItem(); +} + + + + + +void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item) +{ + +} + + + + + +char cItemHandler::GetMaxStackSize(void) +{ + if (m_ItemType < 256) + { + // All blocks can stack up to 64 + return 64; + } + + switch (m_ItemType) //sorted by id + { + case E_ITEM_ARROW: return 64; + case E_ITEM_BAKED_POTATO: return 64; + case E_ITEM_BLAZE_POWDER: return 64; + case E_ITEM_BLAZE_ROD: return 64; + case E_ITEM_BONE: return 64; + case E_ITEM_BOOK: return 64; + case E_ITEM_BOTTLE_O_ENCHANTING: return 64; + case E_ITEM_BOWL: return 64; + case E_ITEM_BREAD: return 64; + case E_ITEM_BREWING_STAND: return 64; + case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 + case E_ITEM_CARROT: return 64; + case E_ITEM_CAULDRON: return 64; + case E_ITEM_CLAY: return 64; + case E_ITEM_CLAY_BRICK: return 64; + case E_ITEM_CLOCK: return 64; + case E_ITEM_COAL: return 64; + case E_ITEM_COMPARATOR: return 64; + case E_ITEM_COMPASS: return 64; + case E_ITEM_COOKED_CHICKEN: return 64; + case E_ITEM_COOKED_FISH: return 64; + case E_ITEM_COOKED_PORKCHOP: return 64; + case E_ITEM_COOKIE: return 64; + case E_ITEM_DIAMOND: return 64; + case E_ITEM_DYE: return 64; + case E_ITEM_EGG: return 16; + case E_ITEM_EMERALD: return 64; + case E_ITEM_ENDER_PEARL: return 16; + case E_ITEM_EYE_OF_ENDER: return 64; + case E_ITEM_FEATHER: return 64; + case E_ITEM_FERMENTED_SPIDER_EYE: return 64; + case E_ITEM_FIRE_CHARGE: return 64; + case E_ITEM_FIREWORK_ROCKET: return 64; + case E_ITEM_FIREWORK_STAR: return 64; + case E_ITEM_FLINT: return 64; + case E_ITEM_FLOWER_POT: return 64; + case E_ITEM_GHAST_TEAR: return 64; + case E_ITEM_GLASS_BOTTLE: return 64; + case E_ITEM_GLISTERING_MELON: return 64; + case E_ITEM_GLOWSTONE_DUST: return 64; + case E_ITEM_GOLD: return 64; + case E_ITEM_GOLDEN_APPLE: return 64; + case E_ITEM_GOLDEN_CARROT: return 64; + case E_ITEM_GOLD_NUGGET: return 64; + case E_ITEM_GUNPOWDER: return 64; + case E_ITEM_HEAD: return 64; + case E_ITEM_IRON: return 64; + case E_ITEM_LEATHER: return 64; + case E_ITEM_MAGMA_CREAM: return 64; + case E_ITEM_MAP: return 64; + case E_ITEM_MELON_SEEDS: return 64; + case E_ITEM_MELON_SLICE: return 64; + case E_ITEM_NETHER_BRICK: return 64; + case E_ITEM_NETHER_WART: return 64; + case E_ITEM_PAINTINGS: return 64; + case E_ITEM_PAPER: return 64; + case E_ITEM_POISONOUS_POTATO: return 64; + case E_ITEM_POTATO: return 64; + case E_ITEM_PUMPKIN_PIE: return 64; + case E_ITEM_PUMPKIN_SEEDS: return 64; + case E_ITEM_RAW_BEEF: return 64; + case E_ITEM_RAW_CHICKEN: return 64; + case E_ITEM_RAW_FISH: return 64; + case E_ITEM_RAW_PORKCHOP: return 64; + case E_ITEM_RED_APPLE: return 64; + case E_ITEM_REDSTONE_DUST: return 64; + case E_ITEM_REDSTONE_REPEATER: return 64; + case E_ITEM_ROTTEN_FLESH: return 64; + case E_ITEM_SEEDS: return 64; + case E_ITEM_SIGN: return 16; + case E_ITEM_SLIMEBALL: return 64; + case E_ITEM_SNOWBALL: return 16; + case E_ITEM_SPAWN_EGG: return 64; + case E_ITEM_SPIDER_EYE: return 64; + case E_ITEM_STEAK: return 64; + case E_ITEM_STICK: return 64; + case E_ITEM_STRING: return 64; + case E_ITEM_SUGAR: return 64; + case E_ITEM_SUGAR_CANE: return 64; + case E_ITEM_WHEAT: return 64; + } + // By default items don't stack: + return 1; +} + + + + + +bool cItemHandler::IsTool() +{ + // TODO: Rewrite this to list all tools specifically + return + (m_ItemType >= 256 && m_ItemType <= 259) + || (m_ItemType == 261) + || (m_ItemType >= 267 && m_ItemType <= 279) + || (m_ItemType >= 283 && m_ItemType <= 286) + || (m_ItemType >= 290 && m_ItemType <= 294) + || (m_ItemType >= 256 && m_ItemType <= 259) + || (m_ItemType == 325) + || (m_ItemType == 346); +} + + + + + +bool cItemHandler::IsFood(void) +{ + switch (m_ItemType) + { + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_MUSHROOM_SOUP: + case E_ITEM_BREAD: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_COOKED_PORKCHOP: + case E_ITEM_MILK: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_BEEF: + case E_ITEM_STEAK: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_SPIDER_EYE: + case E_ITEM_CARROT: + case E_ITEM_POTATO: + case E_ITEM_BAKED_POTATO: + case E_ITEM_POISONOUS_POTATO: + { + return true; + } + } // switch (m_ItemType) + return false; +} + + + + + +bool cItemHandler::IsPlaceable(void) +{ + // We can place any block that has a corresponding E_BLOCK_TYPE: + return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID); +} + + + + + +bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType) +{ + return false; +} + + + + + +bool cItemHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers + + if (m_ItemType > 256) + { + LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType); + return false; + } + + cBlockHandler * BlockH = BlockHandler(m_ItemType); + return BlockH->GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) +{ + FoodInfo Info = GetFoodInfo(); + + if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f)) + { + bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); + + // If consumed and there's chance of foodpoisoning, do it: + if (Success && (Info.PoisonChance > 0)) + { + cFastRandom r1; + if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0) + { + a_Player->FoodPoison(300); + } + } + + return Success; + } + + return false; +} + + + + + +cItemHandler::FoodInfo cItemHandler::GetFoodInfo() +{ + return FoodInfo(0, 0.f); +} + + + + diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h new file mode 100644 index 000000000..e39bb054b --- /dev/null +++ b/src/Items/ItemHandler.h @@ -0,0 +1,99 @@ + +#pragma once + +#include "../Defines.h" +#include "../Item.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cItemHandler +{ +public: + cItemHandler(int a_ItemType); + + /// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); + + /// Called when the client sends the SHOOT status in the lclk packet + virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {} + + /// Called while the player diggs a block using this item + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); + + /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block + virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z); + + /// Called after the player has eaten this item. + virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); + + /// Returns the maximum stack size for a given item + virtual char GetMaxStackSize(void); + + struct FoodInfo + { + int FoodLevel; + double Saturation; + int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning + + FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) : + FoodLevel(a_FoodLevel), + Saturation(a_Saturation), + PoisonChance(a_PoisonChance) + { + } + } ; + + /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) + virtual FoodInfo GetFoodInfo(); + + /// Lets the player eat a selected item. Returns true if the player ate the item + virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); + + /// Indicates if this item is a tool + virtual bool IsTool(void); + + /// Indicates if this item is food + virtual bool IsFood(void); + + /// Blocks simply get placed + virtual bool IsPlaceable(void); + + /** Called before a block is placed into a world. + The handler should return true to allow placement, false to refuse. + Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. + */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); + + static cItemHandler * GetItemHandler(int a_ItemType); + static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); } + + static void Deinit(); + +protected: + int m_ItemType; + static cItemHandler *CreateItemHandler(int m_ItemType); + + static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1]; + static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized +}; + +//Short function +inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); } diff --git a/src/Items/ItemHoe.h b/src/Items/ItemHoe.h new file mode 100644 index 000000000..7b6b3e6ac --- /dev/null +++ b/src/Items/ItemHoe.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemHoeHandler : + public cItemHandler +{ +public: + cItemHoeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0); + + a_Player->UseEquippedItem(); + return true; + + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemLeaves.h b/src/Items/ItemLeaves.h new file mode 100644 index 000000000..60222eaa9 --- /dev/null +++ b/src/Items/ItemLeaves.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemLeavesHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemLeavesHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + bool res = super::GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed + return res; + } +} ; + + + + diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h new file mode 100644 index 000000000..4281a2d0c --- /dev/null +++ b/src/Items/ItemLighter.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Entities/TNTEntity.h" + + + + + +class cItemLighterHandler : + public cItemHandler +{ +public: + cItemLighterHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (a_BlockFace < 0) + { + return false; + } + + a_Player->UseEquippedItem(); + + switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) + { + case E_BLOCK_TNT: + { + // Activate the TNT: + a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); + break; + } + default: + { + // Light a fire next to/on top of the block if air: + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) + { + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); + break; + } + } + } + + return false; + } +} ; + + + + diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h new file mode 100644 index 000000000..f8eb31a49 --- /dev/null +++ b/src/Items/ItemMinecart.h @@ -0,0 +1,82 @@ + +// ItemMinecart.h + +// Declares the various minecart ItemHandlers + + + + + +#pragma once + +#include "../Entities/Minecart.h" + + + + + +class cItemMinecartHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemMinecartHandler(int a_ItemType) : + super(a_ItemType) + { + } + + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_Dir < 0) + { + return false; + } + + // Check that there's rail in there: + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + switch (Block) + { + case E_BLOCK_MINECART_TRACKS: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_ACTIVATOR_RAIL: + { + // These are allowed + break; + } + default: + { + LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str()); + return false; + } + } + + double x = (double)a_BlockX + 0.5; + double y = (double)a_BlockY + 0.5; + double z = (double)a_BlockZ + 0.5; + cMinecart * Minecart = NULL; + switch (m_ItemType) + { + case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break; + case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break; + case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (x, y, z); break; + case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (x, y, z); break; + case E_ITEM_MINECART_WITH_HOPPER: Minecart = new cMinecartWithHopper (x, y, z); break; + default: + { + ASSERT(!"Unhandled minecart item"); + return false; + } + } // switch (m_ItemType) + Minecart->Initialize(a_World); + return true; + } + +} ; + + + + diff --git a/src/Items/ItemPickaxe.h b/src/Items/ItemPickaxe.h new file mode 100644 index 000000000..bde7f0905 --- /dev/null +++ b/src/Items/ItemPickaxe.h @@ -0,0 +1,92 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + +class cItemPickaxeHandler : + public cItemHandler +{ +public: + cItemPickaxeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + char PickaxeLevel() + { + switch(m_ItemType) + { + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + return 1; + case E_ITEM_STONE_PICKAXE: + return 2; + case E_ITEM_IRON_PICKAXE: + return 3; + case E_ITEM_DIAMOND_PICKAXE: + return 4; + default: + return 0; + } + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + switch(a_BlockType) + { + case E_BLOCK_OBSIDIAN: + { + return PickaxeLevel() >= 4; + } + + case E_BLOCK_DIAMOND_BLOCK: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_GOLD_BLOCK: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + case E_BLOCK_EMERALD_ORE: + { + return PickaxeLevel() >= 3; + } + + case E_BLOCK_IRON_BLOCK: + case E_BLOCK_IRON_ORE: + case E_BLOCK_LAPIS_ORE: + case E_BLOCK_LAPIS_BLOCK: + { + return PickaxeLevel() >= 2; + } + + case E_BLOCK_COAL_ORE: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_END_STONE: + case E_BLOCK_MOSSY_COBBLESTONE: + case E_BLOCK_SANDSTONE_STAIRS: + case E_BLOCK_SANDSTONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_NETHER_BRICK: + case E_BLOCK_NETHERRACK: + case E_BLOCK_STONE_SLAB: + case E_BLOCK_DOUBLE_STONE_SLAB: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_BRICK: + case E_BLOCK_COBBLESTONE_STAIRS: + case E_BLOCK_STONE_BRICK_STAIRS: + case E_BLOCK_NETHER_BRICK_STAIRS: + case E_BLOCK_CAULDRON: + { + return PickaxeLevel() >= 1; + } + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h new file mode 100644 index 000000000..b7860b187 --- /dev/null +++ b/src/Items/ItemRedstoneDust.h @@ -0,0 +1,38 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemRedstoneDustHandler : public cItemHandler +{ +public: + cItemRedstoneDustHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_REDSTONE_WIRE; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemRedstoneRepeater.h b/src/Items/ItemRedstoneRepeater.h new file mode 100644 index 000000000..f69f24eb8 --- /dev/null +++ b/src/Items/ItemRedstoneRepeater.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../Blocks/BlockRedstoneRepeater.h" + + + + + +class cItemRedstoneRepeaterHandler : + public cItemHandler +{ +public: + cItemRedstoneRepeaterHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable() override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF; + a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; + } +} ; + + + + diff --git a/src/Items/ItemSapling.h b/src/Items/ItemSapling.h new file mode 100644 index 000000000..dc0810a45 --- /dev/null +++ b/src/Items/ItemSapling.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemSaplingHandler : public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemSaplingHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + bool res = super::GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + // Only the lowest 3 bits are important + a_BlockMeta = a_BlockMeta & 0x7; + return res; + } +} ; + + + + diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h new file mode 100644 index 000000000..8ca86663f --- /dev/null +++ b/src/Items/ItemSeeds.h @@ -0,0 +1,65 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemSeedsHandler : + public cItemHandler +{ +public: + cItemSeedsHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace != BLOCK_FACE_TOP) + { + // Only allow planting seeds from the top side of the block + return false; + } + + // Only allow placement on farmland + int X = a_BlockX; + int Y = a_BlockY; + int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, a_BlockFace, true); + if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND) + { + return false; + } + + a_BlockMeta = 0; + switch (m_ItemType) + { + case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true; + case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true; + case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true; + case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true; + case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true; + default: a_BlockType = E_BLOCK_AIR; return true; + } + return false; + } +} ; + + + + diff --git a/src/Items/ItemShears.h b/src/Items/ItemShears.h new file mode 100644 index 000000000..6a17607ee --- /dev/null +++ b/src/Items/ItemShears.h @@ -0,0 +1,62 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemShearsHandler : + public cItemHandler +{ +public: + cItemShearsHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsTool(void) override + { + return true; + } + + + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_LEAVES) + { + cItems Drops; + Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03)); + a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_Player->UseEquippedItem(); + return true; + } + return false; + } + + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + switch (a_BlockType) + { + case E_BLOCK_COBWEB: + case E_BLOCK_VINES: + case E_BLOCK_LEAVES: + { + return true; + } + } // switch (a_BlockType) + return false; + } +} ; + + + + diff --git a/src/Items/ItemShovel.h b/src/Items/ItemShovel.h new file mode 100644 index 000000000..d0625ef1c --- /dev/null +++ b/src/Items/ItemShovel.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + +#include "../Blocks/BlockHandler.h" + + + + + +class cItemShovelHandler : public cItemHandler +{ +public: + cItemShovelHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_SNOW) + { + BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_Player->UseEquippedItem(); + return true; + } + return false; + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + return (a_BlockType == E_BLOCK_SNOW); + } +};
\ No newline at end of file diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h new file mode 100644 index 000000000..5ccd79e29 --- /dev/null +++ b/src/Items/ItemSign.h @@ -0,0 +1,51 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Blocks/BlockSign.h" + + + + + +class cItemSignHandler : + public cItemHandler +{ +public: + cItemSignHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace == BLOCK_FACE_TOP) + { + a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation()); + a_BlockType = E_BLOCK_SIGN_POST; + } + else + { + a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace); + a_BlockType = E_BLOCK_WALLSIGN; + } + return true; + } +} ; + + + + diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h new file mode 100644 index 000000000..26dd15b7d --- /dev/null +++ b/src/Items/ItemSpawnEgg.h @@ -0,0 +1,52 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemSpawnEggHandler : public cItemHandler +{ +public: + cItemSpawnEggHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (a_BlockFace < 0) + { + return false; + } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + a_BlockY--; + } + + if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, (cMonster::eType)(a_Item.m_ItemDamage)) >= 0) + { + if (a_Player->GetGameMode() != 1) + { + // The mob was spawned, "use" the item: + a_Player->GetInventory().RemoveOneEquippedItem(); + } + return true; + } + + return false; + } +} ; + + + + diff --git a/src/Items/ItemSugarcane.h b/src/Items/ItemSugarcane.h new file mode 100644 index 000000000..ce93aa3e5 --- /dev/null +++ b/src/Items/ItemSugarcane.h @@ -0,0 +1,39 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemSugarcaneHandler : + public cItemHandler +{ +public: + cItemSugarcaneHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_SUGARCANE; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/src/Items/ItemSword.h b/src/Items/ItemSword.h new file mode 100644 index 000000000..a7c1d2432 --- /dev/null +++ b/src/Items/ItemSword.h @@ -0,0 +1,30 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +class cItemSwordHandler : + public cItemHandler +{ +public: + cItemSwordHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + return (a_BlockType == E_BLOCK_COBWEB); + } +} ; + + + + diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h new file mode 100644 index 000000000..fc24e775a --- /dev/null +++ b/src/Items/ItemThrowable.h @@ -0,0 +1,140 @@ + +// ItemThrowable.h + +// Declares the itemhandlers for throwable items: eggs, snowballs and ender pearls + + + + + +#pragma once + + + + + +class cItemThrowableHandler : + public cItemHandler +{ + typedef cItemHandler super; +public: + cItemThrowableHandler(int a_ItemType, cProjectileEntity::eKind a_ProjectileKind, double a_SpeedCoeff) : + super(a_ItemType), + m_ProjectileKind(a_ProjectileKind), + m_SpeedCoeff(a_SpeedCoeff) + { + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + + Vector3d Pos = a_Player->GetThrowStartPos(); + Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; + a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed); + + return true; + } + +protected: + cProjectileEntity::eKind m_ProjectileKind; + double m_SpeedCoeff; +} ; + + + + + +class cItemEggHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; +public: + cItemEggHandler(void) : + super(E_ITEM_EGG, cProjectileEntity::pkEgg, 30) + { + } +} ; + + + + +class cItemSnowballHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; + +public: + cItemSnowballHandler(void) : + super(E_ITEM_SNOWBALL, cProjectileEntity::pkSnowball, 30) + { + } +} ; + + + + + +class cItemEnderPearlHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; + +public: + cItemEnderPearlHandler(void) : + super(E_ITEM_ENDER_PEARL, cProjectileEntity::pkEnderPearl, 30) + { + } +} ; + + + + + +class cItemBottleOEnchantingHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; +public: + cItemBottleOEnchantingHandler(void) : + super(E_ITEM_BOTTLE_O_ENCHANTING, cProjectileEntity::pkExpBottle, 10) + { + } +}; + + + + + +class cItemFireworkHandler : + public cItemThrowableHandler +{ + typedef cItemThrowableHandler super; +public: + cItemFireworkHandler(void) : + super(E_ITEM_FIREWORK_ROCKET, cProjectileEntity::pkFirework, 0) + { + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) + { + return false; + } + + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + + a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, 0); + + return true; + } + +};
\ No newline at end of file diff --git a/src/LeakFinder.cpp b/src/LeakFinder.cpp new file mode 100644 index 000000000..0f84adb2b --- /dev/null +++ b/src/LeakFinder.cpp @@ -0,0 +1,1047 @@ + +// LeakFinder.cpp + +// Finds memory leaks rather effectively + +// _X: downloaded from http://www.codeproject.com/Articles/3134/Memory-Leak-and-Exception-Trace-CRT-and-COM-Leaks - the real link is in the comments, RC11 version + + + + + +/********************************************************************** + * + * LEAKFINDER.CPP + * + * + * + * History: + * 2010-04-15 RC10 - Updated to VC10 RTM + * Fixed Bug: Application Verifier, thanks to handsinmypocket! + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3439751#xx3439751xx + * 2008-08-04 RC6 - Updated to VC9 RTM + * Fixed Bug: Missing "ole32.lib" LIB + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2253980#xx2253980xx + * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx + * Fixed Bug: Compiling with "/Wall" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx + * Removed "#pragma init_seg (compiler)" from h-file + * + * 2005-12-30 RC5 - Now again VC8 RTM compatible + * - Added Xml-Output (like in the old Leakfinder) + * YOu need to define XML_LEAK_FINDER to activate it + * So you can use the LeakAnalyseTool from + * http://www.codeproject.com/tools/leakfinder.asp + * + * 2005-12-13 RC4 - Merged with the new "StackWalker"-project on + * http://www.codeproject.com/threads/StackWalker.asp + * + * 2005-08-01 RC3 - Merged with the new "StackWalker"-project on + * http://www.codeproject.com/threads/StackWalker.asp + * + * 2005-07-05 RC2 - First version with x86, IA64 and x64 support + * + * 2005-07-04 RC1 - Added "OutputOptions" + * - New define "INIT_LEAK_FINDER_VERBOSE" to + * display more info (for error reporting) + * + * 2005-07-01 Beta3 - Workaround for a bug in the new dbghelp.dll + * (version 6.5.3.7 from 2005-05-30; StakWalk64 no + * refused to produce an callstack on x86 systems + * if the context is NULL or has some registers set + * to 0 (for example Esp). This is against the + * documented behaviour of StackWalk64...) + * - First version with x64-support + * + * 2005-06-16 Beta1 First public release with the following features: + * - Completely rewritten in C++ (object oriented) + * - CRT-Leak-Report + * - COM-Leak-Report + * - Report is done via "OutputDebugString" so + * the line can directly selected in the debugger + * and is opening the corresponding file/line of + * the allocation + * - Tried to support x64 systems, bud had some + * trouble wih StackWalk64 + * See: http://blog.kalmbachnet.de/?postid=43 + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +#include <windows.h> +#include <objidl.h> // Needed if compiled with "WIN32_LEAN_AND_MEAN" +#include <tchar.h> +#include <crtdbg.h> +#include <stdio.h> + +#include <string> +#include <vector> + + +#include "LeakFinder.h" + +// Currently only tested with MS VC++ 5 to 10 +#if (_MSC_VER < 1100) || (_MSC_VER > 1800) +#error Only MS VC++ 5/6/7/7.1/8/9/10/11/12 supported. Check if the '_CrtMemBlockHeader' has not changed with this compiler! +#endif + + +/* _X: MSVC 2012 (MSC 1700) seems to use a different allocation scheme for STL containers, +* allocating lots of small objects and running out of memory very soon +* Thus for MSVC 2012 we cut the callstack buffer length in half +* +* _X 2013_08_25: The callstack tracking gets worse even for MSVC 2008, a single lua_state eats 50 MiB of RAM +* Therefore I decided to further reduce the buffers from 0x2000 to 0x1000 +*/ +// Controlling the callstack depth +#if (_MSC_VER < 1700) + #define MAX_CALLSTACK_LEN_BUF 0x1000 +#else + #define MAX_CALLSTACK_LEN_BUF 0x0800 +#endif + + + + + +#define IGNORE_CRT_ALLOC + +// disable 64-bit compatibility-checks (because we explicite have here either x86 or x64!) +#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'DWORD' to 'LPCVOID' of greater size +#pragma warning(disable:4826) + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + + + + + +static std::string SimpleXMLEncode(LPCSTR szText) +{ + std::string szRet; + for (size_t i=0; i<strlen(szText); i++) + { + switch(szText[i]) + { + case '&': + szRet.append("&"); + break; + case '<': + szRet.append("<"); + break; + case '>': + szRet.append(">"); + break; + case '"': + szRet.append("""); + break; + case '\'': + szRet.append("'"); + break; + default: + szRet += szText[i]; + } + } + return szRet; +} + + + + + +LeakFinderOutput::LeakFinderOutput(int options, LPCSTR szSymPath) + : StackWalker(options, szSymPath) +{ +} + + + + + +void LeakFinderOutput::OnLeakSearchStart(LPCSTR szLeakFinderName) +{ + CHAR buffer[1024]; + _snprintf_s(buffer, 1024, "######## %s ########\n", szLeakFinderName); + this->OnOutput(buffer); +} + + + + + +void LeakFinderOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) +{ + CHAR buffer[1024]; + _snprintf_s(buffer, 1024, "--------------- Key: %s, %d bytes ---------\n", szKeyName, nDataSize); + this->OnOutput(buffer); +} + + + + + +void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if ( ((this->m_options & LeakFinderShowCompleteCallstack) == 0) && ( + (strstr(entry.lineFileName, "afxmem.cpp") != NULL) || + (strstr(entry.lineFileName, "dbgheap.c") != NULL) || + (strstr(entry.lineFileName, "new.cpp") != NULL) || + (strstr(entry.lineFileName, "newop.cpp") != NULL) || + (strstr(entry.lineFileName, "leakfinder.cpp") != NULL) || + (strstr(entry.lineFileName, "stackwalker.cpp") != NULL) + ) ) + { + return; + } + } + StackWalker::OnCallstackEntry(eType, entry); +} + + + + + +// #################################################################### +// XML-Output +LeakFinderXmlOutput::LeakFinderXmlOutput() +{ + TCHAR szXMLFileName[1024]; + + GetModuleFileName(NULL, szXMLFileName, sizeof(szXMLFileName) / sizeof(TCHAR)); + _tcscat_s(szXMLFileName, _T(".mem.xml-leaks")); +#if _MSC_VER < 1400 + m_fXmlFile = _tfopen(szXMLFileName, _T("w")); +#else + m_fXmlFile = NULL; + _tfopen_s(&m_fXmlFile, szXMLFileName, _T("w")); +#endif + if (m_fXmlFile != NULL) + { + SYSTEMTIME st; + GetLocalTime(&st); + fprintf(m_fXmlFile, "<MEMREPORT date=\"%.2d/%.2d/%.4d\" time=\"%.2d:%.2d:%.2d\">\n", + st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond); + } + else + { + MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); + } +} + + + + + +LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) : + m_Progress(10) +{ +#if _MSC_VER < 1400 + m_fXmlFile = _tfopen(szFileName, _T("w")); +#else + m_fXmlFile = NULL; + _tfopen_s(&m_fXmlFile, szFileName, _T("w")); +#endif + if (m_fXmlFile == NULL) + { + MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); + } + else + { + fprintf(m_fXmlFile, "<MEMREPORT>\n"); + } +} + + + + + +LeakFinderXmlOutput::~LeakFinderXmlOutput() +{ + if (m_fXmlFile != NULL) + { + // Write the ending-tags and close the file + fprintf(m_fXmlFile, "</MEMREPORT>\n"); + fclose(m_fXmlFile); + } + m_fXmlFile = NULL; +} + + + + + +void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName) +{ +} + + + + + +void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) +{ + if (m_fXmlFile != NULL) + { + fprintf(m_fXmlFile, "\t<LEAK requestID=\"%s\" size=\"%d\">\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize); + } + if (--m_Progress == 0) + { + m_Progress = 100; + putc('.', stdout); + } +} + + + + + +void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + if (m_fXmlFile != NULL) + { + if (eType != lastEntry) + { + fprintf(m_fXmlFile, "\t\t<STACKENTRY decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(entry.undName).c_str(), entry.offsetFromSmybol); + fprintf(m_fXmlFile, "srcfile=\"%s\" line=\"%d\" line_offset=\"%+ld\" ", SimpleXMLEncode(entry.lineFileName).c_str(), entry.lineNumber, entry.offsetFromLine); + fprintf(m_fXmlFile, "module=\"%s\" base=\"%08lx\" ", SimpleXMLEncode(entry.moduleName).c_str(), entry.baseOfImage); + fprintf(m_fXmlFile, "/>\n"); + } + else + { + fprintf(m_fXmlFile, "\t</LEAK>\n"); + } + } +} + + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Base class for storing contexts in a hashtable +template <typename HASHTABLE_KEY> class ContextHashtableBase +{ +public: + ContextHashtableBase(SIZE_T sizeOfHastable, LPCSTR finderName) + { + SIZE_T s = sizeOfHastable*sizeof(AllocHashEntryType); + m_hHeap = HeapCreate(0, 10*1024 + s, 0); + if (m_hHeap == NULL) + throw; + pAllocHashTable = (AllocHashEntryType*) own_malloc(s); + sAllocEntries = sizeOfHastable; + m_finderName = own_strdup(finderName); + } + +protected: + virtual ~ContextHashtableBase() + { + if (pAllocHashTable != NULL) + own_free(pAllocHashTable); + pAllocHashTable = NULL; + + own_free(m_finderName); + m_finderName = NULL; + + if (m_hHeap != NULL) + HeapDestroy(m_hHeap); + } + + __inline LPVOID own_malloc(SIZE_T size) + { + return HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size); + } + __inline VOID own_free(LPVOID memblock) + { + HeapFree(m_hHeap, 0, memblock); + } + __inline CHAR *own_strdup(const char *str) + { + size_t len = strlen(str)+1; + CHAR *c = (CHAR*)own_malloc(len); +#if _MSC_VER >= 1400 + strcpy_s(c, len, str); +#else + strcpy(c, str); +#endif + return c; + } + + // Disables this leak-finder + virtual LONG Disable() = 0; + // enables the leak-finder again... + virtual LONG Enable() = 0; + +protected: + // Entry for each allocation + typedef struct AllocHashEntryType { + HASHTABLE_KEY key; + SIZE_T nDataSize; // Size of the allocated memory + struct AllocHashEntryType *Next; + CONTEXT c; + PVOID pStackBaseAddr; + SIZE_T nMaxStackSize; + + PVOID pCallstackOffset; + SIZE_T nCallstackLen; + char pcCallstackAddr[MAX_CALLSTACK_LEN_BUF]; // min of both values... + } AllocHashEntryType; + +protected: + virtual SIZE_T HashFunction(HASHTABLE_KEY &key) = 0; + virtual BOOL IsKeyEmpty(HASHTABLE_KEY &key) = 0; + virtual VOID SetEmptyKey(HASHTABLE_KEY &key) = 0; + virtual VOID GetKeyAsString(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) = 0; + //virtual SIZE_T GetNativeBytes(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) { return 0; } + +public: + VOID Insert(HASHTABLE_KEY &key, CONTEXT &context, SIZE_T nDataSize) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry; + + // generate hash-value + HashIdx = HashFunction(key); + + pHashEntry = &pAllocHashTable[HashIdx]; + if (IsKeyEmpty(pHashEntry->key) != FALSE) { + // Entry is empty... + } + else { + // Entry is not empy! make a list of entries for this hash value... + while(pHashEntry->Next != NULL) { + pHashEntry = pHashEntry->Next; + } + + pHashEntry->Next = (AllocHashEntryType*) own_malloc(sizeof(AllocHashEntryType)); + g_CurrentMemUsage += CRTTable::AllocHashEntryTypeSize; + pHashEntry = pHashEntry->Next; + if (pHashEntry == NULL) + { + // Exhausted the available memory? + return; + } + } + pHashEntry->key = key; + pHashEntry->nDataSize = nDataSize; + pHashEntry->Next = NULL; +#ifdef _M_IX86 + pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp); +#elif _M_X64 + pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp); +#elif _M_IA64 + pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP); +#else +#error "Platform not supported!" +#endif + pHashEntry->c = context; + + // Query the max. stack-area: + MEMORY_BASIC_INFORMATION MemBuffer; + if(VirtualQuery((LPCVOID) pHashEntry->pCallstackOffset, &MemBuffer, sizeof(MemBuffer)) > 0) + { + pHashEntry->pStackBaseAddr = MemBuffer.BaseAddress; + pHashEntry->nMaxStackSize = MemBuffer.RegionSize; + } + else + { + pHashEntry->pStackBaseAddr = 0; + pHashEntry->nMaxStackSize = 0; + } + + SIZE_T bytesToRead = MAX_CALLSTACK_LEN_BUF; + if (pHashEntry->nMaxStackSize > 0) + { + SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset; + bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF); + } + // Now read the callstack: + if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0) + { + // Could not read memory... + pHashEntry->nCallstackLen = 0; + pHashEntry->pCallstackOffset = 0; + } // read callstack + } // Insert + + BOOL Remove(HASHTABLE_KEY &key) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry, *pHashEntryLast; + + // get the Hash-Value + HashIdx = HashFunction(key); + + pHashEntryLast = NULL; + pHashEntry = &pAllocHashTable[HashIdx]; + while(pHashEntry != NULL) { + if (pHashEntry->key == key) { + // release my memory + if (pHashEntryLast == NULL) { + // It is an entry in the table, so do not release this memory + if (pHashEntry->Next == NULL) { + // It was the last entry, so empty the table entry + SetEmptyKey(pAllocHashTable[HashIdx].key); + //memset(&pAllocHashTable[HashIdx], 0, sizeof(pAllocHashTable[HashIdx])); + } + else { + // There are some more entries, so shorten the list + AllocHashEntryType *pTmp = pHashEntry->Next; + *pHashEntry = *(pHashEntry->Next); + own_free(pTmp); + g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize; + } + return TRUE; + } + else { + // now, I am in an dynamic allocated entry (it was a collision) + pHashEntryLast->Next = pHashEntry->Next; + own_free(pHashEntry); + g_CurrentMemUsage -= CRTTable::AllocHashEntryTypeSize; + return TRUE; + } + } + pHashEntryLast = pHashEntry; + pHashEntry = pHashEntry->Next; + } + + // if we are here, we could not find the RequestID + return FALSE; + } + + AllocHashEntryType *Find(HASHTABLE_KEY &key) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry; + + // get the Hash-Value + HashIdx = HashFunction(key); + + pHashEntry = &pAllocHashTable[HashIdx]; + while(pHashEntry != NULL) { + if (pHashEntry->key == key) { + return pHashEntry; + } + pHashEntry = pHashEntry->Next; + } + + // entry was not found! + return NULL; + } + + // For the followong static-var See comment in "ShowCallstack"... + static BOOL CALLBACK ReadProcessMemoryFromHashEntry64( + HANDLE hProcess, // hProcess must be a pointer to an hash-entry! + DWORD64 lpBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ) + { + *lpNumberOfBytesRead = 0; + AllocHashEntryType *pHashEntry = (AllocHashEntryType*) pUserData; + if (pHashEntry == NULL) + { + return FALSE; + } + + if ( ( (DWORD64)lpBaseAddress >= (DWORD64)pHashEntry->pCallstackOffset) && ((DWORD64)lpBaseAddress <= ((DWORD64)pHashEntry->pCallstackOffset+pHashEntry->nCallstackLen)) ) { + // Memory is located in saved Callstack: + // Calculate the offset + DWORD dwOffset = (DWORD) ((DWORD64)lpBaseAddress - (DWORD64)pHashEntry->pCallstackOffset); + DWORD dwSize = __min(nSize, MAX_CALLSTACK_LEN_BUF-dwOffset); + memcpy(lpBuffer, &(pHashEntry->pcCallstackAddr[dwOffset]), dwSize); + *lpNumberOfBytesRead = dwSize; + if (dwSize != nSize) + { + return FALSE; + } + *lpNumberOfBytesRead = nSize; + return TRUE; + } + + if (*lpNumberOfBytesRead == 0) // Memory could not be found + { + if ( ( (DWORD64)lpBaseAddress < (DWORD64)pHashEntry->pStackBaseAddr) || ((DWORD64)lpBaseAddress > ((DWORD64)pHashEntry->pStackBaseAddr+pHashEntry->nMaxStackSize)) ) + { + // Stackwalking is done by reading the "real memory" (normally this happens when the StackWalk64 tries to read some code) + SIZE_T st = 0; + BOOL bRet = ReadProcessMemory(hProcess, (LPCVOID) lpBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + return bRet; + } + } + + return TRUE; + } + + VOID ShowLeaks(LeakFinderOutput &leakFinderOutput) + { + SIZE_T ulTemp; + AllocHashEntryType *pHashEntry; + ULONG ulCount = 0; + SIZE_T ulLeaksByte = 0; + + leakFinderOutput.OnLeakSearchStart(this->m_finderName); + + // Move throu every entry + CHAR keyName[1024]; + for(ulTemp = 0; ulTemp < this->sAllocEntries; ulTemp++) { + pHashEntry = &pAllocHashTable[ulTemp]; + if (IsKeyEmpty(pHashEntry->key) == FALSE) { + while(pHashEntry != NULL) { + ulCount++; + CONTEXT c; + memcpy(&c, &(pHashEntry->c), sizeof(CONTEXT)); + + this->GetKeyAsString(pHashEntry->key, keyName, 1024); + + leakFinderOutput.OnLeakStartEntry(keyName, pHashEntry->nDataSize); + leakFinderOutput.ShowCallstack(GetCurrentThread(), &c, ReadProcessMemoryFromHashEntry64, pHashEntry); + + // Count the number of leaky bytes + ulLeaksByte += pHashEntry->nDataSize; + + pHashEntry = pHashEntry->Next; + } // while + } + } + } + + AllocHashEntryType *pAllocHashTable; + SIZE_T sAllocEntries; + HANDLE m_hHeap; + LPSTR m_finderName; + bool m_bSupressUselessLines; +}; // template <typename HASHTABLE_KEY> class ContextHashtableBase + + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Specialization for CRT-Leaks: +// VC5 has excluded all types in release-builds +#ifdef _DEBUG + +// The follwoing is copied from dbgint.h: +// <CRT_INTERNALS> +/* +* For diagnostic purpose, blocks are allocated with extra information and +* stored in a doubly-linked list. This makes all blocks registered with +* how big they are, when they were allocated, and what they are used for. +*/ + +// forward declaration: +#ifndef _M_CEE_PURE +#define MyAllocHookCallingConvention __cdecl +#endif +#if _MSC_VER >= 1400 +#ifdef _M_CEE +#define MyAllocHookCallingConvention __clrcall +#endif +#endif + +static int MyAllocHookCallingConvention MyAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, +#if _MSC_VER <= 1100 // Special case for VC 5 and before + const char * szFileName, +#else + const unsigned char * szFileName, +#endif + int nLine); + +static _CRT_ALLOC_HOOK s_pfnOldCrtAllocHook = NULL; +static LONG s_CrtDisableCount = 0; +static LONG s_lMallocCalled = 0; + + + + + +class CRTTable : public ContextHashtableBase<LONG> +{ +public: + CRTTable() : ContextHashtableBase<LONG>(1021, "CRT-Leaks") + { + // save the previous alloc hook + s_pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook); + } + + virtual ~CRTTable() + { + _CrtSetAllocHook(s_pfnOldCrtAllocHook); + } + + virtual LONG Disable() + { + return InterlockedIncrement(&s_CrtDisableCount); + } + virtual LONG Enable() + { + return InterlockedDecrement(&s_CrtDisableCount); + } + + virtual SIZE_T HashFunction(LONG &key) + { + // I couldn´t find any better and faster + return key % sAllocEntries; + } + virtual BOOL IsKeyEmpty(LONG &key) + { + if (key == 0) + return TRUE; + return FALSE; + } + virtual VOID SetEmptyKey(LONG &key) + { + key = 0; + } + virtual VOID GetKeyAsString(LONG &key, CHAR *szName, SIZE_T nBufferLen) + { +#if _MSC_VER < 1400 + _snprintf_s(szName, nBufferLen, "%d", key); +#else + _snprintf_s(szName, nBufferLen, nBufferLen, "%d", key); +#endif + } + + static const int AllocHashEntryTypeSize = sizeof(AllocHashEntryType); + +protected: + CHAR *m_pBuffer; + SIZE_T m_maxBufferLen; + SIZE_T m_bufferLen; +}; // class CRTTable + + +#define nNoMansLandSize 4 + +typedef struct _CrtMemBlockHeader +{ + struct _CrtMemBlockHeader * pBlockHeaderNext; + struct _CrtMemBlockHeader * pBlockHeaderPrev; + char * szFileName; + int nLine; +#ifdef _WIN64 + /* These items are reversed on Win64 to eliminate gaps in the struct + * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is + * maintained in the debug heap. + */ + int nBlockUse; + size_t nDataSize; +#else /* _WIN64 */ + size_t nDataSize; + int nBlockUse; +#endif /* _WIN64 */ + long lRequest; + unsigned char gap[nNoMansLandSize]; + /* followed by: + * unsigned char data[nDataSize]; + * unsigned char anotherGap[nNoMansLandSize]; + */ +} _CrtMemBlockHeader; +#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1)) +#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) +// </CRT_INTERNALS> + +static CRTTable *g_pCRTTable = NULL; + +size_t g_CurrentMemUsage = 0; + + + + + +// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function! +static int MyAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, +#if _MSC_VER <= 1100 // Special case for VC 5 + const char * szFileName, +#else + const unsigned char * szFileName, +#endif + int nLine) +{ + //static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") }; + //static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") }; + +#ifdef IGNORE_CRT_ALLOC + if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations + return TRUE; +#endif + extern int _crtDbgFlag; + if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) ) + { + // Someone has disabled that the runtime should log this allocation + // so we do not log this allocation + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + // Handle the Disable/Enable setting + if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0) + { + return TRUE; + } + + // Prevent from reentrat calls + if (InterlockedIncrement(&s_lMallocCalled) > 1) + { + // I was already called + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) ); + _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); + + if (nAllocType == _HOOK_FREE) + { + // freeing + // Try to get the header information + if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer + // get the ID + _CrtMemBlockHeader *pHead; + // get a pointer to memory block header + pHead = pHdr(pvData); + nSize = pHead->nDataSize; + lRequest = pHead->lRequest; // This is the ID! + + if (pHead->nBlockUse == _IGNORE_BLOCK) + { + InterlockedDecrement(&s_lMallocCalled); + if (s_pfnOldCrtAllocHook != NULL) + { + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + } + return TRUE; + } + } + if (lRequest != 0) + { + // RequestID was found + size_t temp = g_CurrentMemUsage; + g_CurrentMemUsage -= nSize ; + g_pCRTTable->Remove(lRequest); + if (g_CurrentMemUsage > temp) + { + printf("********************************************\n"); + printf("** Server detected underflow in memory **\n"); + printf("** usage counter. Something is not right. **\n"); + printf("** Writing memory dump into memdump.xml **\n"); + printf("********************************************\n"); + printf("Please wait\n"); + + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + + printf("\nMemory dump complete. Server will now abort.\n"); + abort(); + } + } + } // freeing + + if (nAllocType == _HOOK_REALLOC) + { + // re-allocating + // Try to get the header information + if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer + BOOL bRet; + LONG lReallocRequest; + // get the ID + _CrtMemBlockHeader *pHead; + // get a pointer to memory block header + pHead = pHdr(pvData); + // Try to find the RequestID in the Hash-Table, mark it that it was freed + lReallocRequest = pHead->lRequest; + size_t temp = g_CurrentMemUsage; + g_CurrentMemUsage -= pHead->nDataSize; + bRet = g_pCRTTable->Remove(lReallocRequest); + if (g_CurrentMemUsage > temp) + { + printf("********************************************\n"); + printf("** Server detected underflow in memory **\n"); + printf("** usage counter. Something is not right. **\n"); + printf("** Writing memory dump into memdump.xml **\n"); + printf("********************************************\n"); + printf("Please wait\n"); + + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + + printf("\nMemory dump complete. Server will now abort.\n"); + abort(); + } + } // ValidHeapPointer + } // re-allocating + + //if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) { + if (nAllocType == _HOOK_FREE) + { + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + { + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + } + return TRUE; + } + + CONTEXT c; + GET_CURRENT_CONTEXT(c, CONTEXT_FULL); + + // Only insert in the Hash-Table if it is not a "freeing" + if (nAllocType != _HOOK_FREE) + { + if (lRequest != 0) // Always a valid RequestID should be provided (see comments in the header) + { + //No need to check for overflow since we are checking if we are getting higher than 1gb. + //If we change this, then we probably would want an overflow check. + g_CurrentMemUsage += nSize ; + g_pCRTTable->Insert(lRequest, c, nSize); + + if (g_CurrentMemUsage > 1536 * 1024* 1024) + { + printf("******************************************\n"); + printf("** Server reached 1.5 GiB memory usage, **\n"); + printf("** something is probably wrong. **\n"); + printf("** Writing memory dump into memdump.xml **\n"); + printf("******************************************\n"); + printf("Please wait\n"); + + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + + printf("\nMemory dump complete. Server will now abort.\n"); + abort(); + } + } + } + + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; // allow the memory operation to proceed +} // MyAllocHook + +#endif // _DEBUG + + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Init/Deinit functions + +HRESULT InitLeakFinder() +{ + #ifdef _DEBUG + g_pCRTTable = new CRTTable(); + #endif + return S_OK; +} + + + + + +void DumpUsedMemory(LeakFinderOutput * output) +{ + LeakFinderOutput *pLeakFinderOutput = output; + + #ifdef _DEBUG + g_pCRTTable->Disable(); + #endif + + if (pLeakFinderOutput == NULL) + { + pLeakFinderOutput = new LeakFinderOutput(); + } + + // explicitly load the modules: + pLeakFinderOutput->LoadModules(); + + #ifdef _DEBUG + g_pCRTTable->ShowLeaks(*pLeakFinderOutput); + #endif + + if (output == NULL) + { + delete pLeakFinderOutput; + } +} + + + + + +void DeinitLeakFinder(LeakFinderOutput *output) +{ + DumpUsedMemory(output); + + #ifdef _DEBUG + delete g_pCRTTable; + g_pCRTTable = NULL; + #endif +} + + + + + +void DeinitLeakFinder() +{ + DeinitLeakFinder(NULL); +} + + + + diff --git a/src/LeakFinder.h b/src/LeakFinder.h new file mode 100644 index 000000000..e63b9ec5d --- /dev/null +++ b/src/LeakFinder.h @@ -0,0 +1,156 @@ +/********************************************************************** + * + * LEAKFINDER.H + * + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#include <windows.h> + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT InitLeakFinder(); +void DeinitLeakFinder(); + +#ifdef __cplusplus +} +#endif + + +// The following is only available if the file is CPP +#ifdef __cplusplus + +#include "StackWalker.h" + +// Interface for output... +class LeakFinderOutput : public StackWalker +{ +public: + typedef enum LeakFinderOptions + { + // No addition info will be retrived + // (only the address is available) + LeakFinderNone = 0, + LeakFinderShowCompleteCallstack = 0x1000 + } LeakFinderOptions; + + LeakFinderOutput(int options = OptionsAll, LPCSTR szSymPath = NULL); + virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); + virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); +protected: + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnOutput(LPCSTR szText) + { + printf(szText); + StackWalker::OnOutput(szText); + } + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) + { + if (strcmp(szFuncName, "SymGetLineFromAddr64") == 0) return; + StackWalker::OnDbgHelpErr(szFuncName, gle, addr); + } +}; + +class LeakFinderXmlOutput : public LeakFinderOutput +{ +public: + LeakFinderXmlOutput(); + virtual ~LeakFinderXmlOutput(); + LeakFinderXmlOutput(LPCTSTR szFileName); + virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); + virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); +protected: + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnOutput(LPCSTR szText) { } + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } + + FILE * m_fXmlFile; + int m_Progress; +}; + +// C++ interface: +void DeinitLeakFinder(LeakFinderOutput *output); + +class ZZZ_LeakFinder +{ +public: + ZZZ_LeakFinder() + { + m_pXml = NULL; +#ifdef XML_LEAK_FINDER + m_pXml = new LeakFinderXmlOutput(); +#endif + InitLeakFinder(); + } + ~ZZZ_LeakFinder() + { + DeinitLeakFinder(m_pXml); + if (m_pXml != NULL) delete m_pXml; + } +protected: + LeakFinderXmlOutput *m_pXml; +}; + +#if defined(INIT_LEAK_FINDER) +#if _MSC_VER >= 1200 +#pragma warning(push) +#endif +#pragma warning (disable:4074) + +// WARNING: If you enable this option, the code might run without the CRT being initialized or after the CRT was deinitialized!!! +// Currently the code is not designed to bypass the CRT... +//#pragma init_seg (compiler) +ZZZ_LeakFinder zzz_LeakFinder; + +#if _MSC_VER >= 1200 +#pragma warning(pop) +#else +#pragma warning(default:4074) +#endif +#endif + +#endif // __cplusplus + + + + +extern void DumpUsedMemory(LeakFinderOutput * output = NULL); + + + + + diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp new file mode 100644 index 000000000..d7e60e458 --- /dev/null +++ b/src/LightingThread.cpp @@ -0,0 +1,562 @@ + +// LightingThread.cpp + +// Implements the cLightingThread class representing the thread that processes requests for lighting + +#include "Globals.h" +#include "LightingThread.h" +#include "ChunkMap.h" +#include "World.h" + + + + + +/// If more than this many chunks are in the queue, a warning is printed to the log +#define WARN_ON_QUEUE_SIZE 800 + + + + + +/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]: +class cReader : + public cChunkDataCallback +{ + virtual void BlockTypes(const BLOCKTYPE * a_Type) override + { + // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that) + // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :) + typedef struct {BLOCKTYPE m_Row[16]; } ROW; + ROW * InputRows = (ROW *)a_Type; + ROW * OutputRows = (ROW *)m_BlockTypes; + int InputIdx = 0; + int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + OutputRows[OutputIdx] = InputRows[InputIdx++]; + OutputIdx += 3; + } // for z + // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows + // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip + OutputIdx += cChunkDef::Width * 6; + } // for y + } // BlockTypes() + + + virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override + { + typedef struct {HEIGHTTYPE m_Row[16]; } ROW; + ROW * InputRows = (ROW *)a_Heightmap; + ROW * OutputRows = (ROW *)m_HeightMap; + int InputIdx = 0; + int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3; + for (int z = 0; z < cChunkDef::Width; z++) + { + OutputRows[OutputIdx] = InputRows[InputIdx++]; + OutputIdx += 3; + } // for z + } + +public: + int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start + int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start + BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs) + HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs) +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLightingThread: + +cLightingThread::cLightingThread(void) : + super("cLightingThread"), + m_World(NULL) +{ +} + + + + + +cLightingThread::~cLightingThread() +{ + Stop(); +} + + + + + +bool cLightingThread::Start(cWorld * a_World) +{ + ASSERT(m_World == NULL); // Not started yet + m_World = a_World; + + return super::Start(); +} + + + + + +void cLightingThread::Stop(void) +{ + { + cCSLock Lock(m_CS); + for (sItems::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr) + { + delete itr->m_ChunkStay; + } + m_Queue.clear(); + } + m_ShouldTerminate = true; + m_evtItemAdded.Set(); + + Wait(); +} + + + + + +void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter) +{ + ASSERT(m_World != NULL); // Did you call Start() properly? + + cChunkStay * ChunkStay = new cChunkStay(m_World); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ); + ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1); + ChunkStay->Enable(); + ChunkStay->Load(); + cCSLock Lock(m_CS); + m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter)); + if (m_Queue.size() > WARN_ON_QUEUE_SIZE) + { + LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size()); + } + m_evtItemAdded.Set(); +} + + + + + +void cLightingThread::WaitForQueueEmpty(void) +{ + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty())) + { + cCSUnlock Unlock(Lock); + m_evtQueueEmpty.Wait(); + } +} + + + + + +size_t cLightingThread::GetQueueLength(void) +{ + cCSLock Lock(m_CS); + return m_Queue.size() + m_PostponedQueue.size(); +} + + + + + +void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ) +{ + // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue + + bool NewlyAdded = false; + { + cCSLock Lock(m_CS); + for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); ) + { + if ( + (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) && + (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) + ) + { + // It is a neighbor + m_Queue.push_back(*itr); + itr = m_PostponedQueue.erase(itr); + NewlyAdded = true; + } + else + { + ++itr; + } + } // for itr - m_PostponedQueue[] + } // Lock(m_CS) + + if (NewlyAdded) + { + m_evtItemAdded.Set(); // Notify the thread it has some work to do + } +} + + + + + +void cLightingThread::Execute(void) +{ + while (true) + { + { + cCSLock Lock(m_CS); + if (m_Queue.size() == 0) + { + cCSUnlock Unlock(Lock); + m_evtItemAdded.Wait(); + } + } + + if (m_ShouldTerminate) + { + return; + } + + // Process one items from the queue: + sItem Item; + { + cCSLock Lock(m_CS); + if (m_Queue.empty()) + { + continue; + } + Item = m_Queue.front(); + m_Queue.pop_front(); + if (m_Queue.empty()) + { + m_evtQueueEmpty.Set(); + } + } // CSLock(m_CS) + + LightChunk(Item); + } +} + + + + + + +void cLightingThread::LightChunk(cLightingThread::sItem & a_Item) +{ + cChunkDef::BlockNibbles BlockLight, SkyLight; + + if (!ReadChunks(a_Item.x, a_Item.z)) + { + // Neighbors not available. Re-queue in the postponed queue + cCSLock Lock(m_CS); + m_PostponedQueue.push_back(a_Item); + return; + } + + /* + // DEBUG: torch somewhere: + m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH; + // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++; + */ + + PrepareBlockLight(); + CalcLight(m_BlockLight); + + PrepareSkyLight(); + + /* + // DEBUG: Save chunk data with highlighted seeds for visual inspection: + cFile f4; + if ( + f4.Open(Printf("Chunk_%d_%d_seeds.grab", a_Item.x, a_Item.z), cFile::fmWrite) + ) + { + for (int z = 0; z < cChunkDef::Width * 3; z++) + { + for (int y = cChunkDef::Height / 2; y >= 0; y--) + { + unsigned char Seeds [cChunkDef::Width * 3]; + memcpy(Seeds, m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3); + for (int x = 0; x < cChunkDef::Width * 3; x++) + { + if (m_IsSeed1[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x]) + { + Seeds[x] = E_BLOCK_DIAMOND_BLOCK; + } + } + f4.Write(Seeds, cChunkDef::Width * 3); + } + } + } + //*/ + + CalcLight(m_SkyLight); + + /* + // DEBUG: Save XY slices of the chunk data and lighting for visual inspection: + cFile f1, f2, f3; + if ( + f1.Open(Printf("Chunk_%d_%d_data.grab", a_Item.x, a_Item.z), cFile::fmWrite) && + f2.Open(Printf("Chunk_%d_%d_sky.grab", a_Item.x, a_Item.z), cFile::fmWrite) && + f3.Open(Printf("Chunk_%d_%d_glow.grab", a_Item.x, a_Item.z), cFile::fmWrite) + ) + { + for (int z = 0; z < cChunkDef::Width * 3; z++) + { + for (int y = cChunkDef::Height / 2; y >= 0; y--) + { + f1.Write(m_BlockTypes + y * BlocksPerYLayer + z * cChunkDef::Width * 3, cChunkDef::Width * 3); + unsigned char SkyLight [cChunkDef::Width * 3]; + unsigned char BlockLight[cChunkDef::Width * 3]; + for (int x = 0; x < cChunkDef::Width * 3; x++) + { + SkyLight[x] = m_SkyLight [y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4; + BlockLight[x] = m_BlockLight[y * BlocksPerYLayer + z * cChunkDef::Width * 3 + x] << 4; + } + f2.Write(SkyLight, cChunkDef::Width * 3); + f3.Write(BlockLight, cChunkDef::Width * 3); + } + } + } + //*/ + + CompressLight(m_BlockLight, BlockLight); + CompressLight(m_SkyLight, SkyLight); + + m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight); + + if (a_Item.m_Callback != NULL) + { + a_Item.m_Callback->Call(a_Item.x, a_Item.z); + } + delete a_Item.m_ChunkStay; +} + + + + + +bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ) +{ + cReader Reader; + Reader.m_BlockTypes = m_BlockTypes; + Reader.m_HeightMap = m_HeightMap; + + for (int z = 0; z < 3; z++) + { + Reader.m_ReadingChunkZ = z; + for (int x = 0; x < 3; x++) + { + Reader.m_ReadingChunkX = x; + if (!m_World->GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader)) + { + return false; + } + } // for z + } // for x + + memset(m_BlockLight, 0, sizeof(m_BlockLight)); + memset(m_SkyLight, 0, sizeof(m_SkyLight)); + return true; +} + + + + + +void cLightingThread::PrepareSkyLight(void) +{ + // Clear seeds: + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + + // Walk every column that has all XZ neighbors + for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) + { + int BaseZ = z * cChunkDef::Width * 3; + for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) + { + int idx = BaseZ + x; + int Current = m_HeightMap[idx] + 1; + int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1 + int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1 + int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1 + int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1 + int MaxNeighbor = std::max(std::max(Neighbor1, Neighbor2), std::max(Neighbor3, Neighbor4)); // Maximum of the four neighbors + + // Fill the column from the top down to Current with all-light: + for (int y = cChunkDef::Height - 1, Index = idx + y * BlocksPerYLayer; y >= Current; y--, Index -= BlocksPerYLayer) + { + m_SkyLight[Index] = 15; + } + + // Add Current as a seed: + if (Current < cChunkDef::Height) + { + int CurrentIdx = idx + Current * BlocksPerYLayer; + m_IsSeed1[CurrentIdx] = true; + m_SeedIdx1[m_NumSeeds++] = CurrentIdx; + } + + // Add seed from Current up to the highest neighbor: + for (int y = Current + 1, Index = idx + y * BlocksPerYLayer; y < MaxNeighbor; y++, Index += BlocksPerYLayer) + { + m_IsSeed1[Index] = true; + m_SeedIdx1[m_NumSeeds++] = Index; + } + } + } +} + + + + + +void cLightingThread::PrepareBlockLight(void) +{ + // Clear seeds: + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); + m_NumSeeds = 0; + + // Walk every column that has all XZ neighbors, make a seed for each light-emitting block: + for (int z = 1; z < cChunkDef::Width * 3 - 1; z++) + { + int BaseZ = z * cChunkDef::Width * 3; + for (int x = 1; x < cChunkDef::Width * 3 - 1; x++) + { + int idx = BaseZ + x; + for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer) + { + if (g_BlockLightValue[m_BlockTypes[Index]] == 0) + { + continue; + } + + // Add current block as a seed: + m_IsSeed1[Index] = true; + m_SeedIdx1[m_NumSeeds++] = Index; + + // Light it up: + m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]]; + } + } + } +} + + + + + +void cLightingThread::CalcLight(NIBBLETYPE * a_Light) +{ + int NumSeeds2 = 0; + while (m_NumSeeds > 0) + { + // Buffer 1 -> buffer 2 + memset(m_IsSeed2, 0, sizeof(m_IsSeed2)); + NumSeeds2 = 0; + CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2); + if (NumSeeds2 == 0) + { + return; + } + + // Buffer 2 -> buffer 1 + memset(m_IsSeed1, 0, sizeof(m_IsSeed1)); + m_NumSeeds = 0; + CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1); + } +} + + + + + +void cLightingThread::CalcLightStep( + NIBBLETYPE * a_Light, + int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut +) +{ + int NumSeedsOut = 0; + for (int i = 0; i < a_NumSeedsIn; i++) + { + int SeedIdx = a_SeedIdxIn[i]; + int SeedX = SeedIdx % (cChunkDef::Width * 3); + int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3); + int SeedY = SeedIdx / BlocksPerYLayer; + + // Propagate seed: + if (SeedX < cChunkDef::Width * 3 - 1) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedX > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedZ < cChunkDef::Width * 3 - 1) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedZ > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedY < cChunkDef::Height - 1) + { + PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + if (SeedY > 0) + { + PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut); + } + } // for i - a_SeedIdxIn[] + a_NumSeedsOut = NumSeedsOut; +} + + + + + +void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight) +{ + int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray + int OutIdx = 0; + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x += 2) + { + a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx]; + InIdx += 2; + } + InIdx += cChunkDef::Width * 2; + } + // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows + // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip + InIdx += cChunkDef::Width * cChunkDef::Width * 6; + } +} + + + + diff --git a/src/LightingThread.h b/src/LightingThread.h new file mode 100644 index 000000000..498755025 --- /dev/null +++ b/src/LightingThread.h @@ -0,0 +1,181 @@ + +// LightingThread.h + +// Interfaces to the cLightingThread class representing the thread that processes requests for lighting + +/* +Lighting is done on whole chunks. For each chunk to be lighted, the whole 3x3 chunk area around it is read, +then it is processed, so that the middle chunk area has valid lighting, and the lighting is copied into the ChunkMap. +Lighting is calculated in full char arrays instead of nibbles, so that accessing the arrays is fast. +Lighting is calculated in a flood-fill fashion: +1. Generate seeds from where the light spreads (full skylight / light-emitting blocks) +2. For each seed: + - Spread the light 1 block in each of the 6 cardinal directions, if the blocktype allows + - If the recipient block has had lower lighting value than that being spread, make it a new seed +3. Repeat step 2, until there are no more seeds +The seeds need two fast operations: + - Check if a block at [x, y, z] is already a seed + - Get the next seed in the row +For that reason it is stored in two arrays, one stores a bool saying a seed is in that position, +the other is an array of seed coords, encoded as a single int. +Step 2 needs two separate storages for old seeds and new seeds, so there are two actual storages for that purpose, +their content is swapped after each full step-2-cycle. + +The thread has two queues of chunks that are to be lighted. +The first queue, m_Queue, is the only one that is publicly visible, chunks get queued there by external requests. +The second one, m_PostponedQueue, is for chunks that have been taken out of m_Queue and didn't have neighbors ready. +Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors get valid, using the ChunkReady callback. +*/ + + + +#pragma once + +#include "OSSupport/IsThread.h" +#include "ChunkDef.h" + + + + + +// fwd: "cWorld.h" +class cWorld; + +// fwd: "cChunkMap.h" +class cChunkStay; + + + + + +class cLightingThread : + public cIsThread +{ + typedef cIsThread super; + +public: + + cLightingThread(void); + ~cLightingThread(); + + bool Start(cWorld * a_World); + + void Stop(void); + + /// Queues the entire chunk for lighting + void QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter = NULL); + + /// Blocks until the queue is empty or the thread is terminated + void WaitForQueueEmpty(void); + + size_t GetQueueLength(void); + + /// Called from cWorld when a chunk gets valid. Chunks in m_PostponedQueue may need moving into m_Queue + void ChunkReady(int a_ChunkX, int a_ChunkZ); + +protected: + + struct sItem + { + int x, z; + cChunkStay * m_ChunkStay; + cChunkCoordCallback * m_Callback; + + sItem(void) {} // empty default constructor needed + sItem(int a_X, int a_Z, cChunkStay * a_ChunkStay, cChunkCoordCallback * a_Callback) : + x(a_X), + z(a_Z), + m_ChunkStay(a_ChunkStay), + m_Callback(a_Callback) + { + } + } ; + + typedef std::list<sItem> sItems; + + cWorld * m_World; + cCriticalSection m_CS; + sItems m_Queue; + sItems m_PostponedQueue; // Chunks that have been postponed due to missing neighbors + cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread + cEvent m_evtQueueEmpty; // Set when the queue gets empty + + // Buffers for the 3x3 chunk data + // These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less + // Placing the buffers into the object means that this object can light chunks only in one thread! + // The blobs are XZY organized as a whole, instead of 3x3 XZY-organized subarrays -> + // -> This means data has to be scatterred when reading and gathered when writing! + static const int BlocksPerYLayer = cChunkDef::Width * cChunkDef::Width * 3 * 3; + BLOCKTYPE m_BlockTypes[BlocksPerYLayer * cChunkDef::Height]; + NIBBLETYPE m_BlockLight[BlocksPerYLayer * cChunkDef::Height]; + NIBBLETYPE m_SkyLight [BlocksPerYLayer * cChunkDef::Height]; + HEIGHTTYPE m_HeightMap [BlocksPerYLayer]; + + // Seed management (5.7 MiB) + // Two buffers, in each calc step one is set as input and the other as output, then in the next step they're swapped + // Each seed is represented twice in this structure - both as a "list" and as a "position". + // "list" allows fast traversal from seed to seed + // "position" allows fast checking if a coord is already a seed + unsigned char m_IsSeed1 [BlocksPerYLayer * cChunkDef::Height]; + unsigned int m_SeedIdx1[BlocksPerYLayer * cChunkDef::Height]; + unsigned char m_IsSeed2 [BlocksPerYLayer * cChunkDef::Height]; + unsigned int m_SeedIdx2[BlocksPerYLayer * cChunkDef::Height]; + int m_NumSeeds; + + virtual void Execute(void) override; + + /// Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk + void LightChunk(sItem & a_Item); + + /// Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays + bool ReadChunks(int a_ChunkX, int a_ChunkZ); + + /// Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight + void PrepareSkyLight(void); + + /// Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight + void PrepareBlockLight(void); + + /// Calculates light in the light array specified, using stored seeds + void CalcLight(NIBBLETYPE * a_Light); + + /// Does one step in the light calculation - one seed propagation and seed recalculation + void CalcLightStep( + NIBBLETYPE * a_Light, + int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut + ); + + /// Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage): + void CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight); + + inline void PropagateLight( + NIBBLETYPE * a_Light, + int a_SrcIdx, int a_DstIdx, + int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut + ) + { + ASSERT(a_SrcIdx >= 0); + ASSERT(a_SrcIdx < ARRAYCOUNT(m_SkyLight)); + ASSERT(a_DstIdx >= 0); + ASSERT(a_DstIdx < ARRAYCOUNT(m_BlockTypes)); + + if (a_Light[a_SrcIdx] <= a_Light[a_DstIdx] + g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]]) + { + // We're not offering more light than the dest block already has + return; + } + + a_Light[a_DstIdx] = a_Light[a_SrcIdx] - g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]]; + if (!a_IsSeedOut[a_DstIdx]) + { + a_IsSeedOut[a_DstIdx] = true; + a_SeedIdxOut[a_NumSeedsOut++] = a_DstIdx; + } + } + +} ; + + + + diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp new file mode 100644 index 000000000..9fcbca915 --- /dev/null +++ b/src/LineBlockTracer.cpp @@ -0,0 +1,262 @@ + +// LineBlockTracer.cpp + +// Implements the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points + +#include "Globals.h" +#include "LineBlockTracer.h" +#include "Vector3d.h" +#include "World.h" +#include "Chunk.h" + + + + + + +cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : + super(a_World, a_Callbacks) +{ +} + + + + + +bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End) +{ + cLineBlockTracer Tracer(a_World, a_Callbacks); + return Tracer.Trace(a_Start.x, a_Start.y, a_Start.z, a_End.x, a_End.y, a_End.z); +} + + + + + +bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks &a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ) +{ + cLineBlockTracer Tracer(a_World, a_Callbacks); + return Tracer.Trace(a_StartX, a_StartY, a_StartZ, a_EndX, a_EndY, a_EndZ); +} + + + + + +bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ) +{ + // Initialize the member veriables: + m_StartX = a_StartX; + m_StartY = a_StartY; + m_StartZ = a_StartZ; + m_EndX = a_EndX; + m_EndY = a_EndY; + m_EndZ = a_EndZ; + m_DirX = (m_StartX < m_EndX) ? 1 : -1; + m_DirY = (m_StartY < m_EndY) ? 1 : -1; + m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1; + m_CurrentFace = BLOCK_FACE_NONE; + + // Check the start coords, adjust into the world: + if (m_StartY < 0) + { + if (m_EndY < 0) + { + // Nothing to trace + m_Callbacks->OnNoMoreHits(); + return true; + } + FixStartBelowWorld(); + m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ); + } + else if (m_StartY >= cChunkDef::Height) + { + if (m_EndY >= cChunkDef::Height) + { + m_Callbacks->OnNoMoreHits(); + return true; + } + FixStartAboveWorld(); + m_Callbacks->OnIntoWorld(m_StartX, m_StartY, m_StartZ); + } + + m_CurrentX = (int)floor(m_StartX); + m_CurrentY = (int)floor(m_StartY); + m_CurrentZ = (int)floor(m_StartZ); + + m_DiffX = m_EndX - m_StartX; + m_DiffY = m_EndY - m_StartY; + m_DiffZ = m_EndZ - m_StartZ; + + // The actual trace is handled with ChunkMapCS locked by calling our Item() for the specified chunk + int BlockX = (int)floor(m_StartX); + int BlockZ = (int)floor(m_StartZ); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); + return m_World->DoWithChunk(ChunkX, ChunkZ, *this); +} + + + + + +void cLineBlockTracer::FixStartAboveWorld(void) +{ + // We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on + // Therefore we use an EPS-offset from the height, as small as reasonably possible. + const double Height = (double)cChunkDef::Height - 0.00001; + CalcXZIntersection(Height, m_StartX, m_StartZ); + m_StartY = Height; +} + + + + + +void cLineBlockTracer::FixStartBelowWorld(void) +{ + CalcXZIntersection(0, m_StartX, m_StartZ); + m_StartY = 0; +} + + + + + +void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ) +{ + double Ratio = (m_StartY - a_Y) / (m_StartY - m_EndY); + a_IntersectX = m_StartX + (m_EndX - m_StartX) * Ratio; + a_IntersectZ = m_StartZ + (m_EndZ - m_StartZ) * Ratio; +} + + + + + +bool cLineBlockTracer::MoveToNextBlock(void) +{ + // Find out which of the current block's walls gets hit by the path: + static const double EPS = 0.00001; + double Coeff = 1; + enum eDirection + { + dirNONE, + dirX, + dirY, + dirZ, + } Direction = dirNONE; + if (abs(m_DiffX) > EPS) + { + double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX; + Coeff = (DestX - m_StartX) / m_DiffX; + if (Coeff <= 1) + { + Direction = dirX; + } + } + if (abs(m_DiffY) > EPS) + { + double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY; + double CoeffY = (DestY - m_StartY) / m_DiffY; + if (CoeffY < Coeff) + { + Coeff = CoeffY; + Direction = dirY; + } + } + if (abs(m_DiffZ) > EPS) + { + double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ; + double CoeffZ = (DestZ - m_StartZ) / m_DiffZ; + if (CoeffZ < Coeff) + { + Coeff = CoeffZ; + Direction = dirZ; + } + } + + // Based on the wall hit, adjust the current coords + switch (Direction) + { + case dirX: m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break; + case dirY: m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break; + case dirZ: m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break; + case dirNONE: return false; + } + return true; +} + + + + + +bool cLineBlockTracer::Item(cChunk * a_Chunk) +{ + ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld() + + // This is the actual line tracing loop. + bool Finished = false; + while (true) + { + // Report the current block through the callbacks: + if (a_Chunk == NULL) + { + m_Callbacks->OnNoChunk(); + return false; + } + if (a_Chunk->IsValid()) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width; + a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta); + if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace)) + { + // The callback terminated the trace + return false; + } + } + else + { + if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace)) + { + // The callback terminated the trace + return false; + } + } + + // Move to next block + if (!MoveToNextBlock()) + { + // We've reached the end + m_Callbacks->OnNoMoreHits(); + return true; + } + + // Update the current chunk + if (a_Chunk != NULL) + { + a_Chunk = a_Chunk->GetNeighborChunk(m_CurrentX, m_CurrentZ); + } + + if ((m_CurrentY < 0) || (m_CurrentY >= cChunkDef::Height)) + { + // We've gone out of the world, that's the end of this trace + double IntersectX, IntersectZ; + CalcXZIntersection(m_CurrentY, IntersectX, IntersectZ); + if (m_Callbacks->OnOutOfWorld(IntersectX, m_CurrentY, IntersectZ)) + { + // The callback terminated the trace + return false; + } + m_Callbacks->OnNoMoreHits(); + return true; + } + } +} + + + + diff --git a/src/LineBlockTracer.h b/src/LineBlockTracer.h new file mode 100644 index 000000000..ccbb70ea6 --- /dev/null +++ b/src/LineBlockTracer.h @@ -0,0 +1,87 @@ + +// LineBlockTracer.h + +// Declares the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points + + + + + +#pragma once + +#include "BlockTracer.h" + + + + + +// fwd: Chunk.h +class cChunk; + +// fwd: cChunkMap.h +typedef cItemCallback<cChunk> cChunkCallback; + + + + + + +class cLineBlockTracer : + public cBlockTracer, + public cChunkCallback +{ + typedef cBlockTracer super; + +public: + cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks); + + /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) + bool Trace(double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ); + + // Utility functions for simple one-line usage: + /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) + static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, double a_StartX, double a_StartY, double a_StartZ, double a_EndX, double a_EndY, double a_EndZ); + + /// Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) + static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, const Vector3d & a_Start, const Vector3d & a_End); + +protected: + // The start point of the trace + double m_StartX, m_StartY, m_StartZ; + + // The end point of the trace + double m_EndX, m_EndY, m_EndZ; + + // The difference in coords, End - Start + double m_DiffX, m_DiffY, m_DiffZ; + + // The increment at which the block coords are going from Start to End; either +1 or -1 + int m_DirX, m_DirY, m_DirZ; + + // The current block + int m_CurrentX, m_CurrentY, m_CurrentZ; + + // The face through which the current block has been entered + char m_CurrentFace; + + + /// Adjusts the start point above the world to just at the world's top + void FixStartAboveWorld(void); + + /// Adjusts the start point below the world to just at the world's bottom + void FixStartBelowWorld(void); + + /// Calculates the XZ coords of an intersection with the specified Yconst plane; assumes that such an intersection exists + void CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ); + + /// Moves m_Current to the next block on the line; returns false if no move is possible (reached the end) + bool MoveToNextBlock(void); + + // cChunkCallback overrides: + virtual bool Item(cChunk * a_Chunk) override; +} ; + + + + + diff --git a/src/LinearInterpolation.cpp b/src/LinearInterpolation.cpp new file mode 100644 index 000000000..d4975418b --- /dev/null +++ b/src/LinearInterpolation.cpp @@ -0,0 +1,251 @@ + +// LinearInterpolation.cpp + +// Implements methods for linear interpolation over 1D, 2D and 3D arrays + +#include "Globals.h" +#include "LinearInterpolation.h" + + + + + +/* +// Perform an automatic test upon program start (use breakpoints to debug): + +extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); + +class Test +{ +public: + Test(void) + { + // DoTest1(); + DoTest2(); + } + + + void DoTest1(void) + { + float In[8] = {0, 1, 2, 3, 1, 2, 2, 2}; + float Out[3 * 3 * 3]; + LinearInterpolate1DArray(In, 4, Out, 9); + LinearInterpolate2DArray(In, 2, 2, Out, 3, 3); + LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3); + LOGD("Out[0]: %f", Out[0]); + } + + + void DoTest2(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + float Out[15 * 16 * 17]; + LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17); + Debug3DNoise(Out, 15, 16, 17, "LERP test"); + } +} gTest; +//*/ + + + + + +// Puts linearly interpolated values from one array into another array. 1D version +void LinearInterpolate1DArray( + float * a_Src, + int a_SrcSizeX, + float * a_Dst, + int a_DstSizeX +) +{ + a_Dst[0] = a_Src[0]; + int DstSizeXm1 = a_DstSizeX - 1; + int SrcSizeXm1 = a_SrcSizeX - 1; + float fDstSizeXm1 = (float)DstSizeXm1; + float fSrcSizeXm1 = (float)SrcSizeXm1; + for (int x = 1; x < DstSizeXm1; x++) + { + int SrcIdx = x * SrcSizeXm1 / DstSizeXm1; + float ValLo = a_Src[SrcIdx]; + float ValHi = a_Src[SrcIdx + 1]; + float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx; + a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio; + } + a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1]; +} + + + + + +// Puts linearly interpolated values from one array into another array. 2D version +void LinearInterpolate2DArray( + float * a_Src, + int a_SrcSizeX, int a_SrcSizeY, + float * a_Dst, + int a_DstSizeX, int a_DstSizeY +) +{ + ASSERT(a_DstSizeX > 0); + ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); + ASSERT(a_DstSizeY > 0); + ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); + + // Calculate interpolation ratios and src indices along each axis: + float RatioX[MAX_INTERPOL_SIZEX]; + float RatioY[MAX_INTERPOL_SIZEY]; + int SrcIdxX[MAX_INTERPOL_SIZEX]; + int SrcIdxY[MAX_INTERPOL_SIZEY]; + for (int x = 1; x < a_DstSizeX; x++) + { + SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); + RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; + } + for (int y = 1; y < a_DstSizeY; y++) + { + SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); + RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; + } + + // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: + SrcIdxX[0] = 0; + RatioX[0] = 0; + SrcIdxY[0] = 0; + RatioY[0] = 0; + SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; + RatioX[a_DstSizeX - 1] = 1; + SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; + RatioY[a_DstSizeY - 1] = 1; + + // Output all the dst array values using the indices and ratios: + int idx = 0; + for (int y = 0; y < a_DstSizeY; y++) + { + int idxLoY = a_SrcSizeX * SrcIdxY[y]; + int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); + float ry = RatioY[y]; + for (int x = 0; x < a_DstSizeX; x++) + { + // The four src corners of the current "cell": + float LoXLoY = a_Src[SrcIdxX[x] + idxLoY]; + float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY]; + float LoXHiY = a_Src[SrcIdxX[x] + idxHiY]; + float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY]; + + // Linear interpolation along the X axis: + float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x]; + float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x]; + + // Linear interpolation along the Y axis: + a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry; + idx += 1; + } + } +} + + + + + +/// Puts linearly interpolated values from one array into another array. 3D version +void LinearInterpolate3DArray( + float * a_Src, + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, + float * a_Dst, + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ +) +{ + ASSERT(a_DstSizeX > 0); + ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); + ASSERT(a_DstSizeY > 0); + ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); + ASSERT(a_DstSizeZ > 0); + ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ); + + // Calculate interpolation ratios and src indices along each axis: + float RatioX[MAX_INTERPOL_SIZEX]; + float RatioY[MAX_INTERPOL_SIZEY]; + float RatioZ[MAX_INTERPOL_SIZEZ]; + int SrcIdxX[MAX_INTERPOL_SIZEX]; + int SrcIdxY[MAX_INTERPOL_SIZEY]; + int SrcIdxZ[MAX_INTERPOL_SIZEZ]; + for (int x = 1; x < a_DstSizeX; x++) + { + SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); + RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; + } + for (int y = 1; y < a_DstSizeY; y++) + { + SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); + RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; + } + for (int z = 1; z < a_DstSizeZ; z++) + { + SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1); + RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z]; + } + + // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: + SrcIdxX[0] = 0; + RatioX[0] = 0; + SrcIdxY[0] = 0; + RatioY[0] = 0; + SrcIdxZ[0] = 0; + RatioZ[0] = 0; + SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; + RatioX[a_DstSizeX - 1] = 1; + SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; + RatioY[a_DstSizeY - 1] = 1; + SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2; + RatioZ[a_DstSizeZ - 1] = 1; + + // Output all the dst array values using the indices and ratios: + int idx = 0; + for (int z = 0; z < a_DstSizeZ; z++) + { + int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z]; + int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1); + float rz = RatioZ[z]; + for (int y = 0; y < a_DstSizeY; y++) + { + int idxLoY = a_SrcSizeX * SrcIdxY[y]; + int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); + float ry = RatioY[y]; + for (int x = 0; x < a_DstSizeX; x++) + { + // The eight src corners of the current "cell": + float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ]; + float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ]; + float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ]; + float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ]; + float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ]; + float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ]; + float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ]; + float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ]; + + // Linear interpolation along the Z axis: + float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz; + float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz; + float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz; + float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz; + + // Linear interpolation along the Y axis: + float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry; + float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry; + + // Linear interpolation along the X axis: + a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x]; + idx += 1; + } // for x + } // for y + } // for z +} + + + + + diff --git a/src/LinearInterpolation.h b/src/LinearInterpolation.h new file mode 100644 index 000000000..4b798d9bc --- /dev/null +++ b/src/LinearInterpolation.h @@ -0,0 +1,60 @@ + +// LinearInterpolation.h + +// Declares methods for linear interpolation over 1D, 2D and 3D arrays + + + + + +#pragma once + + + + + +// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays +// These arrays enforce a max size of the dest array, but the limits are settable here: +const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array +const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array +const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array + + + + + +/// Puts linearly interpolated values from one array into another array. 1D version +void LinearInterpolate1DArray( + float * a_Src, ///< Src array + int a_SrcSizeX, ///< Count of the src array + float * a_Dst, ///< Src array + int a_DstSizeX ///< Count of the dst array +); + + + + + +/// Puts linearly interpolated values from one array into another array. 2D version +void LinearInterpolate2DArray( + float * a_Src, ///< Src array, [x + a_SrcSizeX * y] + int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction + float * a_Dst, ///< Dst array, [x + a_DstSizeX * y] + int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction +); + + + + + +/// Puts linearly interpolated values from one array into another array. 3D version +void LinearInterpolate3DArray( + float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z] + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction + float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z] + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction +); + + + + diff --git a/src/LinearUpscale.h b/src/LinearUpscale.h new file mode 100644 index 000000000..b337b3219 --- /dev/null +++ b/src/LinearUpscale.h @@ -0,0 +1,244 @@ + +// LinearUpscale.h + +// Declares the functions for linearly upscaling arrays + +/* +Upscaling means that the array is divided into same-size "cells", and each cell is +linearly interpolated between its corners. The array's dimensions are therefore +1 + CellSize * NumCells, for each direction. + +Upscaling is more efficient than linear interpolation, because the cell sizes are integral +and therefore the cells' boundaries are on the array points. + +However, upscaling usually requires generating the "1 +" in each direction. + +Upscaling is implemented in templates, so that it's compatible with multiple datatypes. +Therefore, there is no cpp file. + +InPlace upscaling works on a single array and assumes that the values to work on have already +been interspersed into the array to the cell boundaries. +Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value. + +Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed. +*/ + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works in-place (input is already present at the correct output coords) +*/ +template<typename TYPE> void LinearUpscale2DArrayInPlace( + TYPE * a_Array, + int a_SizeX, int a_SizeY, // Dimensions of the array + int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction +) +{ + // First interpolate columns where the anchor points are: + int LastYCell = a_SizeY - a_AnchorStepY; + for (int y = 0; y < LastYCell; y += a_AnchorStepY) + { + int Idx = a_SizeX * y; + for (int x = 0; x < a_SizeX; x += a_AnchorStepX) + { + TYPE StartValue = a_Array[Idx]; + TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; + TYPE Diff = EndValue - StartValue; + for (int CellY = 1; CellY < a_AnchorStepY; CellY++) + { + a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY; + } // for CellY + Idx += a_AnchorStepX; + } // for x + } // for y + + // Now interpolate in rows, each row has values in the anchor columns + int LastXCell = a_SizeX - a_AnchorStepX; + for (int y = 0; y < a_SizeY; y++) + { + int Idx = a_SizeX * y; + for (int x = 0; x < LastXCell; x += a_AnchorStepX) + { + TYPE StartValue = a_Array[Idx]; + TYPE EndValue = a_Array[Idx + a_AnchorStepX]; + TYPE Diff = EndValue - StartValue; + for (int CellX = 1; CellX < a_AnchorStepX; CellX++) + { + a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; + } // for CellY + Idx += a_AnchorStepX; + } + } +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template<typename TYPE> void LinearUpscale2DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY + int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) + int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + + // Interpolate each XY cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoY = a_Src[idx]; + TYPE LoXHiY = a_Src[idx + a_SrcSizeX]; + TYPE HiXLoY = a_Src[idx + 1]; + TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY); + TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY]; + TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for x + } // for y +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template<typename TYPE> void LinearUpscale3DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) + int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + const int MAX_UPSCALE_Z = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_SrcSizeZ > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleZ > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + TYPE RatioZ[MAX_UPSCALE_Z]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + for (int z = 0; z <= a_UpscaleZ; z++) + { + RatioZ[z] = (TYPE)z / a_UpscaleZ; + } + + // Interpolate each XYZ cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1; + for (int z = 0; z < (a_SrcSizeZ - 1); z++) + { + int DstZ = z * a_UpscaleZ; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoYLoZ = a_Src[idx]; + TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY]; + TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX]; + TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXLoYLoZ = a_Src[idx + 1]; + TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX]; + TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++) + { + TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ]; + TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ]; + TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ]; + TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ); + TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY]; + TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for CellZ + } // for x + } // for y + } // for z +} + + + + + diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 000000000..a0de4531b --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,169 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Log.h" + +#include <fstream> +#include <ctime> +#include "OSSupport/IsThread.h" + +#if defined(ANDROID_NDK) + #include <android/log.h> + #include "ToJava.h" +#endif + + + + +cLog* cLog::s_Log = NULL; + +cLog::cLog(const AString & a_FileName ) + : m_File(NULL) +{ + s_Log = this; + + // create logs directory + cFile::CreateFolder(FILE_IO_PREFIX + AString("logs")); + + OpenLog((FILE_IO_PREFIX + AString("logs/") + a_FileName).c_str() ); +} + + + + + +cLog::~cLog() +{ + CloseLog(); + s_Log = NULL; +} + + + + + +cLog* cLog::GetInstance() +{ + if (s_Log != NULL) + { + return s_Log; + } + + new cLog("log.txt"); + return s_Log; +} + + + + + +void cLog::CloseLog() +{ + if( m_File ) + fclose (m_File); + m_File = 0; +} + + + + + +void cLog::OpenLog( const char* a_FileName ) +{ + if(m_File) fclose (m_File); + #ifdef _MSC_VER + fopen_s( &m_File, a_FileName, "a+" ); + #else + m_File = fopen(a_FileName, "a+" ); + #endif +} + + + + + +void cLog::ClearLog() +{ + #ifdef _MSC_VER + if( fopen_s( &m_File, "log.txt", "w" ) == 0) + fclose (m_File); + #else + m_File = fopen("log.txt", "w" ); + if( m_File ) + fclose (m_File); + #endif + m_File = 0; +} + + + + + +void cLog::Log(const char * a_Format, va_list argList) +{ + AString Message; + AppendVPrintf(Message, a_Format, argList); + + time_t rawtime; + time ( &rawtime ); + + struct tm* timeinfo; +#ifdef _MSC_VER + struct tm timeinforeal; + timeinfo = &timeinforeal; + localtime_s(timeinfo, &rawtime ); +#else + timeinfo = localtime( &rawtime ); +#endif + + AString Line; + #ifdef _DEBUG + Printf(Line, "[%04x|%02d:%02d:%02d] %s", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); + #else + Printf(Line, "[%02d:%02d:%02d] %s", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); + #endif + if (m_File) + { + fprintf(m_File, "%s\n", Line.c_str()); + fflush(m_File); + } + + // Print to console: +#if defined(ANDROID_NDK) + //__android_log_vprint(ANDROID_LOG_ERROR,"MCServer", a_Format, argList); + __android_log_print(ANDROID_LOG_ERROR, "MCServer", "%s", Line.c_str() ); + //CallJavaFunction_Void_String(g_JavaThread, "AddToLog", Line ); +#else + printf("%s", Line.c_str()); +#endif + + #if defined (_WIN32) && defined(_DEBUG) + // In a Windows Debug build, output the log to debug console as well: + OutputDebugStringA((Line + "\n").c_str()); + #endif // _WIN32 +} + + + + + +void cLog::Log(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + Log( a_Format, argList ); + va_end(argList); +} + + + + + +void cLog::SimpleLog(const char* a_String) +{ + Log("%s", a_String ); +} + + + + diff --git a/src/Log.h b/src/Log.h new file mode 100644 index 000000000..d00022c6f --- /dev/null +++ b/src/Log.h @@ -0,0 +1,30 @@ + +#pragma once + + + + + +class cLog +{ // tolua_export +private: + FILE * m_File; + static cLog * s_Log; + +public: + cLog(const AString & a_FileName); + ~cLog(); + void Log(const char* a_Format, va_list argList ); + void Log(const char* a_Format, ...); + // tolua_begin + void SimpleLog(const char* a_String); + void OpenLog( const char* a_FileName ); + void CloseLog(); + void ClearLog(); + static cLog* GetInstance(); +}; +// tolua_end + + + + diff --git a/src/LuaFunctions.h b/src/LuaFunctions.h new file mode 100644 index 000000000..0ad420881 --- /dev/null +++ b/src/LuaFunctions.h @@ -0,0 +1,17 @@ +#pragma once + +#include "MCLogger.h" +#include <time.h> +// tolua_begin + +unsigned int GetTime() +{ + return (unsigned int)time(0); +} + +std::string GetChar( std::string & a_Str, unsigned int a_Idx ) +{ + return std::string(1, a_Str[ a_Idx ]); +} + +// tolua_end diff --git a/src/LuaState.cpp b/src/LuaState.cpp new file mode 100644 index 000000000..6be1ee58c --- /dev/null +++ b/src/LuaState.cpp @@ -0,0 +1,1024 @@ + +// LuaState.cpp + +// Implements the cLuaState class representing the wrapper over lua_State *, provides associated helper functions + +#include "Globals.h" +#include "LuaState.h" + +extern "C" +{ + #include "lua/src/lualib.h" +} + +#include "tolua++.h" +#include "Bindings.h" +#include "ManualBindings.h" + +// fwd: SQLite/lsqlite3.c +extern "C" +{ + LUALIB_API int luaopen_lsqlite3(lua_State * L); +} + +// fwd: LuaExpat/lxplib.c: +extern "C" +{ + int luaopen_lxp(lua_State * L); +} + + + + + + +const cLuaState::cRet cLuaState::Return = {}; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaState: + +cLuaState::cLuaState(const AString & a_SubsystemName) : + m_LuaState(NULL), + m_IsOwned(false), + m_SubsystemName(a_SubsystemName), + m_NumCurrentFunctionArgs(-1) +{ +} + + + + + +cLuaState::cLuaState(lua_State * a_AttachState) : + m_LuaState(a_AttachState), + m_IsOwned(false), + m_SubsystemName("<attached>"), + m_NumCurrentFunctionArgs(-1) +{ +} + + + + + +cLuaState::~cLuaState() +{ + if (IsValid()) + { + if (m_IsOwned) + { + Close(); + } + else + { + Detach(); + } + } +} + + + + + +void cLuaState::Create(void) +{ + if (m_LuaState != NULL) + { + LOGWARNING("%s: Trying to create an already-existing LuaState, ignoring.", __FUNCTION__); + return; + } + m_LuaState = lua_open(); + luaL_openlibs(m_LuaState); + tolua_AllToLua_open(m_LuaState); + ManualBindings::Bind(m_LuaState); + luaopen_lsqlite3(m_LuaState); + luaopen_lxp(m_LuaState); + m_IsOwned = true; +} + + + + + +void cLuaState::Close(void) +{ + if (m_LuaState == NULL) + { + LOGWARNING("%s: Trying to close an invalid LuaState, ignoring.", __FUNCTION__); + return; + } + if (!m_IsOwned) + { + LOGWARNING( + "%s: Detected mis-use, calling Close() on an attached state (0x%p). Detaching instead.", + __FUNCTION__, m_LuaState + ); + Detach(); + return; + } + lua_close(m_LuaState); + m_LuaState = NULL; + m_IsOwned = false; +} + + + + + +void cLuaState::Attach(lua_State * a_State) +{ + if (m_LuaState != NULL) + { + LOGINFO("%s: Already contains a LuaState (0x%p), will be closed / detached.", __FUNCTION__, m_LuaState); + if (m_IsOwned) + { + Close(); + } + else + { + Detach(); + } + } + m_LuaState = a_State; + m_IsOwned = false; +} + + + + + +void cLuaState::Detach(void) +{ + if (m_LuaState == NULL) + { + return; + } + if (m_IsOwned) + { + LOGWARNING( + "%s: Detected a mis-use, calling Detach() when the state is owned. Closing the owned state (0x%p).", + __FUNCTION__, m_LuaState + ); + Close(); + return; + } + m_LuaState = NULL; +} + + + + + +bool cLuaState::LoadFile(const AString & a_FileName) +{ + ASSERT(IsValid()); + + // Load the file: + int s = luaL_loadfile(m_LuaState, a_FileName.c_str()); + if (ReportErrors(s)) + { + LOGWARNING("Can't load %s because of an error in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); + return false; + } + + // Execute the globals: + s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); + if (ReportErrors(s)) + { + LOGWARNING("Error in %s in file %s", m_SubsystemName.c_str(), a_FileName.c_str()); + return false; + } + + return true; +} + + + + + +bool cLuaState::HasFunction(const char * a_FunctionName) +{ + if (!IsValid()) + { + // This happens if cPlugin::Initialize() fails with an error + return false; + } + + lua_getglobal(m_LuaState, a_FunctionName); + bool res = (!lua_isnil(m_LuaState, -1) && lua_isfunction(m_LuaState, -1)); + lua_pop(m_LuaState, 1); + return res; +} + + + + + +bool cLuaState::PushFunction(const char * a_FunctionName) +{ + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + if (!IsValid()) + { + // This happens if cPlugin::Initialize() fails with an error + return false; + } + + lua_getglobal(m_LuaState, a_FunctionName); + if (!lua_isfunction(m_LuaState, -1)) + { + LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName); + lua_pop(m_LuaState, 1); + return false; + } + m_CurrentFunctionName.assign(a_FunctionName); + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + +bool cLuaState::PushFunction(int a_FnRef) +{ + ASSERT(IsValid()); + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() + if (!lua_isfunction(m_LuaState, -1)) + { + lua_pop(m_LuaState, 1); + return false; + } + m_CurrentFunctionName = "<callback>"; + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + +bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName) +{ + ASSERT(IsValid()); + ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack + + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref + if (!lua_istable(m_LuaState, -1)) + { + // Not a table, bail out + lua_pop(m_LuaState, 1); + return false; + } + lua_getfield(m_LuaState, -1, a_FnName); + if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1)) + { + // Not a valid function, bail out + lua_pop(m_LuaState, 2); + return false; + } + lua_remove(m_LuaState, -2); // Remove the table ref from the stack + m_CurrentFunctionName = "<table_callback>"; + m_NumCurrentFunctionArgs = 0; + return true; +} + + + + + +void cLuaState::Push(const AString & a_String) +{ + ASSERT(IsValid()); + + tolua_pushcppstring(m_LuaState, a_String); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const AStringVector & a_Vector) +{ + ASSERT(IsValid()); + + lua_createtable(m_LuaState, a_Vector.size(), 0); + int newTable = lua_gettop(m_LuaState); + int index = 1; + for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index) + { + tolua_pushstring(m_LuaState, itr->c_str()); + lua_rawseti(m_LuaState, newTable, index); + } + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::PushUserType(void * a_Object, const char * a_Type) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Object, a_Type); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(int a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(double a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const char * a_Value) +{ + ASSERT(IsValid()); + + tolua_pushstring(m_LuaState, a_Value); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(bool a_Value) +{ + ASSERT(IsValid()); + + tolua_pushboolean(m_LuaState, a_Value ? 1 : 0); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cWorld * a_World) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_World, "cWorld"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cPlayer * a_Player) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const cPlayer * a_Player) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cEntity * a_Entity) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Entity, "cEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cMonster * a_Monster) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Monster, "cMonster"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cItem * a_Item) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Item, "cItem"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cItems * a_Items) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Items, "cItems"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cClientHandle * a_Client) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Client, "cClientHandle"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cPickup * a_Pickup) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Pickup, "cPickup"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cChunkDesc * a_ChunkDesc) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_ChunkDesc, "cChunkDesc"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const cCraftingGrid * a_Grid) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const cCraftingRecipe * a_Recipe) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(TakeDamageInfo * a_TDI) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_TDI, "TakeDamageInfo"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cWindow * a_Window) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Window, "cWindow"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cPluginLua * a_Plugin) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Plugin, "cPluginLua"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const HTTPRequest * a_Request) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPRequest"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cWebAdmin * a_WebAdmin) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_WebAdmin, "cWebAdmin"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(const HTTPTemplateRequest * a_Request) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, (void *)a_Request, "HTTPTemplateRequest"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cTNTEntity * a_TNTEntity) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_TNTEntity, "cTNTEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cCreeper * a_Creeper) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(Vector3i * a_Vector) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Vector, "Vector3i"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(void * a_Ptr) +{ + ASSERT(IsValid()); + + lua_pushnil(m_LuaState); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cHopperEntity * a_Hopper) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Hopper, "cHopperEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::Push(cBlockEntity * a_BlockEntity) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_BlockEntity, "cBlockEntity"); + m_NumCurrentFunctionArgs += 1; +} + + + + + +void cLuaState::GetReturn(int a_StackPos, bool & a_ReturnedVal) +{ + a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); +} + + + + + +void cLuaState::GetReturn(int a_StackPos, AString & a_ReturnedVal) +{ + if (lua_isstring(m_LuaState, a_StackPos)) + { + a_ReturnedVal = tolua_tocppstring(m_LuaState, a_StackPos, a_ReturnedVal.c_str()); + } +} + + + + + +void cLuaState::GetReturn(int a_StackPos, int & a_ReturnedVal) +{ + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); + } +} + + + + + +void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal) +{ + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); + } +} + + + + + +bool cLuaState::CallFunction(int a_NumResults) +{ + ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first + ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); + + int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0); + if (ReportErrors(s)) + { + LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str()); + m_NumCurrentFunctionArgs = -1; + m_CurrentFunctionName.clear(); + return false; + } + m_NumCurrentFunctionArgs = -1; + m_CurrentFunctionName.clear(); + return true; +} + + + + + +bool cLuaState::CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isusertable(m_LuaState, i, a_UserTable, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isusertype(m_LuaState, i, a_UserType, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_istable(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamNumber(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isnumber(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam) +{ + ASSERT(IsValid()); + + if (a_EndParam < 0) + { + a_EndParam = a_StartParam; + } + + tolua_Error tolua_err; + for (int i = a_StartParam; i <= a_EndParam; i++) + { + if (tolua_isstring(m_LuaState, i, 0, &tolua_err)) + { + continue; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; + } // for i - Param + + // All params checked ok + return true; +} + + + + + +bool cLuaState::CheckParamEnd(int a_Param) +{ + tolua_Error tolua_err; + if (tolua_isnoobj(m_LuaState, a_Param, &tolua_err)) + { + return true; + } + // Not the correct parameter + lua_Debug entry; + VERIFY(lua_getstack(m_LuaState, 0, &entry)); + VERIFY(lua_getinfo (m_LuaState, "n", &entry)); + AString ErrMsg = Printf("#ferror in function '%s': Too many arguments.", (entry.name != NULL) ? entry.name : "?"); + tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); + return false; +} + + + + + +bool cLuaState::ReportErrors(int a_Status) +{ + return ReportErrors(m_LuaState, a_Status); +} + + + + + +bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status) +{ + if (a_Status == 0) + { + // No error to report + return false; + } + + LOGWARNING("LUA: %d - %s", a_Status, lua_tostring(a_LuaState, -1)); + lua_pop(a_LuaState, 1); + return true; +} + + + + + +void cLuaState::LogStackTrace(void) +{ + LOGWARNING("Stack trace:"); + lua_Debug entry; + int depth = 0; + while (lua_getstack(m_LuaState, depth, &entry)) + { + int status = lua_getinfo(m_LuaState, "Sln", &entry); + assert(status); + + LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); + depth++; + } + LOGWARNING("Stack trace end"); +} + + + + + +AString cLuaState::GetTypeText(int a_StackPos) +{ + int Type = lua_type(m_LuaState, a_StackPos); + switch (Type) + { + case LUA_TNONE: return "TNONE"; + case LUA_TNIL: return "TNIL"; + case LUA_TBOOLEAN: return "TBOOLEAN"; + case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA"; + case LUA_TNUMBER: return "TNUMBER"; + case LUA_TSTRING: return "TSTRING"; + case LUA_TTABLE: return "TTABLE"; + case LUA_TFUNCTION: return "TFUNCTION"; + case LUA_TUSERDATA: return "TUSERDATA"; + case LUA_TTHREAD: return "TTHREAD"; + } + return Printf("Unknown (%d)", Type); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaState::cRef: + +cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) : + m_LuaState(a_LuaState) +{ + ASSERT(m_LuaState.IsValid()); + + lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack + m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX); +} + + + + + +cLuaState::cRef::~cRef() +{ + ASSERT(m_LuaState.IsValid()); + + if (IsValid()) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); + } +} + + + + diff --git a/src/LuaState.h b/src/LuaState.h new file mode 100644 index 000000000..5e7f3d180 --- /dev/null +++ b/src/LuaState.h @@ -0,0 +1,817 @@ + +// LuaState.h + +// Declares the cLuaState class representing the wrapper over lua_State *, provides associated helper functions + +/* +The contained lua_State can be either owned or attached. +Owned lua_State is created by calling Create() and the cLuaState automatically closes the state +Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state +Attaching a state will automatically close an owned state. + +Calling a Lua function is done by pushing the function, either by PushFunction() or PushFunctionFromRegistry(), +then pushing the arguments (PushString(), PushNumber(), PushUserData() etc.) and finally +executing CallFunction(). cLuaState automatically keeps track of the number of arguments and the name of the +function (for logging purposes), which makes the call less error-prone. + +Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to +any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with +automatic resource management. +*/ + + + + +#pragma once + +extern "C" +{ + #include "lua/src/lauxlib.h" +} + + + + + +class cWorld; +class cPlayer; +class cEntity; +class cMonster; +class cItem; +class cItems; +class cClientHandle; +class cPickup; +class cChunkDesc; +class cCraftingGrid; +class cCraftingRecipe; +struct TakeDamageInfo; +class cWindow; +class cPluginLua; +struct HTTPRequest; +class cWebAdmin; +struct HTTPTemplateRequest; +class cTNTEntity; +class cCreeper; +class Vector3i; +class cHopperEntity; +class cBlockEntity; + + + + + +/// Encapsulates a Lua state and provides some syntactic sugar for common operations +class cLuaState +{ +public: + + /// Used for storing references to object in the global registry + class cRef + { + public: + /// Creates a reference in the specified LuaState for object at the specified StackPos + cRef(cLuaState & a_LuaState, int a_StackPos); + ~cRef(); + + /// Returns true if the reference is valid + bool IsValid(void) const {return (m_Ref != LUA_REFNIL); } + + /// Allows to use this class wherever an int (i. e. ref) is to be used + operator int(void) const { return m_Ref; } + + protected: + cLuaState & m_LuaState; + int m_Ref; + } ; + + + /// A dummy class that's used only to delimit function args from return values for cLuaState::Call() + class cRet + { + } ; + + static const cRet Return; // Use this constant to delimit function args from return values for cLuaState::Call() + + + /** Creates a new instance. The LuaState is not initialized. + a_SubsystemName is used for reporting problems in the console, it is "plugin %s" for plugins, + or "LuaScript" for the cLuaScript template + */ + cLuaState(const AString & a_SubsystemName); + + /** Creates a new instance. The a_AttachState is attached. + Subsystem name is set to "<attached>". + */ + explicit cLuaState(lua_State * a_AttachState); + + ~cLuaState(); + + /// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions + operator lua_State * (void) { return m_LuaState; } + + /// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor + void Create(void); + + /// Closes the m_LuaState, if not closed already + void Close(void); + + /// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor + void Attach(lua_State * a_State); + + /// Detaches a previously attached state. + void Detach(void); + + /// Returns true if the m_LuaState is valid + bool IsValid(void) const { return (m_LuaState != NULL); } + + /** Loads the specified file + Returns false and logs a warning to the console if not successful (but the LuaState is kept open). + m_SubsystemName is displayed in the warning log message. + */ + bool LoadFile(const AString & a_FileName); + + /// Returns true if a_FunctionName is a valid Lua function that can be called + bool HasFunction(const char * a_FunctionName); + + /** Pushes the function of the specified name onto the stack. + Returns true if successful. Logs a warning on failure (incl. m_SubsystemName) + */ + bool PushFunction(const char * a_FunctionName); + + /** Pushes a function that has been saved into the global registry, identified by a_FnRef. + Returns true if successful. Logs a warning on failure + */ + bool PushFunction(int a_FnRef); + + /** Pushes a function that is stored in a table ref. + Returns true if successful, false on failure. Doesn't log failure. + */ + bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName); + + /// Pushes a usertype of the specified class type onto the stack + void PushUserType(void * a_Object, const char * a_Type); + + // Push a value onto the stack + void Push(const AString & a_String); + void Push(const AStringVector & a_Vector); + void Push(int a_Value); + void Push(double a_Value); + void Push(const char * a_Value); + void Push(bool a_Value); + void Push(cWorld * a_World); + void Push(cPlayer * a_Player); + void Push(const cPlayer * a_Player); + void Push(cEntity * a_Entity); + void Push(cMonster * a_Monster); + void Push(cItem * a_Item); + void Push(cItems * a_Items); + void Push(cClientHandle * a_ClientHandle); + void Push(cPickup * a_Pickup); + void Push(cChunkDesc * a_ChunkDesc); + void Push(const cCraftingGrid * a_Grid); + void Push(const cCraftingRecipe * a_Recipe); + void Push(TakeDamageInfo * a_TDI); + void Push(cWindow * a_Window); + void Push(cPluginLua * a_Plugin); + void Push(const HTTPRequest * a_Request); + void Push(cWebAdmin * a_WebAdmin); + void Push(const HTTPTemplateRequest * a_Request); + void Push(cTNTEntity * a_TNTEntity); + void Push(cCreeper * a_Creeper); + void Push(Vector3i * a_Vector); + void Push(void * a_Ptr); + void Push(cHopperEntity * a_Hopper); + void Push(cBlockEntity * a_BlockEntity); + + /// Call any 0-param 0-return Lua function in a single line: + template <typename FnT> + bool Call(FnT a_FnName) + { + if (!PushFunction(a_FnName)) + { + return false; + } + return CallFunction(0); + } + + /// Call any 1-param 0-return Lua function in a single line: + template< + typename FnT, + typename ArgT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + return CallFunction(0); + } + + /// Call any 2-param 0-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + return CallFunction(0); + } + + /// Call any 1-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 2-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 3-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 4-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 5-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 6-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 7-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 8-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename ArgT8, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 9-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + Push(a_Arg9); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 10-param 1-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, + typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, ArgT10 a_Arg10, const cRet & a_Mark, RetT1 & a_Ret1) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + Push(a_Arg9); + Push(a_Arg10); + if (!CallFunction(1)) + { + return false; + } + GetReturn(-1, a_Ret1); + lua_pop(m_LuaState, 1); + return true; + } + + /// Call any 1-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 2-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 3-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 4-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 5-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 6-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 7-param 2-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, + typename RetT1, typename RetT2 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + if (!CallFunction(2)) + { + return false; + } + GetReturn(-2, a_Ret1); + GetReturn(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; + } + + /// Call any 7-param 3-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, + typename RetT1, typename RetT2, typename RetT3 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + if (!CallFunction(3)) + { + return false; + } + GetReturn(-3, a_Ret1); + GetReturn(-2, a_Ret2); + GetReturn(-1, a_Ret3); + lua_pop(m_LuaState, 3); + return true; + } + + /// Call any 8-param 3-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, typename ArgT8, + typename RetT1, typename RetT2, typename RetT3 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + if (!CallFunction(3)) + { + return false; + } + GetReturn(-3, a_Ret1); + GetReturn(-2, a_Ret2); + GetReturn(-1, a_Ret3); + lua_pop(m_LuaState, 3); + return true; + } + + /// Call any 9-param 5-return Lua function in a single line: + template< + typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, + typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, + typename RetT1, typename RetT2, typename RetT3, typename RetT4, typename RetT5 + > + bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3, RetT4 & a_Ret4, RetT5 & a_Ret5) + { + if (!PushFunction(a_FnName)) + { + return false; + } + Push(a_Arg1); + Push(a_Arg2); + Push(a_Arg3); + Push(a_Arg4); + Push(a_Arg5); + Push(a_Arg6); + Push(a_Arg7); + Push(a_Arg8); + Push(a_Arg9); + if (!CallFunction(5)) + { + return false; + } + GetReturn(-5, a_Ret1); + GetReturn(-4, a_Ret2); + GetReturn(-3, a_Ret3); + GetReturn(-2, a_Ret4); + GetReturn(-1, a_Ret5); + lua_pop(m_LuaState, 5); + return true; + } + + + /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, bool & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, AString & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, int & a_ReturnedVal); + + /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged + void GetReturn(int a_StackPos, double & a_ReturnedVal); + + /** + Calls the function that has been pushed onto the stack by PushFunction(), + with arguments pushed by PushXXX(). + Returns true if successful, logs a warning on failure. + */ + bool CallFunction(int a_NumReturnValues); + + /// Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions + bool CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions + bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are tables; also logs warning if not + bool CheckParamTable(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are numbers; also logs warning if not + bool CheckParamNumber(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameters on the stack are strings; also logs warning if not + bool CheckParamString(int a_StartParam, int a_EndParam = -1); + + /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) + bool CheckParamEnd(int a_Param); + + /// If the status is nonzero, prints the text on the top of Lua stack and returns true + bool ReportErrors(int status); + + /// If the status is nonzero, prints the text on the top of Lua stack and returns true + static bool ReportErrors(lua_State * a_LuaState, int status); + + /// Logs all items in the current stack trace to the server console + void LogStackTrace(void); + + /// Returns the type of the item on the specified position in the stack + AString GetTypeText(int a_StackPos); + +protected: + lua_State * m_LuaState; + + /// If true, the state is owned by this object and will be auto-Closed. False => attached state + bool m_IsOwned; + + /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" + whatever is given to the constructor + */ + AString m_SubsystemName; + + /// Name of the currently pushed function (for the Push / Call chain) + AString m_CurrentFunctionName; + + /// Number of arguments currently pushed (for the Push / Call chain) + int m_NumCurrentFunctionArgs; +} ; + + + + diff --git a/src/LuaWindow.cpp b/src/LuaWindow.cpp new file mode 100644 index 000000000..f49ba2109 --- /dev/null +++ b/src/LuaWindow.cpp @@ -0,0 +1,185 @@ + +// LuaWindow.cpp + +// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player + +#include "Globals.h" +#include "LuaWindow.h" +#include "UI/SlotArea.h" +#include "PluginLua.h" +#include "Entities/Player.h" +#include "lib/lua/src/lauxlib.h" // Needed for LUA_REFNIL + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaWindow: + +cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : + super(a_WindowType, a_Title), + m_Contents(a_SlotsX, a_SlotsY), + m_Plugin(NULL), + m_LuaRef(LUA_REFNIL), + m_OnClosingFnRef(LUA_REFNIL), + m_OnSlotChangedFnRef(LUA_REFNIL) +{ + m_Contents.AddListener(*this); + m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); + + // If appropriate, add an Armor slot area: + switch (a_WindowType) + { + case cWindow::wtInventory: + case cWindow::wtWorkbench: + { + m_SlotAreas.push_back(new cSlotAreaArmor(*this)); + break; + } + } + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +cLuaWindow::~cLuaWindow() +{ + m_Contents.RemoveListener(*this); + + // Must delete slot areas now, because they are referencing this->m_Contents and would try to access it in cWindow's + // destructor, when the member is already gone. + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + delete *itr; + } + m_SlotAreas.clear(); + + ASSERT(m_OpenedBy.empty()); +} + + + + + +void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + ASSERT(m_LuaRef == LUA_REFNIL); + m_Plugin = a_Plugin; + m_LuaRef = a_LuaRef; +} + + + + + +bool cLuaWindow::IsLuaReferenced(void) const +{ + return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL)); +} + + + + + +void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // If there already was a function, unreference it first + if (m_OnClosingFnRef != LUA_REFNIL) + { + m_Plugin->Unreference(m_OnClosingFnRef); + } + + // Store the new reference + m_Plugin = a_Plugin; + m_OnClosingFnRef = a_FnRef; +} + + + + + +void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // If there already was a function, unreference it first + if (m_OnSlotChangedFnRef != LUA_REFNIL) + { + m_Plugin->Unreference(m_OnSlotChangedFnRef); + } + + // Store the new reference + m_Plugin = a_Plugin; + m_OnSlotChangedFnRef = a_FnRef; +} + + + + + +bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + // First notify the plugin through the registered callback: + if (m_OnClosingFnRef != LUA_REFNIL) + { + ASSERT(m_Plugin != NULL); + if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) + { + // The callback disagrees (the higher levels check the CanRefuse flag compliance) + return false; + } + } + + return super::ClosedByPlayer(a_Player, a_CanRefuse); +} + + + + + +void cLuaWindow::Destroy(void) +{ + super::Destroy(); + + if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL)) + { + // The object is referenced by Lua, un-reference it + m_Plugin->Unreference(m_LuaRef); + } + + // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! + m_IsDestroyed = false; +} + + + + + +void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + if (a_ItemGrid != &m_Contents) + { + ASSERT(!"Invalid ItemGrid in callback"); + return; + } + + // If an OnSlotChanged callback has been registered, call it: + if (m_OnSlotChangedFnRef != LUA_REFNIL) + { + m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); + } +} + + + + diff --git a/src/LuaWindow.h b/src/LuaWindow.h new file mode 100644 index 000000000..4c32c263e --- /dev/null +++ b/src/LuaWindow.h @@ -0,0 +1,95 @@ + +// LuaWindow.h + +// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player + + + + + +#pragma once + +#include "UI/Window.h" +#include "ItemGrid.h" + + + + + +// fwd: PluginLua.h +class cPluginLua; + + + + + +/** A window that has been created by a Lua plugin and is handled entirely by that plugin +This object needs extra care with its lifetime management: +- It is created by Lua, so Lua expects to garbage-collect it later +- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them +To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() +delete the window, but rather leaves it dangling, with only Lua having the reference to it. +Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for +cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. +This reference needs to be unreferenced in the Destroy() function. +*/ +class cLuaWindow : // tolua_export + public cItemGrid::cListener, + // tolua_begin + public cWindow +{ + typedef cWindow super; + +public: + /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size + cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + + virtual ~cLuaWindow(); + + /// Returns the internal representation of the contents that are manipulated by Lua + cItemGrid & GetContents(void) { return m_Contents; } + + // tolua_end + + /** Sets the plugin reference and the internal Lua object reference index + used for preventing Lua's GC to collect this class while the window is open + */ + void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef); + + /// Returns true if SetLuaRef() has been called + bool IsLuaReferenced(void) const; + + /// Sets the callback function (Lua reference) to call when the window is about to close + void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef); + + /// Sets the callback function (Lua reference) to call when a slot is changed + void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); + +protected: + /// Contents of the non-inventory part + cItemGrid m_Contents; + + /// The plugin that has opened the window and owns the m_LuaRef + cPluginLua * m_Plugin; + + /// The Lua object reference, used for keeping the object alive as long as any player has the window open + int m_LuaRef; + + /// The Lua reference for the callback to call when the window is closing for any player + int m_OnClosingFnRef; + + /// The Lua reference for the callback to call when a slot has changed + int m_OnSlotChangedFnRef; + + // cWindow overrides: + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; + virtual void Destroy(void) override; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; // tolua_export + + + + + diff --git a/src/MCLogger.cpp b/src/MCLogger.cpp new file mode 100644 index 000000000..4f3e5dc0f --- /dev/null +++ b/src/MCLogger.cpp @@ -0,0 +1,261 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include <time.h> +#include "Log.h" + + + + + +cMCLogger * cMCLogger::s_MCLogger = NULL; +bool g_ShouldColorOutput = false; + +#ifdef _WIN32 + #include <io.h> // Needed for _isatty(), not available on Linux + + HANDLE g_Console = GetStdHandle(STD_OUTPUT_HANDLE); + WORD g_DefaultConsoleAttrib = 0x07; +#elif defined (__linux) && !defined(ANDROID_NDK) + #include <unistd.h> // Needed for isatty() on Linux +#endif + + + + + +cMCLogger * cMCLogger::GetInstance(void) +{ + return s_MCLogger; +} + + + + + +cMCLogger::cMCLogger(void) +{ + AString FileName; + Printf(FileName, "LOG_%d.txt", (int)time(NULL)); + InitLog(FileName); +} + + + + + +cMCLogger::cMCLogger(const AString & a_FileName) +{ + InitLog(a_FileName); +} + + + + + +cMCLogger::~cMCLogger() +{ + m_Log->Log("--- Stopped Log ---\n"); + delete m_Log; + if (this == s_MCLogger) + { + s_MCLogger = NULL; + } +} + + + + + +void cMCLogger::InitLog(const AString & a_FileName) +{ + m_Log = new cLog(a_FileName); + m_Log->Log("--- Started Log ---\n"); + + s_MCLogger = this; + + #ifdef _WIN32 + // See whether we are writing to a console the default console attrib: + g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0); + if (g_ShouldColorOutput) + { + CONSOLE_SCREEN_BUFFER_INFO sbi; + GetConsoleScreenBufferInfo(g_Console, &sbi); + g_DefaultConsoleAttrib = sbi.wAttributes; + } + #elif defined (__linux) && !defined(ANDROID_NDK) + g_ShouldColorOutput = isatty(fileno(stdout)); + // TODO: Check if the terminal supports colors, somehow? + #endif +} + + + + + +void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) +{ + switch( a_LogType ) + { + case 0: + LOG("%s", a_Text); + break; + case 1: + LOGINFO("%s", a_Text); + break; + case 2: + LOGWARN("%s", a_Text); + break; + case 3: + LOGERROR("%s", a_Text); + break; + default: + LOG("(#%d#: %s", a_LogType, a_Text); + break; + } +} + + + + + +void cMCLogger::Log(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csRegular); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::Info(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csInfo); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::Warn(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csWarning); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::Error(const char * a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor(csError); + m_Log->Log(a_Format, a_ArgList); + ResetColor(); + puts(""); +} + + + + + +void cMCLogger::SetColor(eColorScheme a_Scheme) +{ + if (!g_ShouldColorOutput) + { + return; + } + #ifdef _WIN32 + WORD Attrib = 0x07; // by default, gray on black + switch (a_Scheme) + { + case csRegular: Attrib = 0x07; break; // Gray on black + case csInfo: Attrib = 0x0e; break; // Yellow on black + case csWarning: Attrib = 0x0c; break; // Read on black + case csError: Attrib = 0xc0; break; // Black on red + default: ASSERT(!"Unhandled color scheme"); + } + SetConsoleTextAttribute(g_Console, Attrib); + #elif defined(__linux) && !defined(ANDROID_NDK) + switch (a_Scheme) + { + case csRegular: printf("\x1b[0m"); break; // Whatever the console default is + case csInfo: printf("\x1b[33;1m"); break; // Yellow on black + case csWarning: printf("\x1b[31;1m"); break; // Red on black + case csError: printf("\x1b[1;33;41;1m"); break; // Yellow on red + default: ASSERT(!"Unhandled color scheme"); + } + #endif +} + + + + + +void cMCLogger::ResetColor(void) +{ + if (!g_ShouldColorOutput) + { + return; + } + #ifdef _WIN32 + SetConsoleTextAttribute(g_Console, g_DefaultConsoleAttrib); + #elif defined(__linux) && !defined(ANDROID_NDK) + printf("\x1b[0m"); + #endif +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Global functions + +void LOG(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Log( a_Format, argList ); + va_end(argList); +} + +void LOGINFO(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Info( a_Format, argList ); + va_end(argList); +} + +void LOGWARN(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Warn( a_Format, argList ); + va_end(argList); +} + +void LOGERROR(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Error( a_Format, argList ); + va_end(argList); +} + + + + diff --git a/src/MCLogger.h b/src/MCLogger.h new file mode 100644 index 000000000..c949a4cdf --- /dev/null +++ b/src/MCLogger.h @@ -0,0 +1,84 @@ + +#pragma once + + + + +class cLog; + + + + + +class cMCLogger // tolua_export +{ // tolua_export +public: // tolua_export + /// Creates a logger with the default filename, "logs/LOG_<timestamp>.log" + cMCLogger(void); + + /// Creates a logger with the specified filename inside "logs" folder + cMCLogger(const AString & a_FileName); // tolua_export + + ~cMCLogger(); // tolua_export + + void Log(const char* a_Format, va_list a_ArgList); + void Info(const char* a_Format, va_list a_ArgList); + void Warn(const char* a_Format, va_list a_ArgList); + void Error(const char* a_Format, va_list a_ArgList); + + void LogSimple(const char* a_Text, int a_LogType = 0 ); // tolua_export + + static cMCLogger* GetInstance(); +private: + enum eColorScheme + { + csRegular, + csInfo, + csWarning, + csError, + } ; + + cCriticalSection m_CriticalSection; + cLog * m_Log; + static cMCLogger * s_MCLogger; + + + /// Sets the specified color scheme in the terminal (TODO: if coloring available) + void SetColor(eColorScheme a_Scheme); + + /// Resets the color back to whatever is the default in the terminal + void ResetColor(void); + + /// Common initialization for all constructors, creates a logfile with the specified name and assigns s_MCLogger to this + void InitLog(const AString & a_FileName); +}; // tolua_export + + + + + +extern void LOG(const char* a_Format, ...); +extern void LOGINFO(const char* a_Format, ...); +extern void LOGWARN(const char* a_Format, ...); +extern void LOGERROR(const char* a_Format, ...); + + + + + +// In debug builds, translate LOGD to LOG, otherwise leave it out altogether: +#ifdef _DEBUG + #define LOGD LOG +#else + #define LOGD(...) +#endif // _DEBUG + + + + + +#define LOGWARNING LOGWARN + + + + diff --git a/src/ManualBindings.cpp b/src/ManualBindings.cpp new file mode 100644 index 000000000..7ea382b76 --- /dev/null +++ b/src/ManualBindings.cpp @@ -0,0 +1,2285 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ManualBindings.h" +#include "tolua++.h" + +#include "Root.h" +#include "World.h" +#include "Plugin.h" +#include "PluginLua.h" +#include "PluginManager.h" +#include "Entities/Player.h" +#include "WebAdmin.h" +#include "ClientHandle.h" +#include "BlockEntities/ChestEntity.h" +#include "BlockEntities/DispenserEntity.h" +#include "BlockEntities/DropperEntity.h" +#include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" +#include "lib/md5/md5.h" +#include "LuaWindow.h" +#include "LineBlockTracer.h" + + + + + +/**************************** + * Better error reporting for Lua + **/ +int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError) +{ + // Retrieve current function name + lua_Debug entry; + VERIFY(lua_getstack(L, 0, &entry)); + VERIFY(lua_getinfo(L, "n", &entry)); + + // Insert function name into error msg + AString msg(a_pMsg); + ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); + + // Send the error to Lua + tolua_error(L, msg.c_str(), a_pToLuaError); + return 0; +} + + + + + +int lua_do_error(lua_State* L, const char * a_pFormat, ...) +{ + // Retrieve current function name + lua_Debug entry; + VERIFY(lua_getstack(L, 0, &entry)); + VERIFY(lua_getinfo(L, "n", &entry)); + + // Insert function name into error msg + AString msg(a_pFormat); + ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); + + // Copied from luaL_error and modified + va_list argp; + va_start(argp, a_pFormat); + luaL_where(L, 1); + lua_pushvfstring(L, msg.c_str(), argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + + + + + +/**************************** + * Lua bound functions with special return types + **/ + +static int tolua_StringSplit(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); + std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + + AStringVector Split = StringSplit(str, delim); + LuaState.Push(Split); + return 1; +} + + + + + +static int tolua_StringSplitAndTrim(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + std::string str = (std::string)tolua_tocppstring(LuaState, 1, 0); + std::string delim = (std::string)tolua_tocppstring(LuaState, 2, 0); + + AStringVector Split = StringSplitAndTrim(str, delim); + LuaState.Push(Split); + return 1; +} + + + + + +static int tolua_LOG(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 0 ); + return 0; +} + + + + + +static int tolua_LOGINFO(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 1 ); + return 0; +} + + + + + +static int tolua_LOGWARN(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 2 ); + return 0; +} + + + + + +static int tolua_LOGERROR(lua_State* tolua_S) +{ + const char* str = tolua_tocppstring(tolua_S,1,0); + cMCLogger::GetInstance()->LogSimple( str, 3 ); + return 0; +} + + + + + +cPluginLua * GetLuaPlugin(lua_State * L) +{ + // Get the plugin identification out of LuaState: + lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); + if (!lua_islightuserdata(L, -1)) + { + LOGWARNING("%s: cannot get plugin instance, what have you done to my Lua state?", __FUNCTION__); + lua_pop(L, 1); + return NULL; + } + cPluginLua * Plugin = (cPluginLua *)lua_topointer(L, -1); + lua_pop(L, 1); + + return Plugin; +} + + + + + +static int tolua_cFile_GetFolderContents(lua_State * tolua_S) +{ + cLuaState LuaState(tolua_S); + if ( + !LuaState.CheckParamUserTable(1, "cFile") || + !LuaState.CheckParamString (2) || + !LuaState.CheckParamEnd (3) + ) + { + return 0; + } + + AString Folder = (AString)tolua_tocppstring(LuaState, 2, 0); + + AStringVector Contents = cFile::GetFolderContents(Folder); + LuaState.Push(Contents); + return 1; +} + + + + + +template< + class Ty1, + class Ty2, + bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &) + > +static int tolua_DoWith(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 2) && (NumArgs != 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + + const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); + if ((ItemName == NULL) || (ItemName[0] == 0)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs); + } + if (!lua_isfunction( tolua_S, 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 3) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; // Abort enumeration + } + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + + bool bRetVal = (self->*Func1)(ItemName, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< + class Ty1, + class Ty2, + bool (Ty1::*Func1)(int, cItemCallback<Ty2> &) +> +static int tolua_DoWithID(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 2) && (NumArgs != 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, 0); + + int ItemID = (int)tolua_tonumber(tolua_S, 2, 0); + if (!lua_isfunction(tolua_S, 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 3) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef, int a_TableRef) : + LuaState(a_LuaState), + FuncRef(a_FuncRef), + TableRef(a_TableRef) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item + if (TableRef != LUA_REFNIL) + { + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; // Abort enumeration + } + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + + bool bRetVal = (self->*Func1)(ItemID, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< + class Ty1, + class Ty2, + bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &) +> +static int tolua_DoWithXYZ(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 4) && (NumArgs != 5)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); + } + + int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0)); + int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0)); + int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0)); + LOG("x %i y %i z %i", ItemX, ItemY, ItemZ ); + if (!lua_isfunction( tolua_S, 5)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4"); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 5) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #5"); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #4"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; // Abort enumeration + } + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + bool bRetVal = (self->*Func1)(ItemX, ItemY, ItemZ, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< class Ty1, + class Ty2, + bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &) > +static int tolua_ForEachInChunk(lua_State* tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if ((NumArgs != 3) && (NumArgs != 4)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2"); + } + + int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0)); + int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0)); + + if (!lua_isfunction( tolua_S, 4)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3"); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 4) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #4"); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #3"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean(LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + bool bRetVal = (self->*Func1)(ChunkX, ChunkZ, Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +template< class Ty1, + class Ty2, + bool (Ty1::*Func1)(cItemCallback<Ty2> &) > +static int tolua_ForEach(lua_State * tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1 && NumArgs != 2) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs); + } + + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + + if (!lua_isfunction( tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ + int TableRef = LUA_REFNIL; + if (NumArgs == 2) + { + TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (TableRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2"); + } + } + + /* table value is popped, and now function is on top of the stack */ + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + class cLuaCallback : public cItemCallback<Ty2> + { + public: + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + , TableRef( a_TableRef ) + {} + + private: + virtual bool Item(Ty2 * a_Item) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic() ); + if (TableRef != LUA_REFNIL) + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ + } + + int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + int TableRef; + } Callback(tolua_S, FuncRef, TableRef); + + bool bRetVal = (self->*Func1)(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal ); + return 1; +} + + + + + +static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType .. a_BlockSkyLight) + // Function signature: GetBlockInfo(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta, BlockSkyLight, BlockBlockLight] + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 5, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetBlockInfo'", NULL); + } + #endif + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta, BlockSkyLight, BlockBlockLight; + bool res = self->GetBlockInfo(BlockX, BlockY, BlockZ, BlockType, BlockMeta, BlockSkyLight, BlockBlockLight); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + tolua_pushnumber(tolua_S, BlockSkyLight); + tolua_pushnumber(tolua_S, BlockBlockLight); + return 5; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetBlockInfo'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_BlockType, a_BlockMeta) + // Function signature: GetBlockTypeMeta(BlockX, BlockY, BlockZ) -> BlockValid, [BlockType, BlockMeta] + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 5, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetBlockTypeMeta'", NULL); + } + #endif + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + bool res = self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + return 3; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetBlockTypeMeta'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_GetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional parameters (a_Line1 .. a_Line4) + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_isnoobj (tolua_S, 10, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'GetSignLines'", NULL); + } + #endif + { + AString Line1, Line2, Line3, Line4; + bool res = self->GetSignLines(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushstring(tolua_S, Line1.c_str()); + tolua_pushstring(tolua_S, Line2.c_str()); + tolua_pushstring(tolua_S, Line3.c_str()); + tolua_pushstring(tolua_S, Line4.c_str()); + return 5; + } + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'GetSignLines'.", &tolua_err); + return 0; + #endif +} + + + + + +static int tolua_cWorld_SetSignLines(lua_State * tolua_S) +{ + // Exported manually, because tolua would generate useless additional return values (a_Line1 .. a_Line4) + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 0, &tolua_err) || + !tolua_isnumber (tolua_S, 2, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 3, 0, &tolua_err) || + !tolua_isnumber (tolua_S, 4, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 5, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 6, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 7, 0, &tolua_err) || + !tolua_iscppstring(tolua_S, 8, 0, &tolua_err) || + !tolua_isusertype (tolua_S, 9, "cPlayer", 1, &tolua_err) || + !tolua_isnoobj (tolua_S, 10, &tolua_err) + ) + goto tolua_lerror; + else + #endif + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); + const AString Line1 = tolua_tocppstring(tolua_S, 5, 0); + const AString Line2 = tolua_tocppstring(tolua_S, 6, 0); + const AString Line3 = tolua_tocppstring(tolua_S, 7, 0); + const AString Line4 = tolua_tocppstring(tolua_S, 8, 0); + cPlayer * Player = (cPlayer *)tolua_tousertype (tolua_S, 9, NULL); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'SetSignLines' / 'UpdateSign'", NULL); + } + #endif + { + bool res = self->UpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4, Player); + tolua_pushboolean(tolua_S, res ? 1 : 0); + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'SetSignLines' / 'UpdateSign'.", &tolua_err); + return 0; + #endif +} + + + + +static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) +{ + // Exported manually, because tolua would require the out-only param a_Height to be used when calling + // Takes (a_World,) a_BlockX, a_BlockZ + // Returns Height, IsValid + #ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype (tolua_S, 1, "cWorld", 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 + { + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); + int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0); + #ifndef TOLUA_RELEASE + if (self == NULL) + { + tolua_error(tolua_S, "Invalid 'self' in function 'TryGetHeight'", NULL); + } + #endif + { + int Height = 0; + bool res = self->TryGetHeight(BlockX, BlockZ, Height); + tolua_pushnumber(tolua_S, Height); + tolua_pushboolean(tolua_S, res ? 1 : 0); + } + } + return 1; + + #ifndef TOLUA_RELEASE +tolua_lerror: + tolua_error(tolua_S, "#ferror in function 'TryGetHeight'.", &tolua_err); + return 0; + #endif +} + + + + + +class cLuaWorldTask : + public cWorld::cTask +{ +public: + cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : + m_Plugin(a_Plugin), + m_FnRef(a_FnRef) + { + } + +protected: + cPluginLua & m_Plugin; + int m_FnRef; + + // cWorld::cTask overrides: + virtual void Run(cWorld & a_World) override + { + m_Plugin.Call(m_FnRef, &a_World); + } +} ; + + + + + +static int tolua_cWorld_QueueTask(lua_State * tolua_S) +{ + // Binding for cWorld::QueueTask + // Params: function + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve the args: + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + } + if (!lua_isfunction(tolua_S, 2)) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + } + + // Create a reference to the function: + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FnRef == LUA_REFNIL) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + } + + self->QueueTask(new cLuaWorldTask(*Plugin, FnRef)); + return 0; +} + + + + + +static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) +{ + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + + const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); + + lua_newtable(tolua_S); + //lua_createtable(tolua_S, AllPlugins.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); + while(iter != AllPlugins.end()) + { + const cPlugin* Plugin = iter->second; + tolua_pushstring( tolua_S, iter->first.c_str() ); + if( Plugin != NULL ) + { + tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" ); + } + else + { + tolua_pushboolean(tolua_S, 0); + } + //lua_rawseti(tolua_S, newTable, index); + lua_rawset(tolua_S, -3); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) +{ + // Helper function for cPluginmanager:AddHook() binding + // Takes care of the new case (#121): args are HOOK_TYPE and CallbackFunction + // The arg types have already been checked + + // Retrieve the cPlugin from the LuaState: + cPluginLua * Plugin = GetLuaPlugin(S); + if (Plugin == NULL) + { + // An error message has been already printed in GetLuaPlugin() + return 0; + } + + // Retrieve and check the hook type + int HookType = (int)tolua_tonumber(S, a_ParamIdx, -1); + if (!a_PluginManager->IsValidHookType(HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); + S.LogStackTrace(); + return 0; + } + + // Add the hook to the plugin + if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); + S.LogStackTrace(); + return 0; + } + a_PluginManager->AddHook(Plugin, HookType); + + // Success + return 0; +} + + + + + +static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) +{ + // Helper function for cPluginmanager:AddHook() binding + // Takes care of the old case (#121): args are cPluginLua and HOOK_TYPE + // The arg types have already been checked + + // Retrieve and check the cPlugin parameter + cPluginLua * Plugin = (cPluginLua *)tolua_tousertype(S, a_ParamIdx, NULL); + if (Plugin == NULL) + { + LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, expected a valid cPlugin object. Hook not added"); + S.LogStackTrace(); + return 0; + } + if (Plugin != GetLuaPlugin(S)) + { + // The plugin parameter passed to us is not our stored plugin. Disallow this! + LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added."); + S.LogStackTrace(); + return 0; + } + + // Retrieve and check the hook type + int HookType = (int)tolua_tonumber(S, a_ParamIdx + 1, -1); + if (!a_PluginManager->IsValidHookType(HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); + S.LogStackTrace(); + return 0; + } + + // Get the standard name for the callback function: + const char * FnName = cPluginLua::GetHookFnName(HookType); + if (FnName == NULL) + { + LOGWARNING("cPluginManager.AddHook(): Unknown hook type (%d). Hook not added.", HookType); + S.LogStackTrace(); + return 0; + } + + // Retrieve the function to call and add it to the plugin: + lua_pushstring(S, FnName); + bool res = Plugin->AddHookRef(HookType, 1); + lua_pop(S, 1); // Pop the function off the stack + if (!res) + { + LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); + S.LogStackTrace(); + return 0; + } + a_PluginManager->AddHook(Plugin, HookType); + + // Success + return 0; +} + + + + + +static int tolua_cPluginManager_AddHook(lua_State * tolua_S) +{ + /* + Function signatures: + cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended + cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently + cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (3) old style (#121), accepted but complained about + cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121) mangled, accepted but complained about + */ + + cLuaState S(tolua_S); + cPluginManager * PlgMgr = cPluginManager::Get(); + + // If the first param is a cPluginManager, use it instead of the global one: + int ParamIdx = 1; + tolua_Error err; + if (tolua_isusertype(S, 1, "cPluginManager", 0, &err)) + { + // Style 2 or 3, retrieve the PlgMgr instance + PlgMgr = (cPluginManager *)tolua_tousertype(S, 1, NULL); + if (PlgMgr == NULL) + { + LOGWARNING("Malformed plugin, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction). Fixing the call for you."); + S.LogStackTrace(); + PlgMgr = cPluginManager::Get(); + } + ParamIdx += 1; + } + + if (lua_isnumber(S, ParamIdx) && lua_isfunction(S, ParamIdx + 1)) + { + // The next params are a number and a function, assume style 1 or 2 + return tolua_cPluginManager_AddHook_FnRef(PlgMgr, S, ParamIdx); + } + else if (tolua_isusertype(S, ParamIdx, "cPlugin", 0, &err) && lua_isnumber(S, ParamIdx + 1)) + { + // The next params are a cPlugin and a number, assume style 3 or 4 + LOGINFO("cPluginManager.AddHook(): Deprecated format used, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) instead. Fixing the call for you."); + S.LogStackTrace(); + return tolua_cPluginManager_AddHook_DefFn(PlgMgr, S, ParamIdx); + } + + AString ParamDesc; + Printf(ParamDesc, "%s, %s, %s", S.GetTypeText(1).c_str(), S.GetTypeText(2).c_str(), S.GetTypeText(3).c_str()); + LOGWARNING("cPluginManager.AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str()); + S.LogStackTrace(); + return 0; +} + + + + + +static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1) + { + LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs); + return 0; + } + + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); + return 0; + } + + if (!lua_isfunction(tolua_S, 2)) + { + LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1"); + return 0; + } + + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1"); + return 0; + } + + class cLuaCallback : public cPluginManager::cCommandEnumCallback + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + {} + + private: + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushcppstring(LuaState, a_Command); + tolua_pushcppstring(LuaState, a_Permission); + tolua_pushcppstring(LuaState, a_HelpString); + + int s = lua_pcall(LuaState, 3, 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + } Callback(tolua_S, FuncRef); + + bool bRetVal = self->ForEachCommand(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal); + return 1; +} + + + + + +static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) +{ + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs); + return 0; + } + + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); + return 0; + } + + if (!lua_isfunction(tolua_S, 2)) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1"); + return 0; + } + + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1"); + return 0; + } + + class cLuaCallback : public cPluginManager::cCommandEnumCallback + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + {} + + private: + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushcppstring(LuaState, a_Command); + tolua_pushcppstring(LuaState, a_HelpString); + + int s = lua_pcall(LuaState, 2, 1, 0); + if (cLuaState::ReportErrors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + } Callback(tolua_S, FuncRef); + + bool bRetVal = self->ForEachConsoleCommand(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal); + return 1; +} + + + + + +static int tolua_cPluginManager_BindCommand(lua_State * L) +{ + /* 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) + { + return 0; + } + + // 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_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, idx + 2)) + { + luaL_error(L, "\"BindCommand\" function expects a function as its 3rd parameter. Command-binding aborted."); + return 0; + } + 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 + int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference + if (FnRef == LUA_REFNIL) + { + LOGERROR("\"BindCommand\": Cannot create a function reference. Command \"%s\" not bound.", Command.c_str()); + return 0; + } + + if (!self->BindCommand(Command, Plugin, Permission, HelpString)) + { + // Refused. Possibly already bound. Error message has been given, display the callstack: + cLuaState LS(L); + LS.LogStackTrace(); + return 0; + } + + Plugin->BindCommand(Command, FnRef); + return 0; +} + + + + + +static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) +{ + /* Function signatures: + cPluginManager:BindConsoleCommand(Command, Function, HelpString) + cPluginManager.BindConsoleCommand(Command, Function, HelpString) -- without the "self" param + */ + + // Get the plugin identification out of LuaState: + cPluginLua * Plugin = GetLuaPlugin(L); + if (Plugin == NULL) + { + return 0; + } + + // 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_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, idx + 1)) + { + luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted."); + return 0; + } + 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 + int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference + if (FnRef == LUA_REFNIL) + { + LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str()); + return 0; + } + + if (!self->BindConsoleCommand(Command, Plugin, HelpString)) + { + // Refused. Possibly already bound. Error message has been given, display the callstack: + cLuaState LS(L); + LS.LogStackTrace(); + return 0; + } + + Plugin->BindConsoleCommand(Command, FnRef); + return 0; +} + + + + + +static int tolua_cPlayer_GetGroups(lua_State* tolua_S) +{ + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + + const cPlayer::GroupList & AllGroups = self->GetGroups(); + + lua_createtable(tolua_S, AllGroups.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPlayer::GroupList::const_iterator iter = AllGroups.begin(); + while(iter != AllGroups.end()) + { + const cGroup* Group = *iter; + tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" ); + lua_rawseti(tolua_S, newTable, index); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S) +{ + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + + cPlayer::StringList AllPermissions = self->GetResolvedPermissions(); + + lua_createtable(tolua_S, AllPermissions.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPlayer::StringList::iterator iter = AllPermissions.begin(); + while(iter != AllPermissions.end()) + { + std::string& Permission = *iter; + tolua_pushstring( tolua_S, Permission.c_str() ); + lua_rawseti(tolua_S, newTable, index); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) +{ + // Function signature: cPlayer:OpenWindow(Window) + + // Retrieve the plugin instance from the Lua state + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + return 0; + } + + // Get the parameters: + cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); + cWindow * wnd = (cWindow *)tolua_tousertype(tolua_S, 2, NULL); + if ((self == NULL) || (wnd == NULL)) + { + LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, self, wnd); + return 0; + } + + // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing + tolua_Error err; + if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err)) + { + cLuaWindow * LuaWnd = (cLuaWindow *)wnd; + // Only if not already referenced + if (!LuaWnd->IsLuaReferenced()) + { + int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (LuaRef == LUA_REFNIL) + { + LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".", + __FUNCTION__, wnd->GetWindowTitle().c_str() + ); + return 0; + } + LuaWnd->SetLuaRef(Plugin, LuaRef); + } + } + + // Open the window + self->OpenWindow(wnd); + return 0; +} + + + + + +template < + class OBJTYPE, + void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) +> +static int tolua_SetObjectCallback(lua_State * tolua_S) +{ + // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) + + // Retrieve the plugin instance from the Lua state + cPluginLua * Plugin = GetLuaPlugin(tolua_S); + if (Plugin == NULL) + { + // Warning message has already been printed by GetLuaPlugin(), bail out silently + return 0; + } + + // Get the parameters - self and the function reference: + OBJTYPE * self = (OBJTYPE *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self); + return 0; + } + int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval + if (FnRef == LUA_REFNIL) + { + LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); + return 0; + } + + // Set the callback + (self->*SetCallback)(Plugin, FnRef); + return 0; +} + + + + + +static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +{ + cPluginLua * self = (cPluginLua *)tolua_tousertype(tolua_S,1,0); + + tolua_Error tolua_err; + tolua_err.array = 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 ) + ) + { + Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + Title = ((std::string) tolua_tocppstring(tolua_S,2,0)); + } + else + { + return tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); + } + + if( Reference != LUA_REFNIL ) + { + if( !self->AddWebTab( Title.c_str(), tolua_S, Reference ) ) + { + luaL_unref( tolua_S, LUA_REGISTRYINDEX, Reference ); + } + } + else + { + LOGERROR("ERROR: cPluginLua:AddWebTab invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str() ); + } + + return 0; +} + + + + + +static int tolua_cPluginLua_AddTab(lua_State* tolua_S) +{ + cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")", + self->GetName().c_str(), self->GetDirectory().c_str() + ); + return tolua_cPluginLua_AddWebTab( tolua_S ); +} + + + + +// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1 +static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top) +{ + for(; i <= top; ++i ) + { + int t = lua_type(a_Source, i); + switch (t) { + case LUA_TSTRING: /* strings */ + { + const char * s = lua_tostring(a_Source, i); + LOGD("%i push string: %s", i, s); + tolua_pushstring(a_Destination, s); + } + break; + case LUA_TBOOLEAN: /* booleans */ + { + int b = tolua_toboolean(a_Source, i, false); + LOGD("%i push bool: %i", i, b); + tolua_pushboolean(a_Destination, b ); + } + break; + case LUA_TNUMBER: /* numbers */ + { + lua_Number d = tolua_tonumber(a_Source, i, 0); + LOGD("%i push number: %0.2f", i, d); + tolua_pushnumber(a_Destination, d ); + } + break; + case LUA_TUSERDATA: + { + const char * type = 0; + if (lua_getmetatable(a_Source,i)) + { + lua_rawget(a_Source, LUA_REGISTRYINDEX); + type = lua_tostring(a_Source, -1); + lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T + } + + // don't need tolua_tousertype we already have the type + void * ud = tolua_touserdata(a_Source, i, 0); + LOGD("%i push usertype: %p of type '%s'", i, ud, type); + if( type == 0 ) + { + LOGERROR("Call(): Something went wrong when trying to get usertype name!"); + return 0; + } + tolua_pushusertype(a_Destination, ud, type); + } + break; + default: /* other values */ + LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t)); + return 0; + } + } + return 1; +} + + + + + +static int tolua_cPlugin_Call(lua_State* tolua_S) +{ + cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + lua_State* targetState = self->GetLuaState(); + int targetTop = lua_gettop(targetState); + + int top = lua_gettop(tolua_S); + LOGD("total in stack: %i", top ); + + std::string funcName = tolua_tostring(tolua_S, 2, ""); + LOGD("Func name: %s", funcName.c_str() ); + + lua_getglobal(targetState, funcName.c_str()); + if(!lua_isfunction(targetState,-1)) + { + LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); + lua_pop(targetState,1); + return 0; + } + + if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively + { + // something went wrong, exit + return 0; + } + + int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0); + if (cLuaState::ReportErrors(targetState, s)) + { + LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() ); + return 0; + } + + int nresults = lua_gettop(targetState) - targetTop; + LOGD("num results: %i", nresults); + int ttop = lua_gettop(targetState); + if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD + { + // something went wrong, exit + return 0; + } + + lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works + + return nresults; +} + + + + + +static int tolua_md5(lua_State* tolua_S) +{ + std::string SourceString = tolua_tostring(tolua_S, 1, 0); + std::string CryptedString = md5( SourceString ); + tolua_pushstring( tolua_S, CryptedString.c_str() ); + return 1; +} + + + + + +static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, std::string >& a_StringStringMap ) +{ + lua_newtable(tolua_S); + int top = lua_gettop(tolua_S); + + for( std::map< std::string, std::string >::iterator it = a_StringStringMap.begin(); it != a_StringStringMap.end(); ++it ) + { + const char* key = it->first.c_str(); + const char* value = it->second.c_str(); + lua_pushstring(tolua_S, key); + lua_pushstring(tolua_S, value); + lua_settable(tolua_S, top); + } + + return 1; +} + + + + + +static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + return tolua_push_StringStringMap(tolua_S, self->Params); +} + + + + + +static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + return tolua_push_StringStringMap(tolua_S, self->PostParams); +} + + + + + +static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S) +{ + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + std::map< std::string, HTTPFormData >& FormData = self->FormData; + + lua_newtable(tolua_S); + int top = lua_gettop(tolua_S); + + for( std::map< std::string, HTTPFormData >::iterator it = FormData.begin(); it != FormData.end(); ++it ) + { + lua_pushstring(tolua_S, it->first.c_str() ); + tolua_pushusertype(tolua_S, &(it->second), "HTTPFormData" ); + //lua_pushlstring(tolua_S, it->second.Value.c_str(), it->second.Value.size() ); // Might contain binary data + lua_settable(tolua_S, top); + } + + return 1; +} + + + + + +static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) +{ + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0); + + const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); + + lua_createtable(tolua_S, AllPlugins.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); + while(iter != AllPlugins.end()) + { + const cWebPlugin* Plugin = *iter; + tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" ); + lua_rawseti(tolua_S, newTable, index); + ++iter; + ++index; + } + return 1; +} + + + + + +static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) +{ + cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0); + + const cWebPlugin::TabNameList & TabNames = self->GetTabNames(); + + lua_newtable(tolua_S); + int newTable = lua_gettop(tolua_S); + int index = 1; + cWebPlugin::TabNameList::const_iterator iter = TabNames.begin(); + while(iter != TabNames.end()) + { + const AString & FancyName = iter->first; + const AString & WebName = iter->second; + tolua_pushstring( tolua_S, WebName.c_str() ); // Because the WebName is supposed to be unique, use it as key + tolua_pushstring( tolua_S, FancyName.c_str() ); + // + lua_rawset(tolua_S, -3); + ++iter; + ++index; + } + return 1; +} + + + + + +static int Lua_ItemGrid_GetSlotCoords(lua_State * L) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(L, 1, "const cItemGrid", 0, &tolua_err) || + !tolua_isnumber (L, 2, 0, &tolua_err) || + !tolua_isnoobj (L, 3, &tolua_err) + ) + { + goto tolua_lerror; + } + + { + const cItemGrid * self = (const cItemGrid *)tolua_tousertype(L, 1, 0); + int SlotNum = (int)tolua_tonumber(L, 2, 0); + if (self == NULL) + { + tolua_error(L, "invalid 'self' in function 'cItemGrid:GetSlotCoords'", NULL); + return 0; + } + int X, Y; + self->GetSlotCoords(SlotNum, X, Y); + tolua_pushnumber(L, (lua_Number)X); + tolua_pushnumber(L, (lua_Number)Y); + return 2; + } + +tolua_lerror: + tolua_error(L, "#ferror in function 'cItemGrid:GetSlotCoords'.", &tolua_err); + return 0; +} + + + + + +/// Provides interface between a Lua table of callbacks and the cBlockTracer::cCallbacks +class cLuaBlockTracerCallbacks : + public cBlockTracer::cCallbacks +{ +public: + cLuaBlockTracerCallbacks(cLuaState & a_LuaState, int a_ParamNum) : + m_LuaState(a_LuaState), + m_TableRef(a_LuaState, a_ParamNum) + { + } + + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + m_LuaState.Push(a_BlockType); + m_LuaState.Push(a_BlockMeta); + m_LuaState.Push(a_EntryFace); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + m_LuaState.Push(a_EntryFace); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnOutOfWorld")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnIntoWorld")) + { + // No such function in the table, skip the callback + return false; + } + m_LuaState.Push(a_BlockX); + m_LuaState.Push(a_BlockY); + m_LuaState.Push(a_BlockZ); + if (!m_LuaState.CallFunction(1)) + { + return false; + } + bool res = false; + if (lua_isboolean(m_LuaState, -1)) + { + res = (lua_toboolean(m_LuaState, -1) != 0); + } + lua_pop(m_LuaState, 1); + return res; + } + + virtual void OnNoMoreHits(void) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoMoreHits")) + { + // No such function in the table, skip the callback + return; + } + m_LuaState.CallFunction(0); + } + + virtual void OnNoChunk(void) override + { + if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoChunk")) + { + // No such function in the table, skip the callback + return; + } + m_LuaState.CallFunction(0); + } + +protected: + cLuaState & m_LuaState; + cLuaState::cRef m_TableRef; +} ; + + + + + +static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) +{ + // cLineBlockTracer.Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ) + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamTable (2) || + !L.CheckParamNumber (3, 8) || + !L.CheckParamEnd (9) + ) + { + return 0; + } + + cWorld * World = (cWorld *)tolua_tousertype(L, 1, NULL); + cLuaBlockTracerCallbacks Callbacks(L, 2); + double StartX = tolua_tonumber(L, 3, 0); + double StartY = tolua_tonumber(L, 4, 0); + double StartZ = tolua_tonumber(L, 5, 0); + double EndX = tolua_tonumber(L, 6, 0); + double EndY = tolua_tonumber(L, 7, 0); + double EndZ = tolua_tonumber(L, 8, 0); + bool res = cLineBlockTracer::Trace(*World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ); + tolua_pushboolean(L, res ? 1 : 0); + return 1; +} + + + + + +static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cRoot") || + !L.CheckParamUserType (2, "const cItem") || + !L.CheckParamEnd (3) + ) + { + return 0; + } + + // Check the input param: + cItem * Input = (cItem *)tolua_tousertype(L, 2, NULL); + if (Input == NULL) + { + LOGWARNING("cRoot:GetFurnaceRecipe: the Input parameter is nil or missing."); + return 0; + } + + // Get the recipe for the input + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input); + if (Recipe == NULL) + { + // There is no such furnace recipe for this input, return no value + return 0; + } + + // Push the output, number of ticks and input as the three return values: + tolua_pushusertype(L, Recipe->Out, "const cItem"); + tolua_pushnumber (L, (lua_Number)(Recipe->CookTime)); + tolua_pushusertype(L, Recipe->In, "const cItem"); + return 3; +} + + + + + +static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S) +{ + // function cHopperEntity::GetOutputBlockPos() + // Exported manually because tolua would require meaningless params + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cHopperEntity") || + !L.CheckParamNumber (2) || + !L.CheckParamEnd (3) + ) + { + return 0; + } + cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL); + return 0; + } + + NIBBLETYPE a_BlockMeta = ((NIBBLETYPE)tolua_tonumber(tolua_S, 2, 0)); + int a_OutputX, a_OutputY, a_OutputZ; + bool res = self->GetOutputBlockPos(a_BlockMeta, a_OutputX, a_OutputY, a_OutputZ); + tolua_pushboolean(tolua_S, res); + if (res) + { + tolua_pushnumber(tolua_S, (lua_Number)a_OutputX); + tolua_pushnumber(tolua_S, (lua_Number)a_OutputY); + tolua_pushnumber(tolua_S, (lua_Number)a_OutputZ); + return 4; + } + return 1; +} + + + + + +void ManualBindings::Bind(lua_State * tolua_S) +{ + tolua_beginmodule(tolua_S, NULL); + tolua_function(tolua_S, "StringSplit", tolua_StringSplit); + tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); + tolua_function(tolua_S, "LOG", tolua_LOG); + tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); + tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); + tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); + tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + + tolua_beginmodule(tolua_S, "cFile"); + tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cHopperEntity"); + tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cLineBlockTracer"); + tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cRoot"); + tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith <cRoot, cPlayer, &cRoot::FindAndDoWithPlayer>); + tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>); + tolua_function(tolua_S, "ForEachWorld", tolua_ForEach<cRoot, cWorld, &cRoot::ForEachWorld>); + tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cWorld"); + tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>); + tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>); + tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>); + tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>); + tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>); + tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>); + tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>); + tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); + tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); + tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); + tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>); + tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>); + tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>); + tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>); + tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>); + tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); + tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); + tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); + tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); + tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); + tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPlugin"); + tolua_function(tolua_S, "Call", tolua_cPlugin_Call); + 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_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPlayer"); + tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups); + tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions); + tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); + tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cPluginLua"); + tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab); + tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab); + tolua_endmodule(tolua_S); + + tolua_cclass(tolua_S,"HTTPRequest","HTTPRequest","",NULL); + tolua_beginmodule(tolua_S,"HTTPRequest"); + // tolua_variable(tolua_S,"Method",tolua_get_HTTPRequest_Method,tolua_set_HTTPRequest_Method); + // tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path); + tolua_variable(tolua_S,"FormData",tolua_get_HTTPRequest_FormData,0); + tolua_variable(tolua_S,"Params",tolua_get_HTTPRequest_Params,0); + tolua_variable(tolua_S,"PostParams",tolua_get_HTTPRequest_PostParams,0); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cWebAdmin"); + tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cWebPlugin"); + tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cClientHandle"); + tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE); + tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE); + tolua_endmodule(tolua_S); + + tolua_beginmodule(tolua_S, "cItemGrid"); + tolua_function(tolua_S, "GetSlotCoords", Lua_ItemGrid_GetSlotCoords); + tolua_endmodule(tolua_S); + + tolua_function(tolua_S, "md5", tolua_md5); + + tolua_endmodule(tolua_S); +} + + + + diff --git a/src/ManualBindings.h b/src/ManualBindings.h new file mode 100644 index 000000000..e6594947e --- /dev/null +++ b/src/ManualBindings.h @@ -0,0 +1,8 @@ +#pragma once + +struct lua_State; +class ManualBindings +{ +public: + static void Bind( lua_State* tolua_S ); +};
\ No newline at end of file diff --git a/src/Matrix4f.cpp b/src/Matrix4f.cpp new file mode 100644 index 000000000..d0a407a99 --- /dev/null +++ b/src/Matrix4f.cpp @@ -0,0 +1,4 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +// _X: empty file?? diff --git a/src/Matrix4f.h b/src/Matrix4f.h new file mode 100644 index 000000000..249c92f5f --- /dev/null +++ b/src/Matrix4f.h @@ -0,0 +1,225 @@ +#pragma once + +#define _USE_MATH_DEFINES +#include <math.h> +#include "Vector3f.h" + +class Matrix4f +{ +public: + enum + { + TX=3, + TY=7, + TZ=11, + D0=0, D1=5, D2=10, D3=15, + SX=D0, SY=D1, SZ=D2, + W=D3 + }; + Matrix4f() { Identity(); } + float& operator [] ( int a_N ) { return cell[a_N]; } + void Identity() + { + cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] = + cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0; + cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; + } + void Init( Vector3f a_Pos, float a_RX, float a_RY, float a_RZ ) + { + Matrix4f t; + t.RotateX( a_RZ ); + RotateY( a_RY ); + Concatenate( t ); + t.RotateZ( a_RX ); + Concatenate( t ); + Translate( a_Pos ); + } + void RotateX( float a_RX ) + { + float sx = (float)sin( a_RX * M_PI / 180 ); + float cx = (float)cos( a_RX * M_PI / 180 ); + Identity(); + cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; + } + void RotateY( float a_RY ) + { + float sy = (float)sin( a_RY * M_PI / 180 ); + float cy = (float)cos( a_RY * M_PI / 180 ); + Identity (); + cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; + } + void RotateZ( float a_RZ ) + { + float sz = (float)sin( a_RZ * M_PI / 180 ); + float cz = (float)cos( a_RZ * M_PI / 180 ); + Identity (); + cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; + } + void Translate( Vector3f a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } + void SetTranslation( Vector3f a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; } + void Concatenate( const Matrix4f& m2 ) + { + Matrix4f res; + int c; + for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) + res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + + cell[r * 4 + 1] * m2.cell[c + 4] + + cell[r * 4 + 2] * m2.cell[c + 8] + + cell[r * 4 + 3] * m2.cell[c + 12]; + for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c]; + } + Vector3f Transform( const Vector3f& v ) const + { + float x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; + float y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; + float z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; + return Vector3f( x, y, z ); + } + void Invert() + { + Matrix4f t; + int h, i; + float tx = -cell[3], ty = -cell[7], tz = -cell[11]; + for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; + for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i]; + cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; + cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; + cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; + } + Vector3f GetXColumn() { return Vector3f( cell[0], cell[1], cell[2] ); } + Vector3f GetYColumn() { return Vector3f( cell[4], cell[5], cell[6] ); } + Vector3f GetZColumn() { return Vector3f( cell[8], cell[9], cell[10] ); } + void SetXColumn( const Vector3f & a_X ) + { + cell[0] = a_X.x; + cell[1] = a_X.y; + cell[2] = a_X.z; + } + void SetYColumn( const Vector3f & a_Y ) + { + cell[4] = a_Y.x; + cell[5] = a_Y.y; + cell[6] = a_Y.z; + } + void SetZColumn( const Vector3f & a_Z ) + { + cell[8] = a_Z.x; + cell[9] = a_Z.y; + cell[10] = a_Z.z; + } + float cell[16]; +}; + + + + + +class Matrix4d +{ +public: + enum + { + TX=3, + TY=7, + TZ=11, + D0=0, D1=5, D2=10, D3=15, + SX=D0, SY=D1, SZ=D2, + W=D3 + }; + Matrix4d() { Identity(); } + double& operator [] ( int a_N ) { return cell[a_N]; } + void Identity() + { + cell[1] = cell[2] = cell[TX] = cell[4] = cell[6] = cell[TY] = + cell[8] = cell[9] = cell[TZ] = cell[12] = cell[13] = cell[14] = 0; + cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; + } + void Init( Vector3f a_Pos, double a_RX, double a_RY, double a_RZ ) + { + Matrix4d t; + t.RotateX( a_RZ ); + RotateY( a_RY ); + Concatenate( t ); + t.RotateZ( a_RX ); + Concatenate( t ); + Translate( a_Pos ); + } + void RotateX( double a_RX ) + { + double sx = (double)sin( a_RX * M_PI / 180 ); + double cx = (double)cos( a_RX * M_PI / 180 ); + Identity(); + cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; + } + void RotateY( double a_RY ) + { + double sy = (double)sin( a_RY * M_PI / 180 ); + double cy = (double)cos( a_RY * M_PI / 180 ); + Identity (); + cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; + } + void RotateZ( double a_RZ ) + { + double sz = (double)sin( a_RZ * M_PI / 180 ); + double cz = (double)cos( a_RZ * M_PI / 180 ); + Identity (); + cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; + } + void Translate( Vector3d a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } + void SetTranslation( Vector3d a_Pos ) { cell[TX] = a_Pos.x; cell[TY] = a_Pos.y; cell[TZ] = a_Pos.z; } + void Concatenate( const Matrix4d & m2 ) + { + Matrix4d res; + int c; + for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) + res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + + cell[r * 4 + 1] * m2.cell[c + 4] + + cell[r * 4 + 2] * m2.cell[c + 8] + + cell[r * 4 + 3] * m2.cell[c + 12]; + for ( c = 0; c < 16; c++ ) cell[c] = res.cell[c]; + } + Vector3d Transform( const Vector3d & v ) const + { + double x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; + double y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; + double z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; + return Vector3d( x, y, z ); + } + void Invert() + { + Matrix4d t; + int h, i; + double tx = -cell[3], ty = -cell[7], tz = -cell[11]; + for ( h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; + for ( i = 0; i < 11; i++ ) cell[i] = t.cell[i]; + cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; + cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; + cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; + } + Vector3d GetXColumn() { return Vector3d( cell[0], cell[1], cell[2] ); } + Vector3d GetYColumn() { return Vector3d( cell[4], cell[5], cell[6] ); } + Vector3d GetZColumn() { return Vector3d( cell[8], cell[9], cell[10] ); } + void SetXColumn( const Vector3d & a_X ) + { + cell[0] = a_X.x; + cell[1] = a_X.y; + cell[2] = a_X.z; + } + void SetYColumn( const Vector3d & a_Y ) + { + cell[4] = a_Y.x; + cell[5] = a_Y.y; + cell[6] = a_Y.z; + } + void SetZColumn( const Vector3d & a_Z ) + { + cell[8] = a_Z.x; + cell[9] = a_Z.y; + cell[10] = a_Z.z; + } + double cell[16]; +} ; + + + + diff --git a/src/MemoryLeak.h b/src/MemoryLeak.h new file mode 100644 index 000000000..e9c0c34e3 --- /dev/null +++ b/src/MemoryLeak.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef _WIN32 + #ifdef _DEBUG + // Enable the CRT debugging features: + #define _CRTDBG_MAP_ALLOC + #include <stdlib.h> + #include <crtdbg.h> + + // This works only in MSVC 2010+: + #if _MSC_VER >= 1600 + // Map the new operator + #ifndef DEBUG_NEW + #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define new DEBUG_NEW + #endif // _CRTDBG_MAP_ALLOC + #endif // _MSC_VER + #endif // _DEBUG +#endif // _WIN32 diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h new file mode 100644 index 000000000..dc7134a93 --- /dev/null +++ b/src/MersenneTwister.h @@ -0,0 +1,456 @@ +// MersenneTwister.h +// Mersenne Twister random number generator -- a C++ class MTRand +// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus +// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu + +// The Mersenne Twister is an algorithm for generating random numbers. It +// was designed with consideration of the flaws in various other generators. +// The period, 2^19937-1, and the order of equidistribution, 623 dimensions, +// are far greater. The generator is also fast; it avoids multiplication and +// division, and it benefits from caches and pipelines. For more information +// see the inventors' web page at +// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + +// Reference +// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally +// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on +// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. + +// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, +// Copyright (C) 2000 - 2009, Richard J. Wagner +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The names of its contributors may not be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef MERSENNETWISTER_H +#define MERSENNETWISTER_H + +// Not thread safe (unless auto-initialization is avoided and each thread has +// its own MTRand object) + +#include <iostream> +#include <climits> +#include <cstdio> +#include <ctime> +#include <cmath> + +class MTRand { +// Data +public: + typedef long uint32; // unsigned integer type, at least 32 bits + + enum { N = 624 }; // length of state vector + enum { SAVE = N + 1 }; // length of array for save() + +protected: + enum { M = 397 }; // period parameter + + uint32 state[N]; // internal state + uint32 *pNext; // next value to get from state + int left; // number of values left before reload needed + +// Methods +public: + MTRand( const uint32 oneSeed ); // initialize with a simple uint32 + MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array + MTRand(); // auto-initialize with /dev/urandom or time() and clock() + MTRand( const MTRand& o ); // copy + + // Do NOT use for CRYPTOGRAPHY without securely hashing several returned + // values together, otherwise the generator state can be learned after + // reading 624 consecutive values. + + // Access to 32-bit random numbers + uint32 randInt(); // integer in [0,2^32-1] + uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32 + double rand(); // real number in [0,1] + double rand( const double n ); // real number in [0,n] + double randExc(); // real number in [0,1) + double randExc( const double n ); // real number in [0,n) + double randDblExc(); // real number in (0,1) + double randDblExc( const double n ); // real number in (0,n) + double operator()(); // same as rand() + + // Access to 53-bit random numbers (capacity of IEEE double precision) + double rand53(); // real number in [0,1) + + // Access to nonuniform random number distributions + double randNorm( const double mean = 0.0, const double stddev = 1.0 ); + + // Re-seeding functions with same behavior as initializers + void seed( const uint32 oneSeed ); + void seed( uint32 *const bigSeed, const uint32 seedLength = N ); + void seed(); + + // Saving and loading generator state + void save( uint32* saveArray ) const; // to array of size SAVE + void load( uint32 *const loadArray ); // from such array + friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); + friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); + MTRand& operator=( const MTRand& o ); + +protected: + void initialize( const uint32 oneSeed ); + void reload(); + uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; } + uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; } + uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; } + uint32 mixBits( const uint32 u, const uint32 v ) const + { return hiBit(u) | loBits(v); } + uint32 magic( const uint32 u ) const + { return loBit(u) ? 0x9908b0dfUL : 0x0UL; } + uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const + { return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); } + static uint32 hash( time_t t, clock_t c ); +}; + +// Functions are defined in order of usage to assist inlining + +inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) +{ + // Get a uint32 from t and c + // Better than uint32(x) in case x is floating point in [0,1] + // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) + + static uint32 differ = 0; // guarantee time-based seeds will change + + uint32 h1 = 0; + unsigned char *p = (unsigned char *) &t; + for( size_t i = 0; i < sizeof(t); ++i ) + { + h1 *= UCHAR_MAX + 2U; + h1 += p[i]; + } + uint32 h2 = 0; + p = (unsigned char *) &c; + for( size_t j = 0; j < sizeof(c); ++j ) + { + h2 *= UCHAR_MAX + 2U; + h2 += p[j]; + } + return ( h1 + differ++ ) ^ h2; +} + +inline void MTRand::initialize( const uint32 seed ) +{ + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + register uint32 *s = state; + register uint32 *r = state; + register int i = 1; + *s++ = seed & 0xffffffffUL; + for( ; i < N; ++i ) + { + *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; + r++; + } +} + +inline void MTRand::reload() +{ + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + static const int MmN = int(M) - int(N); // in case enums are unsigned + register uint32 *p = state; + register int i; + for( i = N - M; i--; ++p ) + *p = twist( p[M], p[0], p[1] ); + for( i = M; --i; ++p ) + *p = twist( p[MmN], p[0], p[1] ); + *p = twist( p[MmN], p[0], state[0] ); + + left = N, pNext = state; +} + +inline void MTRand::seed( const uint32 oneSeed ) +{ + // Seed the generator with a simple uint32 + initialize(oneSeed); + reload(); +} + +inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) +{ + // Seed the generator with an array of uint32's + // There are 2^19937-1 possible initial states. This function allows + // all of those to be accessed by providing at least 19937 bits (with a + // default seed length of N = 624 uint32's). Any bits above the lower 32 + // in each element are discarded. + // Just call seed() if you want to get array from /dev/urandom + initialize(19650218UL); + register int i = 1; + register uint32 j = 0; + register int k = ( N > seedLength ? N : seedLength ); + for( ; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); + state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; + state[i] &= 0xffffffffUL; + ++i; ++j; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + if( j >= seedLength ) j = 0; + } + for( k = N - 1; k; --k ) + { + state[i] = + state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); + state[i] -= i; + state[i] &= 0xffffffffUL; + ++i; + if( i >= N ) { state[0] = state[N-1]; i = 1; } + } + state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array + reload(); +} + +inline void MTRand::seed() +{ + // Seed the generator with an array from /dev/urandom if available + // Otherwise use a hash of time() and clock() values + + // First try getting an array from /dev/urandom + + /* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!ÕNe + FILE* urandom = fopen( "/dev/urandom", "rb" ); + if( urandom ) + { + uint32 bigSeed[N]; + register uint32 *s = bigSeed; + register int i = N; + register bool success = true; + while( success && i-- ) + success = fread( s++, sizeof(uint32), 1, urandom ); + fclose(urandom); + if( success ) { seed( bigSeed, N ); return; } + } + */ + + // Was not successful, so use time() and clock() instead + seed( hash( time(NULL), clock() ) ); +} + +inline MTRand::MTRand( const uint32 oneSeed ) + { seed(oneSeed); } + +inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) + { seed(bigSeed,seedLength); } + +inline MTRand::MTRand() + { seed(); } + +inline MTRand::MTRand( const MTRand& o ) +{ + register const uint32 *t = o.state; + register uint32 *s = state; + register int i = N; + for( ; i--; *s++ = *t++ ) {} + left = o.left; + pNext = &state[N-left]; +} + +inline MTRand::uint32 MTRand::randInt() +{ + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + + if( left == 0 ) reload(); + --left; + + register uint32 s1; + s1 = *pNext++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680UL; + s1 ^= (s1 << 15) & 0xefc60000UL; + return ( s1 ^ (s1 >> 18) ); +} + +inline MTRand::uint32 MTRand::randInt( const uint32 n ) +{ + // Find which bits are used in n + // Optimized by Magnus Jonsson (magnus@smartelectronix.com) + uint32 used = n; + used |= used >> 1; + used |= used >> 2; + used |= used >> 4; + used |= used >> 8; + used |= used >> 16; + + // Draw numbers until one is found in [0,n] + uint32 i; + do + i = randInt() & used; // toss unused bits to shorten search + while( i > n ); + return i; +} + +inline double MTRand::rand() + { return double(randInt()) * (1.0/4294967295.0); } + +inline double MTRand::rand( const double n ) + { return rand() * n; } + +inline double MTRand::randExc() + { return double(randInt()) * (1.0/4294967296.0); } + +inline double MTRand::randExc( const double n ) + { return randExc() * n; } + +inline double MTRand::randDblExc() + { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } + +inline double MTRand::randDblExc( const double n ) + { return randDblExc() * n; } + +inline double MTRand::rand53() +{ + uint32 a = randInt() >> 5, b = randInt() >> 6; + return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada +} + +inline double MTRand::randNorm( const double mean, const double stddev ) +{ + // Return a real number from a normal (Gaussian) distribution with given + // mean and standard deviation by polar form of Box-Muller transformation + double x, y, r; + do + { + x = 2.0 * rand() - 1.0; + y = 2.0 * rand() - 1.0; + r = x * x + y * y; + } + while ( r >= 1.0 || r == 0.0 ); + double s = sqrt( -2.0 * log(r) / r ); + return mean + x * s * stddev; +} + +inline double MTRand::operator()() +{ + return rand(); +} + +inline void MTRand::save( uint32* saveArray ) const +{ + register const uint32 *s = state; + register uint32 *sa = saveArray; + register int i = N; + for( ; i--; *sa++ = *s++ ) {} + *sa = left; +} + +inline void MTRand::load( uint32 *const loadArray ) +{ + register uint32 *s = state; + register uint32 *la = loadArray; + register int i = N; + for( ; i--; *s++ = *la++ ) {} + left = *la; + pNext = &state[N-left]; +} + +inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) +{ + register const MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; os << *s++ << "\t" ) {} + return os << mtrand.left; +} + +inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) +{ + register MTRand::uint32 *s = mtrand.state; + register int i = mtrand.N; + for( ; i--; is >> *s++ ) {} + is >> mtrand.left; + mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; + return is; +} + +inline MTRand& MTRand::operator=( const MTRand& o ) +{ + if( this == &o ) return (*this); + register const uint32 *t = o.state; + register uint32 *s = state; + register int i = N; + for( ; i--; *s++ = *t++ ) {} + left = o.left; + pNext = &state[N-left]; + return (*this); +} + +#endif // MERSENNETWISTER_H + +// Change log: +// +// v0.1 - First release on 15 May 2000 +// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus +// - Translated from C to C++ +// - Made completely ANSI compliant +// - Designed convenient interface for initialization, seeding, and +// obtaining numbers in default or user-defined ranges +// - Added automatic seeding from /dev/urandom or time() and clock() +// - Provided functions for saving and loading generator state +// +// v0.2 - Fixed bug which reloaded generator one step too late +// +// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew +// +// v0.4 - Removed trailing newline in saved generator format to be consistent +// with output format of built-in types +// +// v0.5 - Improved portability by replacing static const int's with enum's and +// clarifying return values in seed(); suggested by Eric Heimburg +// - Removed MAXINT constant; use 0xffffffffUL instead +// +// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits +// - Changed integer [0,n] generator to give better uniformity +// +// v0.7 - Fixed operator precedence ambiguity in reload() +// - Added access for real numbers in (0,1) and (0,n) +// +// v0.8 - Included time.h header to properly support time_t and clock_t +// +// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto +// - Allowed for seeding with arrays of any length +// - Added access for real numbers in [0,1) with 53-bit resolution +// - Added access for real numbers from normal (Gaussian) distributions +// - Increased overall speed by optimizing twist() +// - Doubled speed of integer [0,n] generation +// - Fixed out-of-range number generation on 64-bit machines +// - Improved portability by substituting literal constants for long enum's +// - Changed license from GNU LGPL to BSD +// +// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev" +// - Changed randNorm algorithm from basic to polar form for efficiency +// - Updated includes from deprecated <xxxx.h> to standard <cxxxx> forms +// - Cleaned declarations and definitions to please Intel compiler +// - Revised twist() operator to work on ones'-complement machines +// - Fixed reload() function to work when N and M are unsigned +// - Added copy constructor and copy operator from Salvador Espana diff --git a/src/MobCensus.cpp b/src/MobCensus.cpp new file mode 100644 index 000000000..66b5932bc --- /dev/null +++ b/src/MobCensus.cpp @@ -0,0 +1,92 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobCensus.h" + + + + + +void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance) +{ + m_ProximityCounter.CollectMob(a_Monster, a_Chunk, a_Distance); + m_MobFamilyCollecter.CollectMob(a_Monster); +} + + + + + +bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily) +{ + bool toReturn = true; + const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player + // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319 + // MG TODO : code the correct count + if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily)) + { + return false; + } + return true; +} + + + + + +int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily) +{ + switch (a_MobFamily) + { + case cMonster::mfHostile: return 79; + case cMonster::mfPassive: return 11; + case cMonster::mfAmbient: return 16; + case cMonster::mfWater: return 5; + } + ASSERT(!"Unhandled mob family"); + return -1; +} + + + + + +void cMobCensus::CollectSpawnableChunk(cChunk & a_Chunk) +{ + m_EligibleForSpawnChunks.insert(&a_Chunk); +} + + + + + +int cMobCensus::GetNumChunks(void) +{ + return m_EligibleForSpawnChunks.size(); +} + + + + + +cMobProximityCounter & cMobCensus::GetProximityCounter(void) +{ + return m_ProximityCounter; +} + + + + + +void cMobCensus::Logd() +{ + LOGD("Hostile mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfHostile), IsCapped(cMonster::mfHostile) ? "(capped)" : ""); + LOGD("Ambient mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfAmbient), IsCapped(cMonster::mfAmbient) ? "(capped)" : ""); + LOGD("Water mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfWater), IsCapped(cMonster::mfWater) ? "(capped)" : ""); + LOGD("Passive mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfPassive), IsCapped(cMonster::mfPassive) ? "(capped)" : ""); +} + + + + + diff --git a/src/MobCensus.h b/src/MobCensus.h new file mode 100644 index 000000000..e3892bec6 --- /dev/null +++ b/src/MobCensus.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "MobProximityCounter.h" +#include "MobFamilyCollecter.h" + + + + +// fwd: +class cChunk; +class cMonster; + + + + + +/** This class is used to collect information, for each Mob, what is the distance of the closest player +it was first being designed in order to make mobs spawn / despawn / act +as the behaviour and even life of mobs depends on the distance to closest player + +as side effect : it also collect the chunks that are elligible for spawning +as side effect 2 : it also know the caps for mobs number and can compare census to this numbers +*/ +class cMobCensus +{ +public: + /// Returns the nested proximity counter + cMobProximityCounter & GetProximityCounter(void); + + // collect an elligible Chunk for Mob Spawning + // MG TODO : code the correct rule (not loaded chunk but short distant from players) + void CollectSpawnableChunk(cChunk & a_Chunk); + + /// Collect a mob - it's distance to player, it's family ... + void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance); + + /// Returns true if the family is capped (i.e. there are more mobs of this family than max) + bool IsCapped(cMonster::eFamily a_MobFamily); + + /// log the results of census to server console + void Logd(void); + +protected : + cMobProximityCounter m_ProximityCounter; + cMobFamilyCollecter m_MobFamilyCollecter; + + std::set<cChunk *> m_EligibleForSpawnChunks; + + /// Returns the number of chunks that are elligible for spawning (for now, the loaded, valid chunks) + int GetNumChunks(); + + /// Returns the cap multiplier value of the given monster family + static int GetCapMultiplier(cMonster::eFamily a_MobFamily); +} ; + + + + diff --git a/src/MobFamilyCollecter.cpp b/src/MobFamilyCollecter.cpp new file mode 100644 index 000000000..e9c69e078 --- /dev/null +++ b/src/MobFamilyCollecter.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobFamilyCollecter.h" +#include "Mobs/Monster.h" + + + +void cMobFamilyCollecter::CollectMob(cMonster & a_Monster) +{ + cMonster::eFamily MobFamily = a_Monster.GetMobFamily(); + m_Mobs[MobFamily].insert(&a_Monster); +} + + + + + +int cMobFamilyCollecter::GetNumberOfCollectedMobs(cMonster::eFamily a_Family) +{ + return m_Mobs[a_Family].size(); +} + + + + diff --git a/src/MobFamilyCollecter.h b/src/MobFamilyCollecter.h new file mode 100644 index 000000000..6cef133b5 --- /dev/null +++ b/src/MobFamilyCollecter.h @@ -0,0 +1,39 @@ + +#pragma once + +#include <map> +#include <set> +#include "BlockID.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + + + + +// fwd: +class cChunk; + + + + + +/** This class is used to collect the list of mobs for each family +*/ +class cMobFamilyCollecter +{ +public : + typedef const std::set<cMonster::eFamily> tMobFamilyList; + + // collect a mob + void CollectMob(cMonster & a_Monster); + + // return the number of mobs for this family + int GetNumberOfCollectedMobs(cMonster::eFamily a_Family); + +protected : + std::map<cMonster::eFamily, std::set<cMonster *> > m_Mobs; + +} ; + + + + diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp new file mode 100644 index 000000000..583a71579 --- /dev/null +++ b/src/MobProximityCounter.cpp @@ -0,0 +1,83 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobProximityCounter.h" + +#include "Entities/Entity.h" +#include "Chunk.h" + +void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance) +{ +// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance); + tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster); + if (it == m_MonsterToDistance.end()) + { + sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk); + std::pair<tMonsterToDistance::iterator,bool> result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk)); + if (!result.second) + { + ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it"); + } + } + else + { + if (a_Distance < it->second.m_Distance) + { + it->second.m_Distance = a_Distance; + it->second.m_Chunk = a_Chunk; + } + } + + m_EligibleForSpawnChunks.insert(&a_Chunk); + +} + +void cMobProximityCounter::convertMaps() +{ + for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++) + { + m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk))); + } +} + +cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax) +{ + sIterablePair toReturn; + toReturn.m_Count = 0; + toReturn.m_Begin = m_DistanceToMonster.end(); + toReturn.m_End = m_DistanceToMonster.end(); + + a_DistanceMin *= a_DistanceMin;// this is because is use square distance + a_DistanceMax *= a_DistanceMax; + + if (m_DistanceToMonster.size() <= 0) + { + convertMaps(); + } + + for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++) + { + if (toReturn.m_Begin == m_DistanceToMonster.end()) + { + if (a_DistanceMin == -1 || itr->first > a_DistanceMin) + { + toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin; + } + } + + if (toReturn.m_Begin != m_DistanceToMonster.end()) + { + if (a_DistanceMax != -1 && itr->first > a_DistanceMax) + { + toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax + // Note : if we are not going through this, it's ok, toReturn.m_End will be end(); + break; + } + else + { + toReturn.m_Count ++; + } + } + } + return toReturn; +} diff --git a/src/MobProximityCounter.h b/src/MobProximityCounter.h new file mode 100644 index 000000000..8a67139aa --- /dev/null +++ b/src/MobProximityCounter.h @@ -0,0 +1,65 @@ + +#pragma once + +#include <set> + +class cChunk; +class cEntity; + + +// This class is used to collect, for each Mob, what is the distance of the closest player +// it was first being designed in order to make mobs spawn / despawn / act +// as the behaviour and even life of mobs depends on the distance to closest player +class cMobProximityCounter +{ +protected : + // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster) + struct sDistanceAndChunk + { + sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {} + double m_Distance; + cChunk& m_Chunk; + }; + struct sMonsterAndChunk + { + sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} + cEntity& m_Monster; + cChunk& m_Chunk; + }; + +public : + typedef std::map<cEntity*,sDistanceAndChunk> tMonsterToDistance; + typedef std::multimap<double,sMonsterAndChunk> tDistanceToMonster; + +protected : + // this map is filled during collection phase, it will be later transformed into DistanceToMonster + tMonsterToDistance m_MonsterToDistance; + + // this map is generated after collection phase, in order to access monster by distance to player + tDistanceToMonster m_DistanceToMonster; + + // this are the collected chunks. Used to determinate the number of elligible chunk for spawning. + std::set<cChunk*> m_EligibleForSpawnChunks; + +protected : + // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster + // that will be usefull for picking up. + void convertMaps(); + +public : + // count a mob on a specified chunk with specified distance to an unkown player + // if the distance is shortest than the one collected, this become the new closest + // distance and the chunk become the "hosting" chunk (that is the one that will perform the action) + void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance); + + // return the mobs that are within the range of distance of the closest player they are + // that means that if a mob is 30 m from a player and 150 m from another one. It will be + // in the range [0..50] but not in [100..200] + struct sIterablePair{ + tDistanceToMonster::const_iterator m_Begin; + tDistanceToMonster::const_iterator m_End; + int m_Count; + }; + sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax); + +}; diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp new file mode 100644 index 000000000..4d0b2777b --- /dev/null +++ b/src/MobSpawner.cpp @@ -0,0 +1,361 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobSpawner.h" +#include "Mobs/IncludeAllMonsters.h" + + + + + +cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set<cMonster::eType>& a_AllowedTypes) : + m_MonsterFamily(a_MonsterFamily), + m_NewPack(true), + m_MobType(cMonster::mtInvalidType) +{ + for (std::set<cMonster::eType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++) + { + if (cMonster::FamilyFromType(*itr) == a_MonsterFamily) + { + m_AllowedTypes.insert(*itr); + } + } +} + + + + + +bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType) +{ + // Packs of non-water mobs can only be centered on an air block + // Packs of water mobs can only be centered on a water block + if (m_MonsterFamily == cMonster::mfWater) + { + return IsBlockWater(a_BlockType); + } + else + { + return a_BlockType == E_BLOCK_AIR; + } +} + + + + + +void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType>& toAddIn) +{ + std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(toAdd); + if (itr != m_AllowedTypes.end()) + { + toAddIn.insert(toAdd); + } +} + + + + + +cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) +{ + std::set<cMonster::eType> allowedMobs; + + if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore) + { + addIfAllowed(cMonster::mtMooshroom, allowedMobs); + } + else if (a_Biome == biNether) + { + addIfAllowed(cMonster::mtGhast, allowedMobs); + addIfAllowed(cMonster::mtZombiePigman, allowedMobs); + addIfAllowed(cMonster::mtMagmaCube, allowedMobs); + } + else if (a_Biome == biEnd) + { + addIfAllowed(cMonster::mtEnderman, allowedMobs); + } + else + { + addIfAllowed(cMonster::mtBat, allowedMobs); + addIfAllowed(cMonster::mtSpider, allowedMobs); + addIfAllowed(cMonster::mtZombie, allowedMobs); + addIfAllowed(cMonster::mtSkeleton, allowedMobs); + addIfAllowed(cMonster::mtCreeper, allowedMobs); + addIfAllowed(cMonster::mtSquid, allowedMobs); + + if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean) + { + addIfAllowed(cMonster::mtSheep, allowedMobs); + addIfAllowed(cMonster::mtPig, allowedMobs); + addIfAllowed(cMonster::mtCow, allowedMobs); + addIfAllowed(cMonster::mtChicken, allowedMobs); + addIfAllowed(cMonster::mtEnderman, allowedMobs); + addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule + + if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills) + { + addIfAllowed(cMonster::mtWolf, allowedMobs); + } + else if (a_Biome == biJungle || a_Biome == biJungleHills) + { + addIfAllowed(cMonster::mtOcelot, allowedMobs); + } + } + } + + int allowedMobsSize = allowedMobs.size(); + if (allowedMobsSize > 0) + { + std::set<cMonster::eType>::iterator itr = allowedMobs.begin(); + int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome); + + for(int i = 0; i < iRandom; i++) + { + itr++; + } + + return *itr; + } + return cMonster::mtInvalidType; +} + + + + + +bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome) +{ + BLOCKTYPE TargetBlock; + if (m_AllowedTypes.find(a_MobType) != m_AllowedTypes.end() && a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock)) + { + NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); + NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ); + BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + + SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight); + + switch(a_MobType) + { + case cMonster::mtSquid: + { + return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); + } + + case cMonster::mtBat: + { + return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && (!g_BlockTransparent[BlockAbove]); + } + + case cMonster::mtChicken: + case cMonster::mtCow: + case cMonster::mtPig: + case cMonster::mtHorse: + case cMonster::mtSheep: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (BlockBelow == E_BLOCK_GRASS) && + (SkyLight >= 9) + ); + } + + case cMonster::mtOcelot: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + ( + (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) + ) && + (a_RelY >= 62) && + (m_Random.NextInt(3, a_Biome) != 0) + ); + } + + case cMonster::mtEnderman: + { + if (a_RelY < 250) + { + BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ); + if (BlockTop == E_BLOCK_AIR) + { + BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ); + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (BlockTop == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (SkyLight <= 7) && + (BlockLight <= 7) + ); + } + } + break; + } + + case cMonster::mtSpider: + { + bool CanSpawn = true; + bool HaveFloor = false; + for (int x = 0; x < 2; ++x) + { + for(int z = 0; z < 2; ++z) + { + CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock); + CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR); + if (!CanSpawn) + { + return false; + } + HaveFloor = ( + HaveFloor || + ( + a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) && + !g_BlockTransparent[TargetBlock] + ) + ); + } + } + return CanSpawn && HaveFloor && (SkyLight <= 7) && (BlockLight <= 7); + } + + case cMonster::mtCreeper: + case cMonster::mtSkeleton: + case cMonster::mtZombie: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (SkyLight <= 7) && + (BlockLight <= 7) && + (m_Random.NextInt(2, a_Biome) == 0) + ); + } + + case cMonster::mtSlime: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + ( + (a_RelY <= 40) || (a_Biome == biSwampland) + ) + ); + } + + case cMonster::mtGhast: + case cMonster::mtZombiePigman: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (!g_BlockTransparent[BlockBelow]) && + (m_Random.NextInt(20, a_Biome) == 0) + ); + } + + case cMonster::mtWolf: + { + return ( + (TargetBlock == E_BLOCK_GRASS) && + (BlockAbove == E_BLOCK_AIR) && + ( + (a_Biome == biTaiga) || + (a_Biome == biTaigaHills) || + (a_Biome == biForest) || + (a_Biome == biForestHills) || + (a_Biome == biColdTaiga) || + (a_Biome == biColdTaigaHills) || + (a_Biome == biTaigaM) || + (a_Biome == biMegaTaiga) || + (a_Biome == biMegaTaigaHills) + ) + ); + } + + default: + { + LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType); + return false; + } + } + } + return false; +} + + + + + +cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize) +{ + cMonster* toReturn = NULL; + if (m_NewPack) + { + m_MobType = ChooseMobType(a_Biome); + if (m_MobType == cMonster::mtInvalidType) + { + return toReturn; + } + if (m_MobType == cMonster::mtWolf) + { + a_MaxPackSize = 8; + } + else if (m_MobType == cMonster::mtGhast) + { + a_MaxPackSize = 1; + } + m_NewPack = false; + } + + // Make sure we are looking at the right chunk to spawn in + a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + + if (CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome)) + { + cMonster * newMob = cMonster::NewMonsterFromType(m_MobType); + if (newMob) + { + m_Spawned.insert(newMob); + } + toReturn = newMob; + } + return toReturn; +} + + + + + +void cMobSpawner::NewPack() +{ + m_NewPack = true; +} + + + + + +cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void) +{ + return m_Spawned; +} + + + + + +bool cMobSpawner::CanSpawnAnything(void) +{ + return !m_AllowedTypes.empty(); +} + + + + diff --git a/src/MobSpawner.h b/src/MobSpawner.h new file mode 100644 index 000000000..ea6636310 --- /dev/null +++ b/src/MobSpawner.h @@ -0,0 +1,76 @@ + +#pragma once + +#include <set> +#include "BlockID.h" +#include "ChunkDef.h" +#include "Chunk.h" +#include "FastRandom.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + + + + +// fwd: +class cChunk; + + + + + +/** This class is used to determine which monster can be spawned in which place +it is essentially static (eg. Squids spawn in water, Zombies spawn in dark places) +but it also has dynamic part depending on the world.ini settings. +*/ +class cMobSpawner +{ +public : + // constructor + // a_MobFamily is the Family of mobs that this spawner will spawn + // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set + // would result in no spawn at all + // Allowed mobs thah are not of the right Family will not be include (no warning) + cMobSpawner(cMonster::eFamily MobFamily, const std::set<cMonster::eType> & a_AllowedTypes); + + /// Check if specified block can be a Pack center for this spawner + bool CheckPackCenter(BLOCKTYPE a_BlockType); + + // Try to create a monster here + // if this is the first of a Pack : determine the type of monster + // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here + // MaxPackSize is set to the maximal size for a pack this type of mob + cMonster * TryToSpawnHere(cChunk * a_Chunk, int A_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize); + + // mark the beginning of a new Pack + // all mobs of the same Pack are the same type + void NewPack(void); + + // return true if there is at least one allowed type + bool CanSpawnAnything(void); + + typedef const std::set<cMonster *> tSpawnedContainer; + tSpawnedContainer & getSpawned(void); + +protected : + // return true if specified type of mob can spawn on specified block + bool CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, cMonster::eType a_MobType, EMCSBiome a_Biome); + + // return a random type that can spawn on specified biome. + // returns E_ENTITY_TYPE_DONOTUSE if none is possible + cMonster::eType ChooseMobType(EMCSBiome a_Biome); + + // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes + void addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType> & toAddIn); + +protected : + cMonster::eFamily m_MonsterFamily; + std::set<cMonster::eType> m_AllowedTypes; + bool m_NewPack; + cMonster::eType m_MobType; + std::set<cMonster*> m_Spawned; + cFastRandom m_Random; +} ; + + + + diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp new file mode 100644 index 000000000..cc7e7da2b --- /dev/null +++ b/src/Mobs/AggressiveMonster.cpp @@ -0,0 +1,97 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "AggressiveMonster.h" + +#include "../World.h" +#include "../Vector3f.h" +#include "../Entities/Player.h" +#include "../MersenneTwister.h" + + + + + +cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), + m_ChaseTime(999999) +{ + m_EMPersonality = AGGRESSIVE; +} + + + + + +// What to do if in Chasing State +void cAggressiveMonster::InStateChasing(float a_Dt) +{ + super::InStateChasing(a_Dt); + m_ChaseTime += a_Dt; + if (m_Target != NULL) + { + if (m_Target->IsPlayer()) + { + cPlayer * Player = (cPlayer *) m_Target; + if (Player->IsGameModeCreative()) + { + m_EMState = IDLE; + return; + } + } + + Vector3f Pos = Vector3f( GetPosition() ); + Vector3f Their = Vector3f( m_Target->GetPosition() ); + if ((Their - Pos).Length() <= m_AttackRange) + { + Attack(a_Dt); + } + MoveToPosition(Their + Vector3f(0, 0.65f, 0)); + } + else if (m_ChaseTime > 5.f) + { + m_ChaseTime = 0; + m_EMState = IDLE; + } +} + + + + + +void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity) +{ + super::EventSeePlayer(a_Entity); + m_EMState = CHASING; +} + + + + + +void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + m_SeePlayerInterval += a_Dt; + + if (m_SeePlayerInterval > 1) + { + int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally + + m_SeePlayerInterval = 0.0; + if (rem >= 2) + { + if (m_EMState == CHASING) + { + CheckEventLostPlayer(); + } + else + { + CheckEventSeePlayer(); + } + } + } +} + + diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h new file mode 100644 index 000000000..5a0d93f3d --- /dev/null +++ b/src/Mobs/AggressiveMonster.h @@ -0,0 +1,30 @@ + +#pragma once + +#include "Monster.h" + + + + + +class cAggressiveMonster : + public cMonster +{ + typedef cMonster super; + +public: + cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + virtual void Tick (float a_Dt, cChunk & a_Chunk) override; + virtual void InStateChasing(float a_Dt) override; + + virtual void EventSeePlayer(cEntity *) override; + + +protected: + float m_ChaseTime; +} ; + + + + diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp new file mode 100644 index 000000000..b9c82996b --- /dev/null +++ b/src/Mobs/Bat.cpp @@ -0,0 +1,15 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Bat.h" +#include "../Vector3d.h" +#include "../Chunk.h" + + +cBat::cBat(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) +{ +} + + diff --git a/src/Mobs/Bat.h b/src/Mobs/Bat.h new file mode 100644 index 000000000..e878d0ee8 --- /dev/null +++ b/src/Mobs/Bat.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cBat : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cBat(void); + + CLASS_PROTODEF(cBat); + + bool IsHanging(void) const {return false; } +} ; + + + + diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp new file mode 100644 index 000000000..f9c05b17a --- /dev/null +++ b/src/Mobs/Blaze.cpp @@ -0,0 +1,52 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Blaze.h" +#include "../World.h" + + + + +cBlaze::cBlaze(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) +{ +} + + + + + +void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD); +} + + + + + +void cBlaze::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + + if (m_Target != NULL && m_AttackInterval > 3.0) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + cFireChargeEntity * FireCharge = new cFireChargeEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + if (FireCharge == NULL) + { + return; + } + if (!FireCharge->Initialize(m_World)) + { + delete FireCharge; + return; + } + m_World->BroadcastSpawnEntity(*FireCharge); + m_AttackInterval = 0.0; + // ToDo: Shoot 3 fireballs instead of 1. + } +}
\ No newline at end of file diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h new file mode 100644 index 000000000..cdb3a1306 --- /dev/null +++ b/src/Mobs/Blaze.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cBlaze : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cBlaze(void); + + CLASS_PROTODEF(cBlaze); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Attack(float a_Dt) override; +} ; + + + + diff --git a/src/Mobs/Cavespider.cpp b/src/Mobs/Cavespider.cpp new file mode 100644 index 000000000..aba1ff9f5 --- /dev/null +++ b/src/Mobs/Cavespider.cpp @@ -0,0 +1,40 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cavespider.h" +#include "../World.h" + + + + + +cCavespider::cCavespider(void) : + super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5) +{ +} + + + + + +void cCavespider::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + // TODO: Check vanilla if cavespiders really get passive during the day / in daylight + m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; +} + + + + + +void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); +} + + + + diff --git a/src/Mobs/Cavespider.h b/src/Mobs/Cavespider.h new file mode 100644 index 000000000..10ea03f7b --- /dev/null +++ b/src/Mobs/Cavespider.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cCavespider : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cCavespider(void); + + CLASS_PROTODEF(cCaveSpider); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp new file mode 100644 index 000000000..087fd088a --- /dev/null +++ b/src/Mobs/Chicken.cpp @@ -0,0 +1,62 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Chicken.h" +#include "../World.h" + + + + + + + + +cChicken::cChicken(void) : + super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4), + m_EggDropTimer(0) +{ +} + + + + +void cChicken::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0)) + { + cItems Drops; + m_EggDropTimer = 0; + Drops.push_back(cItem(E_ITEM_EGG, 1)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + else if (m_EggDropTimer == 12000) + { + cItems Drops; + m_EggDropTimer = 0; + Drops.push_back(cItem(E_ITEM_EGG, 1)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + else + { + m_EggDropTimer++; + } +} + + + + + +void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER); + a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1)); +} + + + + + + + + diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h new file mode 100644 index 000000000..979c4d8a0 --- /dev/null +++ b/src/Mobs/Chicken.h @@ -0,0 +1,29 @@ +#pragma once + +#include "PassiveMonster.h" + + + + + +class cChicken : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cChicken(void); + + CLASS_PROTODEF(cChicken); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + + + int m_EggDropTimer; +} ; + + + diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp new file mode 100644 index 000000000..9eb74dac2 --- /dev/null +++ b/src/Mobs/Cow.cpp @@ -0,0 +1,45 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cow.h" +#include "../Entities/Player.h" + + + + + + + +cCow::cCow(void) : + super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) +{ +} + + + + + +void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); +} + + + + + +void cCow::OnRightClicked(cPlayer & a_Player) +{ + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET)) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + a_Player.GetInventory().AddItem(E_ITEM_MILK); + } + } +} + + + diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h new file mode 100644 index 000000000..0391d4a31 --- /dev/null +++ b/src/Mobs/Cow.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cCow : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cCow(); + + CLASS_PROTODEF(cCow); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp new file mode 100644 index 000000000..4e11ae13e --- /dev/null +++ b/src/Mobs/Creeper.cpp @@ -0,0 +1,47 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Creeper.h" +#include "../World.h" + + + + + +cCreeper::cCreeper(void) : + super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8), + m_bIsBlowing(false), + m_bIsCharged(false) +{ +} + + + + + +void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); + + // TODO Check if killed by a skeleton, then drop random music disk +} + + + + + +void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + + if (a_TDI.DamageType == dtLightning) + { + m_bIsCharged = true; + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h new file mode 100644 index 000000000..c3d4edeae --- /dev/null +++ b/src/Mobs/Creeper.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cCreeper : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cCreeper(void); + + CLASS_PROTODEF(cCreeper); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + bool IsBlowing(void) const {return m_bIsBlowing; } + bool IsCharged(void) const {return m_bIsCharged; } + +private: + + bool m_bIsBlowing, m_bIsCharged; + +} ; + + + + diff --git a/src/Mobs/EnderDragon.cpp b/src/Mobs/EnderDragon.cpp new file mode 100644 index 000000000..acd81cde1 --- /dev/null +++ b/src/Mobs/EnderDragon.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "EnderDragon.h" + + + + + +cEnderDragon::cEnderDragon(void) : + // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand + super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0) +{ +} + + + + + +void cEnderDragon::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + return; +} + + + + diff --git a/src/Mobs/EnderDragon.h b/src/Mobs/EnderDragon.h new file mode 100644 index 000000000..77177edfe --- /dev/null +++ b/src/Mobs/EnderDragon.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cEnderDragon : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cEnderDragon(void); + + CLASS_PROTODEF(cEnderDragon); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp new file mode 100644 index 000000000..a784131e4 --- /dev/null +++ b/src/Mobs/Enderman.cpp @@ -0,0 +1,29 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Enderman.h" + + + + + +cEnderman::cEnderman(void) : + super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9), + m_bIsScreaming(false), + CarriedBlock(E_BLOCK_AIR), + CarriedMeta(0) +{ +} + + + + + +void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL); +} + + + + diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h new file mode 100644 index 000000000..32e40e70b --- /dev/null +++ b/src/Mobs/Enderman.h @@ -0,0 +1,36 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cEnderman : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cEnderman(void); + + CLASS_PROTODEF(cEnderman); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsScreaming(void) const {return m_bIsScreaming; } + BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } + NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } + +private: + + bool m_bIsScreaming; + BLOCKTYPE CarriedBlock; + NIBBLETYPE CarriedMeta; + +} ; + + + + diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp new file mode 100644 index 000000000..96a29b2d8 --- /dev/null +++ b/src/Mobs/Ghast.cpp @@ -0,0 +1,54 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Ghast.h" +#include "../World.h" + + + + +cGhast::cGhast(void) : + super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4) +{ +} + + + + + +void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR); +} + + + + + +void cGhast::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + + if (m_Target != NULL && m_AttackInterval > 3.0) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + cGhastFireballEntity * GhastBall = new cGhastFireballEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + if (GhastBall == NULL) + { + return; + } + if (!GhastBall->Initialize(m_World)) + { + delete GhastBall; + return; + } + m_World->BroadcastSpawnEntity(*GhastBall); + m_AttackInterval = 0.0; + } +} + + + diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h new file mode 100644 index 000000000..43e8bedb6 --- /dev/null +++ b/src/Mobs/Ghast.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGhast : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGhast(void); + + CLASS_PROTODEF(cGhast); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Attack(float a_Dt) override; + + bool IsCharging(void) const {return false; } +} ; + + + + diff --git a/src/Mobs/Giant.cpp b/src/Mobs/Giant.cpp new file mode 100644 index 000000000..bbcad46f0 --- /dev/null +++ b/src/Mobs/Giant.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Giant.h" + + + + + +cGiant::cGiant(void) : + super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 3.6, 10.8) +{ + +} + + + + + +void cGiant::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 10, 50, E_ITEM_ROTTEN_FLESH); +} + + + + diff --git a/src/Mobs/Giant.h b/src/Mobs/Giant.h new file mode 100644 index 000000000..356dd4352 --- /dev/null +++ b/src/Mobs/Giant.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGiant : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGiant(void); + + CLASS_PROTODEF(cGiant); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp new file mode 100644 index 000000000..bb9a4e3f6 --- /dev/null +++ b/src/Mobs/Horse.cpp @@ -0,0 +1,152 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Horse.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : + super("Horse", mtHorse, "mob.horse.hit", "mob.horse.death", 1.4, 1.6), + m_bHasChest(false), + m_bIsEating(false), + m_bIsRearing(false), + m_bIsMouthOpen(false), + m_bIsTame(false), + m_bIsSaddled(false), + m_Type(Type), + m_Color(Color), + m_Style(Style), + m_Armour(0), + m_TimesToTame(TameTimes), + m_TameAttemptTimes(0), + m_RearTickCount(0) +{ +} + + + + + +void cHorse::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (!m_bIsMouthOpen) + { + if (m_World->GetTickRandomNumber(50) == 25) + { + m_bIsMouthOpen = true; + } + } + else + { + if (m_World->GetTickRandomNumber(10) == 5) + { + m_bIsMouthOpen = false; + } + } + + if ((m_Attachee != NULL) && (!m_bIsTame)) + { + if (m_TameAttemptTimes < m_TimesToTame) + { + if (m_World->GetTickRandomNumber(50) == 25) + { + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 0); + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 2); + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 6); + m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 8); + + m_Attachee->Detach(); + m_bIsRearing = true; + } + } + else + { + m_bIsTame = true; + } + } + + if (m_bIsRearing) + { + if (m_RearTickCount == 20) + { + m_bIsRearing = false; + m_RearTickCount = 0; + } + else + { + m_RearTickCount++; + } + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cHorse::OnRightClicked(cPlayer & a_Player) +{ + if (!m_bIsSaddled && m_bIsTame) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + // Saddle the horse: + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } + else if (!a_Player.GetEquippedItem().IsEmpty()) + { + // The horse doesn't like being hit, make it rear: + m_bIsRearing = true; + m_RearTickCount = 0; + } + } + else + { + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + return; + } + + m_Attachee->Detach(); + } + + m_TameAttemptTimes++; + a_Player.AttachTo(this); + } +} + + + + + +void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + if (m_bIsSaddled) + { + a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + } +} + + + + diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h new file mode 100644 index 000000000..be0c23f9b --- /dev/null +++ b/src/Mobs/Horse.h @@ -0,0 +1,44 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cHorse : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cHorse(int Type, int Color, int Style, int TameTimes); + + CLASS_PROTODEF(cHorse); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + bool IsSaddled (void) const {return m_bIsSaddled; } + bool IsChested (void) const {return m_bHasChest; } + bool IsEating (void) const {return m_bIsEating; } + bool IsRearing (void) const {return m_bIsRearing; } + bool IsMthOpen (void) const {return m_bIsMouthOpen; } + bool IsTame (void) const {return m_bIsTame; } + int GetHorseType (void) const {return m_Type; } + int GetHorseColor (void) const {return m_Color; } + int GetHorseStyle (void) const {return m_Style; } + int GetHorseArmour (void) const {return m_Armour;} + +private: + + bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; + int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; + +} ; + + + + diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h new file mode 100644 index 000000000..1b436a11f --- /dev/null +++ b/src/Mobs/IncludeAllMonsters.h @@ -0,0 +1,29 @@ +#include "Bat.h" +#include "Blaze.h" +#include "Cavespider.h" +#include "Chicken.h" +#include "Cow.h" +#include "Creeper.h" +#include "Enderman.h" +#include "EnderDragon.h" +#include "Ghast.h" +#include "Giant.h" +#include "Horse.h" +#include "IronGolem.h" +#include "Magmacube.h" +#include "Mooshroom.h" +#include "Ocelot.h" +#include "Pig.h" +#include "Sheep.h" +#include "Silverfish.h" +#include "Skeleton.h" +#include "Slime.h" +#include "SnowGolem.h" +#include "Spider.h" +#include "Squid.h" +#include "Villager.h" +#include "Witch.h" +#include "Wither.h" +#include "Wolf.h" +#include "Zombie.h" +#include "Zombiepigman.h" diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp new file mode 100644 index 000000000..47c961098 --- /dev/null +++ b/src/Mobs/IronGolem.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "IronGolem.h" + + + + + +cIronGolem::cIronGolem(void) : + super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) +{ +} + + + + + +void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON); +} + + + + diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h new file mode 100644 index 000000000..d49ff4cab --- /dev/null +++ b/src/Mobs/IronGolem.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cIronGolem : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cIronGolem(void); + + CLASS_PROTODEF(cIronGolem); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Magmacube.cpp b/src/Mobs/Magmacube.cpp new file mode 100644 index 000000000..86447ff6b --- /dev/null +++ b/src/Mobs/Magmacube.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Magmacube.h" + + + + + +cMagmaCube::cMagmaCube(int a_Size) : + super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), + m_Size(a_Size) +{ +} + + + + + +void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); +} + + + + diff --git a/src/Mobs/Magmacube.h b/src/Mobs/Magmacube.h new file mode 100644 index 000000000..130952970 --- /dev/null +++ b/src/Mobs/Magmacube.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cMagmaCube : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest + cMagmaCube(int a_Size); + + CLASS_PROTODEF(cMagmaCube); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + int GetSize(void) const { return m_Size; } + +protected: + + /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest + int m_Size; +} ; + + + + diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp new file mode 100644 index 000000000..091623c8a --- /dev/null +++ b/src/Mobs/Monster.cpp @@ -0,0 +1,813 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "IncludeAllMonsters.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Entities/ExpOrb.h" +#include "../Defines.h" +#include "../MonsterConfig.h" +#include "../MersenneTwister.h" + +#include "../Vector3f.h" +#include "../Vector3i.h" +#include "../Vector3d.h" +#include "../Tracer.h" +#include "../Chunk.h" +#include "../FastRandom.h" + + + + + +/** Map for eType <-> string +Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType() +The strings need to be lowercase (for more efficient comparisons in StringToMobType()) +*/ +static const struct +{ + cMonster::eType m_Type; + const char * m_lcName; +} g_MobTypeNames[] = +{ + {cMonster::mtBat, "bat"}, + {cMonster::mtBlaze, "blaze"}, + {cMonster::mtCaveSpider, "cavespider"}, + {cMonster::mtChicken, "chicken"}, + {cMonster::mtCow, "cow"}, + {cMonster::mtCreeper, "creeper"}, + {cMonster::mtEnderman, "enderman"}, + {cMonster::mtGhast, "ghast"}, + {cMonster::mtHorse, "horse"}, + {cMonster::mtMagmaCube, "magmacube"}, + {cMonster::mtMooshroom, "mooshroom"}, + {cMonster::mtOcelot, "ocelot"}, + {cMonster::mtPig, "pig"}, + {cMonster::mtSheep, "sheep"}, + {cMonster::mtSilverfish, "silverfish"}, + {cMonster::mtSkeleton, "skeleton"}, + {cMonster::mtSlime, "slime"}, + {cMonster::mtSpider, "spider"}, + {cMonster::mtSquid, "squid"}, + {cMonster::mtVillager, "villager"}, + {cMonster::mtWitch, "witch"}, + {cMonster::mtWolf, "wolf"}, + {cMonster::mtZombie, "zombie"}, + {cMonster::mtZombiePigman, "zombiepigman"}, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMonster: + +cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) + : super(etMonster, a_Width, a_Height) + , m_Target(NULL) + , m_AttackRate(3) + , idle_interval(0) + , m_bMovingToDestination(false) + , m_DestinationTime( 0 ) + , m_DestroyTimer( 0 ) + , m_Jump(0) + , m_MobType(a_MobType) + , m_SoundHurt(a_SoundHurt) + , m_SoundDeath(a_SoundDeath) + , m_EMState(IDLE) + , m_SightDistance(25) + , m_SeePlayerInterval (0) + , m_EMPersonality(AGGRESSIVE) + , m_AttackDamage(1.0f) + , m_AttackRange(2.0f) + , m_AttackInterval(0) + , m_BurnsInDaylight(false) +{ + if (!a_ConfigName.empty()) + { + GetMonsterConfig(a_ConfigName); + } +} + + + + + +void cMonster::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendSpawnMob(*this); +} + + + + + +void cMonster::MoveToPosition( const Vector3f & a_Position ) +{ + m_bMovingToDestination = true; + + m_Destination = a_Position; +} + + + + + +bool cMonster::ReachedDestination() +{ + Vector3f Distance = (m_Destination) - GetPosition(); + if( Distance.SqrLength() < 2.f ) + return true; + + return false; +} + + + + + +void cMonster::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (m_Health <= 0) + { + // The mob is dead, but we're still animating the "puff" they leave when they die + m_DestroyTimer += a_Dt / 1000; + if (m_DestroyTimer > 1) + { + Destroy(true); + } + return; + } + + // Burning in daylight + HandleDaylightBurning(a_Chunk); + + HandlePhysics(a_Dt,a_Chunk); + BroadcastMovementUpdate(); + + a_Dt /= 1000; + + if (m_bMovingToDestination) + { + Vector3f Pos( GetPosition() ); + Vector3f Distance = m_Destination - Pos; + if( !ReachedDestination() ) + { + Distance.y = 0; + Distance.Normalize(); + Distance *= 3; + SetSpeedX( Distance.x ); + SetSpeedZ( Distance.z ); + + if (m_EMState == ESCAPING) + { //Runs Faster when escaping :D otherwise they just walk away + SetSpeedX (GetSpeedX() * 2.f); + SetSpeedZ (GetSpeedZ() * 2.f); + } + } + else + { + m_bMovingToDestination = false; + } + + if( GetSpeed().SqrLength() > 0.f ) + { + if( m_bOnGround ) + { + Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy(); + Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed; + int NextHeight; + if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight)) + { + // The chunk at NextBlock is not loaded + return; + } + if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 ) + { + m_bOnGround = false; + SetSpeedY(5.f); // Jump!! + } + } + } + } + + Vector3d Distance = m_Destination - GetPosition(); + if (Distance.SqrLength() > 0.1f) + { + double Rotation, Pitch; + Distance.Normalize(); + VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch ); + SetHeadYaw (Rotation); + SetRotation( Rotation ); + SetPitch( -Pitch ); + } + + switch (m_EMState) + { + case IDLE: + { + // If enemy passive we ignore checks for player visibility + InStateIdle(a_Dt); + break; + } + + case CHASING: + { + // If we do not see a player anymore skip chasing action + InStateChasing(a_Dt); + break; + } + + case ESCAPING: + { + InStateEscaping(a_Dt); + break; + } + } // switch (m_EMState) +} + + + + + + +void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + if (a_TDI.Attacker != NULL) + { + m_Target = a_TDI.Attacker; + AddReference(m_Target); + } +} + + + + + +void cMonster::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + if (m_SoundHurt != "") + { + m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + } + int Reward; + switch (m_MobType) + { + // Animals + case cMonster::mtChicken: + case cMonster::mtCow: + case cMonster::mtHorse: + case cMonster::mtPig: + case cMonster::mtSheep: + case cMonster::mtSquid: + case cMonster::mtMooshroom: + case cMonster::mtOcelot: + case cMonster::mtWolf: + { + Reward = m_World->GetTickRandomNumber(2) + 1; + } + + // Monsters + case cMonster::mtCaveSpider: + case cMonster::mtCreeper: + case cMonster::mtEnderman: + case cMonster::mtGhast: + case cMonster::mtSilverfish: + case cMonster::mtSkeleton: + case cMonster::mtSpider: + case cMonster::mtWitch: + case cMonster::mtZombie: + case cMonster::mtZombiePigman: + case cMonster::mtSlime: + case cMonster::mtMagmaCube: + { + Reward = 6 + (m_World->GetTickRandomNumber(2)); + } + case cMonster::mtBlaze: + { + Reward = 10; + } + + // Bosses + case cMonster::mtEnderDragon: + { + Reward = 12000; + } + case cMonster::mtWither: + { + Reward = 50; + } + + default: + { + Reward = 0; + } + } + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); + m_DestroyTimer = 0; +} + + + + + +//----State Logic + +const char *cMonster::GetState() +{ + switch(m_EMState) + { + case IDLE: return "Idle"; + case ATTACKING: return "Attacking"; + case CHASING: return "Chasing"; + default: return "Unknown"; + } +} + + + + + +// for debugging +void cMonster::SetState(const AString & a_State) +{ + if (a_State.compare("Idle") == 0) + { + m_EMState = IDLE; + } + else if (a_State.compare("Attacking") == 0) + { + m_EMState = ATTACKING; + } + else if (a_State.compare("Chasing") == 0) + { + m_EMState = CHASING; + } + else + { + LOGD("cMonster::SetState(): Invalid state"); + ASSERT(!"Invalid state"); + } +} + + + + + +//Checks to see if EventSeePlayer should be fired +//monster sez: Do I see the player +void cMonster::CheckEventSeePlayer(void) +{ + // TODO: Rewrite this to use cWorld's DoWithPlayers() + cPlayer * Closest = FindClosestPlayer(); + + if (Closest != NULL) + { + EventSeePlayer(Closest); + } +} + + + + + +void cMonster::CheckEventLostPlayer(void) +{ + Vector3f pos; + cTracer LineOfSight(GetWorld()); + + if (m_Target != NULL) + { + pos = m_Target->GetPosition(); + if ((pos - GetPosition()).Length() > m_SightDistance || LineOfSight.Trace(GetPosition(),(pos - GetPosition()), (int)(pos - GetPosition()).Length())) + { + EventLosePlayer(); + } + } + else + { + EventLosePlayer(); + } +} + + + + + +// What to do if player is seen +// default to change state to chasing +void cMonster::EventSeePlayer(cEntity * a_SeenPlayer) +{ + m_Target = a_SeenPlayer; + AddReference(m_Target); +} + + + + + +void cMonster::EventLosePlayer(void) +{ + Dereference(m_Target); + m_Target = NULL; + m_EMState = IDLE; +} + + + + + +// What to do if in Idle State +void cMonster::InStateIdle(float a_Dt) +{ + idle_interval += a_Dt; + if (idle_interval > 1) + { + // at this interval the results are predictable + int rem = m_World->GetTickRandomNumber(6) + 1; + // LOGD("Moving: int: %3.3f rem: %i",idle_interval,rem); + idle_interval -= 1; // So nothing gets dropped when the server hangs for a few seconds + Vector3f Dist; + Dist.x = (float)(m_World->GetTickRandomNumber(10) - 5); + Dist.z = (float)(m_World->GetTickRandomNumber(10) - 5); + if ((Dist.SqrLength() > 2) && (rem >= 3)) + { + m_Destination.x = (float)(GetPosX() + Dist.x); + m_Destination.z = (float)(GetPosZ() + Dist.z); + int PosY; + if (m_World->TryGetHeight((int)m_Destination.x, (int)m_Destination.z, PosY)) + { + m_Destination.y = (float)PosY + 1.2f; + MoveToPosition(m_Destination); + } + } + } +} + + + + + +// What to do if in Chasing State +// This state should always be defined in each child class +void cMonster::InStateChasing(float a_Dt) +{ + UNUSED(a_Dt); +} + + + + + +// What to do if in Escaping State +void cMonster::InStateEscaping(float a_Dt) +{ + UNUSED(a_Dt); + + if (m_Target != NULL) + { + Vector3d newloc = GetPosition(); + newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); + newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); + MoveToPosition(newloc); + } + else + { + m_EMState = IDLE; // This shouldnt be required but just to be safe + } +} + + + + + +// Do attack here +// a_Dt is passed so we can set attack rate +void cMonster::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + if ((m_Target != NULL) && (m_AttackInterval > 3.0)) + { + // Setting this higher gives us more wiggle room for attackrate + m_AttackInterval = 0.0; + ((cPawn *)m_Target)->TakeDamage(*this); + } +} + + + + + +// Checks for Players close by and if they are visible return the closest +cPlayer * cMonster::FindClosestPlayer(void) +{ + return m_World->FindClosestPlayer(GetPosition(), m_SightDistance); +} + + + + + +void cMonster::GetMonsterConfig(const AString & a_Name) +{ + cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); +} + + + + + +void cMonster::SetAttackRate(int ar) +{ + m_AttackRate = (float)ar; +} + + + + + +void cMonster::SetAttackRange(float ar) +{ + m_AttackRange = ar; +} + + + + + +void cMonster::SetAttackDamage(float ad) +{ + m_AttackDamage = ad; +} + + + + + +void cMonster::SetSightDistance(float sd) +{ + m_SightDistance = sd; +} + + + + + +AString cMonster::MobTypeToString(cMonster::eType a_MobType) +{ + // Mob types aren't sorted, so we need to search linearly: + for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_lcName; + } + } + + // Not found: + return ""; +} + + + + + +cMonster::eType cMonster::StringToMobType(const AString & a_Name) +{ + AString lcName(a_Name); + StrToLower(lcName); + + // Binary-search for the lowercase name: + int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1; + while (hi - lo > 1) + { + int mid = (lo + hi) / 2; + int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str()); + if (res == 0) + { + return g_MobTypeNames[mid].m_Type; + } + if (res < 0) + { + lo = mid; + } + else + { + hi = mid; + } + } + // Range has collapsed to at most two elements, compare each: + if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0) + { + return g_MobTypeNames[lo].m_Type; + } + if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0)) + { + return g_MobTypeNames[hi].m_Type; + } + + // Not found: + return mtInvalidType; +} + + + + + +cMonster::eFamily cMonster::FamilyFromType(eType a_Type) +{ + switch (a_Type) + { + case mtBat: return mfAmbient; + case mtBlaze: return mfHostile; + case mtCaveSpider: return mfHostile; + case mtChicken: return mfPassive; + case mtCow: return mfPassive; + case mtCreeper: return mfHostile; + case mtEnderman: return mfHostile; + case mtGhast: return mfHostile; + case mtHorse: return mfPassive; + case mtMagmaCube: return mfHostile; + case mtMooshroom: return mfHostile; + case mtOcelot: return mfHostile; + case mtPig: return mfPassive; + case mtSheep: return mfPassive; + case mtSilverfish: return mfHostile; + case mtSkeleton: return mfHostile; + case mtSlime: return mfHostile; + case mtSpider: return mfHostile; + case mtSquid: return mfWater; + case mtVillager: return mfPassive; + case mtWitch: return mfHostile; + case mtWolf: return mfHostile; + case mtZombie: return mfHostile; + case mtZombiePigman: return mfHostile; + } ; + ASSERT(!"Unhandled mob type"); + return mfMaxplusone; +} + + + + + +int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) +{ + switch (a_MobFamily) + { + case mfHostile: return 40; + case mfPassive: return 40; + case mfAmbient: return 40; + case mfWater: return 400; + } + ASSERT(!"Unhandled mob family"); + return -1; +} + + + + + +cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) +{ + cFastRandom Random; + cMonster * toReturn = NULL; + + // Create the mob entity + switch (a_MobType) + { + case mtMagmaCube: + case mtSlime: + { + toReturn = new cSlime (Random.NextInt(2) + 1); + break; + } + case mtSkeleton: + { + // TODO: Actual detection of spawning in Nether + toReturn = new cSkeleton(Random.NextInt(1) == 0 ? false : true); + break; + } + case mtVillager: + { + int VillagerType = Random.NextInt(6); + if (VillagerType == 6) + { + // Give farmers a better chance of spawning + VillagerType = 0; + } + + toReturn = new cVillager((cVillager::eVillagerType)VillagerType); + break; + } + case mtHorse: + { + // Horses take a type (species), a colour, and a style (dots, stripes, etc.) + int HorseType = Random.NextInt(7); + int HorseColor = Random.NextInt(6); + int HorseStyle = Random.NextInt(6); + int HorseTameTimes = Random.NextInt(6) + 1; + + if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) + { + // Increase chances of normal horse (zero) + HorseType = 0; + } + + toReturn = new cHorse(HorseType, HorseColor, HorseStyle, HorseTameTimes); + break; + } + + case mtBat: toReturn = new cBat(); break; + case mtBlaze: toReturn = new cBlaze(); break; + case mtCaveSpider: toReturn = new cCavespider(); break; + case mtChicken: toReturn = new cChicken(); break; + case mtCow: toReturn = new cCow(); break; + case mtCreeper: toReturn = new cCreeper(); break; + case mtEnderman: toReturn = new cEnderman(); break; + case mtGhast: toReturn = new cGhast(); break; + case mtMooshroom: toReturn = new cMooshroom(); break; + case mtOcelot: toReturn = new cOcelot(); break; + case mtPig: toReturn = new cPig(); break; + case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter + case mtSilverfish: toReturn = new cSilverfish(); break; + case mtSpider: toReturn = new cSpider(); break; + case mtSquid: toReturn = new cSquid(); break; + case mtWitch: toReturn = new cWitch(); break; + case mtWolf: toReturn = new cWolf(); break; + case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter + case mtZombiePigman: toReturn = new cZombiePigman(); break; + default: + { + ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); + } + } + return toReturn; +} + + + + + +void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) +{ + MTRand r1; + int Count = r1.randInt() % (a_Max + 1 - a_Min) + a_Min; + if (Count > 0) + { + a_Drops.push_back(cItem(a_Item, Count, a_ItemHealth)); + } +} + + + + + +void cMonster::HandleDaylightBurning(cChunk & a_Chunk) +{ + if (!m_BurnsInDaylight) + { + return; + } + + int RelY = (int)floor(GetPosY()); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + // Outside the world + return; + } + + int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width; + int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width; + if ( + (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight + (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand + (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime + !IsOnFire() // Not already burning + ) + { + // Burn for 100 ticks, then decide again + StartBurning(100); + } +} + + + + +cMonster::eFamily cMonster::GetMobFamily(void) const +{ + return FamilyFromType(m_MobType); +} + + + + diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h new file mode 100644 index 000000000..29a705d11 --- /dev/null +++ b/src/Mobs/Monster.h @@ -0,0 +1,195 @@ + +#pragma once + +#include "../Entities/Pawn.h" +#include "../Defines.h" +#include "../BlockID.h" +#include "../Item.h" + + + + + +class Vector3f; +class cClientHandle; +class cWorld; + + + + +// tolua_begin +class cMonster : + public cPawn +{ + typedef cPawn super; +public: + /// This identifies individual monster type, as well as their network type-ID + enum eType + { + mtInvalidType = -1, + + mtBat = E_META_SPAWN_EGG_BAT, + mtBlaze = E_META_SPAWN_EGG_BLAZE, + mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, + mtChicken = E_META_SPAWN_EGG_CHICKEN, + mtCow = E_META_SPAWN_EGG_COW, + mtCreeper = E_META_SPAWN_EGG_CREEPER, + mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON, + mtEnderman = E_META_SPAWN_EGG_ENDERMAN, + mtGhast = E_META_SPAWN_EGG_GHAST, + mtGiant = E_META_SPAWN_EGG_GIANT, + mtHorse = E_META_SPAWN_EGG_HORSE, + mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, + mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, + mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, + mtOcelot = E_META_SPAWN_EGG_OCELOT, + mtPig = E_META_SPAWN_EGG_PIG, + mtSheep = E_META_SPAWN_EGG_SHEEP, + mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, + mtSkeleton = E_META_SPAWN_EGG_SKELETON, + mtSlime = E_META_SPAWN_EGG_SLIME, + mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM, + mtSpider = E_META_SPAWN_EGG_SPIDER, + mtSquid = E_META_SPAWN_EGG_SQUID, + mtVillager = E_META_SPAWN_EGG_VILLAGER, + mtWitch = E_META_SPAWN_EGG_WITCH, + mtWither = E_META_SPAWN_EGG_WITHER, + mtWolf = E_META_SPAWN_EGG_WOLF, + mtZombie = E_META_SPAWN_EGG_ZOMBIE, + mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + } ; + + enum eFamily + { + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid + + mfMaxplusone, // Nothing. Be sure this is the last and the others are in order + } ; + + // tolua_end + + enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; + enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; + + float m_SightDistance; + + /** Creates the mob object. + * If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() + * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22)) + * a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively + */ + cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + CLASS_PROTODEF(cMonster); + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + virtual void KilledBy(cEntity * a_Killer) override; + + virtual void MoveToPosition(const Vector3f & a_Position); + virtual bool ReachedDestination(void); + + // tolua_begin + eType GetMobType(void) const {return m_MobType; } + eFamily GetMobFamily(void) const; + // tolua_end + + + const char * GetState(); + void SetState(const AString & str); + + virtual void CheckEventSeePlayer(void); + virtual void EventSeePlayer(cEntity * a_Player); + virtual cPlayer * FindClosestPlayer(); // non static is easier. also virtual so other mobs can implement their own searching algo + + /// Reads the monster configuration for the specified monster name and assigns it to this object. + void GetMonsterConfig(const AString & a_Name); + + virtual void EventLosePlayer(void); + virtual void CheckEventLostPlayer(void); + + virtual void InStateIdle (float a_Dt); + virtual void InStateChasing (float a_Dt); + virtual void InStateEscaping(float a_Dt); + + virtual void Attack(float a_Dt); + + int GetAttackRate(){return (int)m_AttackRate;} + void SetAttackRate(int ar); + void SetAttackRange(float ar); + void SetAttackDamage(float ad); + void SetSightDistance(float sd); + + /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick + void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } + + // Overridables to handle ageable mobs + virtual bool IsBaby (void) const { return false; } + virtual bool IsTame (void) const { return false; } + virtual bool IsSitting (void) const { return false; } + + // tolua_begin + + /// Translates MobType enum to a string, empty string if unknown + static AString MobTypeToString(eType a_MobType); + + /// Translates MobType string to the enum, mtInvalidType if not recognized + static eType StringToMobType(const AString & a_MobTypeName); + + /// Returns the mob family based on the type + static eFamily FamilyFromType(eType a_MobType); + + /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family + static int GetSpawnDelay(cMonster::eFamily a_MobFamily); + + // tolua_end + + /** Creates a new object of the specified mob. + a_MobType is the type of the mob to be created + Asserts and returns null if mob type is not specified + */ + static cMonster * NewMonsterFromType(eType a_MobType); + +protected: + + cEntity * m_Target; + float m_AttackRate; + float idle_interval; + + Vector3f m_Destination; + bool m_bMovingToDestination; + bool m_bPassiveAggressive; + + float m_DestinationTime; + + float m_DestroyTimer; + float m_Jump; + + eType m_MobType; + + AString m_SoundHurt; + AString m_SoundDeath; + + float m_SeePlayerInterval; + float m_AttackDamage; + float m_AttackRange; + float m_AttackInterval; + + bool m_BurnsInDaylight; + + void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); + + void HandleDaylightBurning(cChunk & a_Chunk); + +} ; // tolua_export + + + + diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp new file mode 100644 index 000000000..940e2db44 --- /dev/null +++ b/src/Mobs/Mooshroom.cpp @@ -0,0 +1,33 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Mooshroom.h" + + + + + +// TODO: Milk Cow + + + + + +cMooshroom::cMooshroom(void) : + super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) +{ +} + + + + + +void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); +} + + + + diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h new file mode 100644 index 000000000..73f6348b6 --- /dev/null +++ b/src/Mobs/Mooshroom.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cMooshroom : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cMooshroom(void); + + CLASS_PROTODEF(cMooshroom); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h new file mode 100644 index 000000000..adb7a1f75 --- /dev/null +++ b/src/Mobs/Ocelot.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cOcelot : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cOcelot(void) : + super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8) + { + } + + CLASS_PROTODEF(cOcelot); +} ; + + + + diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp new file mode 100644 index 000000000..28de65905 --- /dev/null +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -0,0 +1,38 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PassiveAggressiveMonster.h" + +#include "../Entities/Player.h" + + + + + +cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) +{ + m_EMPersonality = PASSIVE; +} + + + + + +void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + + if ((m_Target != NULL) && (m_Target->IsPlayer())) + { + cPlayer * Player = (cPlayer *) m_Target; + if (Player->GetGameMode() != 1) + { + m_EMState = CHASING; + } + } +} + + + + diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h new file mode 100644 index 000000000..2c5ef30b1 --- /dev/null +++ b/src/Mobs/PassiveAggressiveMonster.h @@ -0,0 +1,23 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cPassiveAggressiveMonster : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; +} ; + + + + diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp new file mode 100644 index 000000000..91ceb5a53 --- /dev/null +++ b/src/Mobs/PassiveMonster.cpp @@ -0,0 +1,59 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PassiveMonster.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) +{ + m_EMPersonality = PASSIVE; +} + + + + + +void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL)) + { + m_EMState = ESCAPING; + } +} + + + + + +void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + m_SeePlayerInterval += a_Dt; + + if (m_SeePlayerInterval > 1) // Check every second + { + int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally + + m_SeePlayerInterval = 0.0; + if (rem >= 2) + { + if (m_EMState == ESCAPING) + { + CheckEventLostPlayer(); + } + } + } +} + + + + + diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h new file mode 100644 index 000000000..14a6be6b1 --- /dev/null +++ b/src/Mobs/PassiveMonster.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "Monster.h" + + + + + +class cPassiveMonster : + public cMonster +{ + typedef cMonster super; + +public: + cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + /// When hit by someone, run away + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + +} ; + + + + diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp new file mode 100644 index 000000000..0871a38a9 --- /dev/null +++ b/src/Mobs/Pig.cpp @@ -0,0 +1,77 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Pig.h" +#include "../Entities/Player.h" +#include "../World.h" + + + + + +cPig::cPig(void) : + super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9), + m_bIsSaddled(false) +{ +} + + + + + +void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP); + if (m_bIsSaddled) + { + a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + } +} + + + + + +void cPig::OnRightClicked(cPlayer & a_Player) +{ + if (m_bIsSaddled) + { + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this pig now: + m_Attachee->Detach(); + } + + // Attach the player to this pig + a_Player.AttachTo(this); + } + else if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + // Set saddle state & broadcast metadata + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } +} + + + + + diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h new file mode 100644 index 000000000..4fd0d8db8 --- /dev/null +++ b/src/Mobs/Pig.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cPig : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cPig(void); + + CLASS_PROTODEF(cPig); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsSaddled(void) const { return m_bIsSaddled; } + +private: + + bool m_bIsSaddled; + +} ; + + + + diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp new file mode 100644 index 000000000..bda4ccff8 --- /dev/null +++ b/src/Mobs/Sheep.cpp @@ -0,0 +1,62 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Sheep.h" +#include "../BlockID.h" +#include "../Entities/Player.h" +#include "../World.h" + + + + + +cSheep::cSheep(int a_Color) : + super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3), + m_IsSheared(false), + m_WoolColor(a_Color) +{ +} + + + + + +void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + if (!m_IsSheared) + { + a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor)); + } +} + + + + + +void cSheep::OnRightClicked(cPlayer & a_Player) +{ + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared)) + { + m_IsSheared = true; + m_World->BroadcastEntityMetadata(*this); + + if (!a_Player.IsGameModeCreative()) + { + a_Player.UseEquippedItem(); + } + + cItems Drops; + int NumDrops = m_World->GetTickRandomNumber(2) + 1; + Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - a_Player.GetEquippedItem().m_ItemDamage)) + { + m_WoolColor = 15 - a_Player.GetEquippedItem().m_ItemDamage; + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_World->BroadcastEntityMetadata(*this); + } +} diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h new file mode 100644 index 000000000..8293a2c05 --- /dev/null +++ b/src/Mobs/Sheep.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cSheep : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cSheep(int a_Color); + + CLASS_PROTODEF(cSheep); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + bool IsSheared(void) const { return m_IsSheared; } + int GetFurColor(void) const { return m_WoolColor; } + +private: + + bool m_IsSheared; + int m_WoolColor; + +} ; + + + + diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h new file mode 100644 index 000000000..a6e11c49d --- /dev/null +++ b/src/Mobs/Silverfish.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSilverfish : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSilverfish(void) : + super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7) + { + } + + CLASS_PROTODEF(cSilverfish); +} ; + + + + diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp new file mode 100644 index 000000000..509c2191e --- /dev/null +++ b/src/Mobs/Skeleton.cpp @@ -0,0 +1,70 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Skeleton.h" +#include "../World.h" + + + + +cSkeleton::cSkeleton(bool IsWither) : + super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8), + m_bIsWither(IsWither) +{ + SetBurnsInDaylight(true); +} + + + + + +void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW); + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE); +} + + + + + +void cSkeleton::MoveToPosition(const Vector3f & a_Position) +{ + m_Destination = a_Position; + + // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. + if (!IsOnFire() && m_World->GetTimeOfDay() < 13187 && m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) + { + m_bMovingToDestination = false; + return; + } + m_bMovingToDestination = true; +} + + + + + +void cSkeleton::Attack(float a_Dt) +{ + m_AttackInterval += a_Dt * m_AttackRate; + + if (m_Target != NULL && m_AttackInterval > 3.0) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + if (Arrow == NULL) + { + return; + } + if (!Arrow->Initialize(m_World)) + { + delete Arrow; + return; + } + m_World->BroadcastSpawnEntity(*Arrow); + m_AttackInterval = 0.0; + } +}
\ No newline at end of file diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h new file mode 100644 index 000000000..8f31b42e1 --- /dev/null +++ b/src/Mobs/Skeleton.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSkeleton : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSkeleton(bool IsWither); + + CLASS_PROTODEF(cSkeleton); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void MoveToPosition(const Vector3f & a_Position) override; + virtual void Attack(float a_Dt) override; + bool IsWither(void) const { return m_bIsWither; }; + +private: + + bool m_bIsWither; + +} ; + + + + diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp new file mode 100644 index 000000000..19f376c21 --- /dev/null +++ b/src/Mobs/Slime.cpp @@ -0,0 +1,29 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Slime.h" + + + + + +/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest +cSlime::cSlime(int a_Size) : + super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size), + m_Size(a_Size) +{ +} + + + + + +void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + // TODO: only when tiny + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL); +} + + + + diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h new file mode 100644 index 000000000..782c3113f --- /dev/null +++ b/src/Mobs/Slime.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSlime : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest + cSlime(int a_Size); + + CLASS_PROTODEF(cSlime); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + int GetSize(void) const { return m_Size; } + +protected: + + /// Size of the slime, 1 .. 3, with 1 being the smallest + int m_Size; +} ; + + + + diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp new file mode 100644 index 000000000..9e199f87e --- /dev/null +++ b/src/Mobs/SnowGolem.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SnowGolem.h" + + + + + +cSnowGolem::cSnowGolem(void) : + super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8) +{ +} + + + + + +void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL); +} + + + + diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h new file mode 100644 index 000000000..d1344adfd --- /dev/null +++ b/src/Mobs/SnowGolem.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSnowGolem : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSnowGolem(void); + + CLASS_PROTODEF(cSnowGolem); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp new file mode 100644 index 000000000..b19a5dcef --- /dev/null +++ b/src/Mobs/Spider.cpp @@ -0,0 +1,27 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Spider.h" + + + + + +cSpider::cSpider(void) : + super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9) +{ +} + + + + + +void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE); +} + + + + diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h new file mode 100644 index 000000000..51e65d028 --- /dev/null +++ b/src/Mobs/Spider.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cSpider : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cSpider(void); + + CLASS_PROTODEF(cSpider); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp new file mode 100644 index 000000000..a311108ae --- /dev/null +++ b/src/Mobs/Squid.cpp @@ -0,0 +1,56 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Squid.h" +#include "../Vector3d.h" +#include "../Chunk.h" + + + + + +cSquid::cSquid(void) : + super("Squid", mtSquid, "", "", 0.95, 0.95) +{ +} + + + + + +void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + // Drops 0-3 Ink Sacs + AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK); +} + + + + + +void cSquid::Tick(float a_Dt, cChunk & a_Chunk) +{ + // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk + // that is not where the entity currently resides (FS #411) + + Vector3d Pos = GetPosition(); + + // TODO: Not a real behavior, but cool :D + int RelY = (int)floor(Pos.y); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + return; + } + int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width; + if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire()) + { + // Burn for 10 ticks, then decide again + StartBurning(10); + } + + super::Tick(a_Dt, a_Chunk); +} + + + diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h new file mode 100644 index 000000000..ad299b95c --- /dev/null +++ b/src/Mobs/Squid.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cSquid : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cSquid(); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + CLASS_PROTODEF(cSquid); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + +} ; + + + + diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp new file mode 100644 index 000000000..7f89fb6cc --- /dev/null +++ b/src/Mobs/Villager.cpp @@ -0,0 +1,35 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Villager.h" +#include "../World.h" + + + + + +cVillager::cVillager(eVillagerType VillagerType) : + super("Villager", mtVillager, "", "", 0.6, 1.8), + m_Type(VillagerType) +{ +} + + + + + +void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if (a_TDI.Attacker->IsPlayer()) + { + if (m_World->GetTickRandomNumber(5) == 3) + { + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_VILLAGER_ANGRY); + } + } +} + + + + diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h new file mode 100644 index 000000000..4cd9aaa8e --- /dev/null +++ b/src/Mobs/Villager.h @@ -0,0 +1,43 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cVillager : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + + enum eVillagerType + { + vtFarmer = 0, + vtLibrarian = 1, + vtPriest = 2, + vtBlacksmith = 3, + vtButcher = 4, + vtGeneric = 5, + vtMax + } ; + + cVillager(eVillagerType VillagerType); + + CLASS_PROTODEF(cVillager); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + int GetVilType(void) const { return m_Type; } + +private: + + int m_Type; + +} ; + + + + diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp new file mode 100644 index 000000000..25d27041f --- /dev/null +++ b/src/Mobs/Witch.cpp @@ -0,0 +1,32 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Witch.h" + + + + + +cWitch::cWitch(void) : + super("Witch", mtWitch, "", "", 0.6, 1.8) +{ +} + + + + + +void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR); +} + + + + diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h new file mode 100644 index 000000000..4e637beea --- /dev/null +++ b/src/Mobs/Witch.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWitch : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWitch(); + + CLASS_PROTODEF(cWitch); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + + bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); } +} ; + + + + diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp new file mode 100644 index 000000000..c46e0beab --- /dev/null +++ b/src/Mobs/Wither.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Wither.h" + + + + + +cWither::cWither(void) : + super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0) +{ +} + + + + + +void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 1, 1, E_ITEM_NETHER_STAR); +} + + + + diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h new file mode 100644 index 000000000..56effc6bb --- /dev/null +++ b/src/Mobs/Wither.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWither : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWither(void); + + CLASS_PROTODEF(cWither); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp new file mode 100644 index 000000000..c86250142 --- /dev/null +++ b/src/Mobs/Wolf.cpp @@ -0,0 +1,189 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Wolf.h" +#include "../World.h" +#include "../Entities/Player.h" + + + + + +cWolf::cWolf(void) : + super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8), + m_IsAngry(false), + m_IsTame(false), + m_IsSitting(false), + m_IsBegging(false), + m_OwnerName(""), + m_CollarColor(14) +{ +} + + + + + +void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + super::DoTakeDamage(a_TDI); + if (!m_IsTame) + { + m_IsAngry = true; + } + m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face +} + + + + + +void cWolf::OnRightClicked(cPlayer & a_Player) +{ + if (!IsTame() && !IsAngry()) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + + if (m_World->GetTickRandomNumber(7) == 0) + { + SetMaxHealth(20); + SetIsTame(true); + SetOwner(a_Player.GetName()); + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED); + } + else + { + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING); + } + } + } + else if (IsTame()) + { + if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) + { + SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage); + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + } + else if (IsSitting()) + { + SetIsSitting(false); + } + else + { + SetIsSitting(true); + } + } + } + + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cWolf::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (!IsAngry()) + { + cMonster::Tick(a_Dt, a_Chunk); + } + else + { + super::Tick(a_Dt, a_Chunk); + } + + if (IsSitting()) + { + m_bMovingToDestination = false; + } + + cPlayer * a_Closest_Player = FindClosestPlayer(); + if (a_Closest_Player != NULL) + { + switch (a_Closest_Player->GetEquippedItem().m_ItemType) + { + case E_ITEM_BONE: + case E_ITEM_RAW_BEEF: + case E_ITEM_STEAK: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_ROTTEN_FLESH: + { + if (!IsBegging()) + { + SetIsBegging(true); + m_World->BroadcastEntityMetadata(*this); + } + Vector3f a_NewDestination = a_Closest_Player->GetPosition(); + a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet. + m_Destination = Vector3f(a_NewDestination); + m_bMovingToDestination = false; + break; + } + default: + { + if (IsBegging()) + { + SetIsBegging(false); + m_World->BroadcastEntityMetadata(*this); + } + } + } + } + + if (IsTame()) + { + TickFollowPlayer(); + } +} + + + + + +void cWolf::TickFollowPlayer() +{ + class cCallback : + public cPlayerListCallback + { + virtual bool Item(cPlayer * a_Player) override + { + OwnerPos = a_Player->GetPosition(); + return false; + } + public: + Vector3f OwnerPos; + } Callback; + if (m_World->DoWithPlayer(m_OwnerName, Callback)) + { + // The player is present in the world, follow them: + double Distance = (Callback.OwnerPos - GetPosition()).Length(); + if (Distance < 3) + { + m_bMovingToDestination = false; + } + else if ((Distance > 30) && (!IsSitting())) + { + TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); + } + else + { + m_Destination = Callback.OwnerPos; + } + } +} + + + + diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h new file mode 100644 index 000000000..040e2cf7a --- /dev/null +++ b/src/Mobs/Wolf.h @@ -0,0 +1,54 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" +#include "../Entities/Entity.h" + + + + + +class cWolf : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cWolf(void); + + CLASS_PROTODEF(cWolf); + + virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void TickFollowPlayer(); + + // Get functions + bool IsSitting (void) const { return m_IsSitting; } + bool IsTame (void) const { return m_IsTame; } + bool IsBegging (void) const { return m_IsBegging; } + bool IsAngry (void) const { return m_IsAngry; } + AString GetOwner (void) const { return m_OwnerName; } + int GetCollarColor(void) const { return m_CollarColor; } + + // Set functions + void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; } + void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; } + void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; } + void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; } + void SetOwner (AString a_NewOwner) { m_OwnerName = a_NewOwner; } + void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; } + +protected: + + bool m_IsSitting; + bool m_IsTame; + bool m_IsBegging; + bool m_IsAngry; + AString m_OwnerName; + int m_CollarColor; +} ; + + + + diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp new file mode 100644 index 000000000..a485d2b55 --- /dev/null +++ b/src/Mobs/Zombie.cpp @@ -0,0 +1,47 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Zombie.h" +#include "../World.h" +#include "../LineBlockTracer.h" + + + + +cZombie::cZombie(bool IsVillagerZombie) : + super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8), + m_bIsConverting(false), + m_bIsVillagerZombie(IsVillagerZombie) +{ + SetBurnsInDaylight(true); +} + + + + + +void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH); + + // TODO: Rare drops +} + + + + + +void cZombie::MoveToPosition(const Vector3f & a_Position) +{ + m_Destination = a_Position; + + // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. + if ((m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) && (m_World->GetTimeOfDay() < 13187) && !IsOnFire()) + { + m_bMovingToDestination = false; + return; + } + m_bMovingToDestination = true; +} + + diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h new file mode 100644 index 000000000..7e14fe42f --- /dev/null +++ b/src/Mobs/Zombie.h @@ -0,0 +1,33 @@ +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cZombie : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cZombie(bool IsVillagerZombie); + + CLASS_PROTODEF(cZombie); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void MoveToPosition(const Vector3f & a_Position) override; + + bool IsVillagerZombie(void) const {return m_bIsVillagerZombie; } + bool IsConverting(void) const {return m_bIsConverting; } + +private: + + bool m_bIsVillagerZombie, m_bIsConverting; + +} ; + + + + diff --git a/src/Mobs/Zombiepigman.cpp b/src/Mobs/Zombiepigman.cpp new file mode 100644 index 000000000..6ac89ed4c --- /dev/null +++ b/src/Mobs/Zombiepigman.cpp @@ -0,0 +1,45 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Zombiepigman.h" +#include "../World.h" + + + + + +cZombiePigman::cZombiePigman(void) : + super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) +{ +} + + + + + +void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH); + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET); + + // TODO: Rare drops +} + + + + + +void cZombiePigman::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + + if ((a_Killer != NULL) && (a_Killer->IsPlayer())) + { + // TODO: Anger all nearby zombie pigmen + // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker? + } +} + + + + diff --git a/src/Mobs/Zombiepigman.h b/src/Mobs/Zombiepigman.h new file mode 100644 index 000000000..67991d56a --- /dev/null +++ b/src/Mobs/Zombiepigman.h @@ -0,0 +1,26 @@ + +#pragma once + +#include "PassiveAggressiveMonster.h" + + + + + +class cZombiePigman : + public cPassiveAggressiveMonster +{ + typedef cPassiveAggressiveMonster super; + +public: + cZombiePigman(void); + + CLASS_PROTODEF(cZombiePigman); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void KilledBy(cEntity * a_Killer) override; +} ; + + + + diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp new file mode 100644 index 000000000..6165606f0 --- /dev/null +++ b/src/MonsterConfig.cpp @@ -0,0 +1,103 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MonsterConfig.h" +#include "Mobs/Monster.h" +#include "inifile/iniFile.h" + + + + + +struct cMonsterConfig::sAttributesStruct +{ + AString m_Name; + double m_SightDistance; + double m_AttackDamage; + double m_AttackRange; + double m_AttackRate; + int m_MaxHealth; +}; + + + + + +struct cMonsterConfig::sMonsterConfigState +{ + AString MonsterTypes; + std::list< sAttributesStruct > AttributesList; +}; + + + + + +cMonsterConfig::cMonsterConfig(void) + : m_pState( new sMonsterConfigState ) +{ + Initialize(); +} + + + + + +cMonsterConfig::~cMonsterConfig() +{ + delete m_pState; +} + + + + + +void cMonsterConfig::Initialize() +{ + cIniFile MonstersIniFile; + + if (!MonstersIniFile.ReadFile("monsters.ini")) + { + LOGWARNING("%s: Cannot read monsters.ini file, monster attributes not available", __FUNCTION__); + return; + } + + for (int i = (int)MonstersIniFile.GetNumKeys(); i >= 0; i--) + { + sAttributesStruct Attributes; + AString Name = MonstersIniFile.GetKeyName(i); + Attributes.m_Name = Name; + Attributes.m_AttackDamage = MonstersIniFile.GetValueF(Name, "AttackDamage", 0); + Attributes.m_AttackRange = MonstersIniFile.GetValueF(Name, "AttackRange", 0); + Attributes.m_SightDistance = MonstersIniFile.GetValueF(Name, "SightDistance", 0); + Attributes.m_AttackRate = MonstersIniFile.GetValueF(Name, "AttackRate", 0); + Attributes.m_MaxHealth = MonstersIniFile.GetValueI(Name, "MaxHealth", 1); + m_pState->AttributesList.push_front(Attributes); + } // for i - SplitList[] +} + + + + + +void cMonsterConfig::AssignAttributes(cMonster * a_Monster, const AString & a_Name) +{ + std::list<sAttributesStruct>::const_iterator itr; + for (itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr) + { + if (itr->m_Name.compare(a_Name) == 0) + { + a_Monster->SetAttackDamage ((float)itr->m_AttackDamage); + a_Monster->SetAttackRange ((float)itr->m_AttackRange); + a_Monster->SetSightDistance((float)itr->m_SightDistance); + a_Monster->SetAttackRate ((int)itr->m_AttackRate); + a_Monster->SetMaxHealth (itr->m_MaxHealth); + return; + } + } // for itr - m_pState->AttributesList[] +} + + + + + diff --git a/src/MonsterConfig.h b/src/MonsterConfig.h new file mode 100644 index 000000000..371d324c2 --- /dev/null +++ b/src/MonsterConfig.h @@ -0,0 +1,32 @@ + +#pragma once + + + + + +// fwd: +class cMonster; + + + + + +class cMonsterConfig +{ +public: + cMonsterConfig(void); + ~cMonsterConfig(); + + void AssignAttributes(cMonster * a_Monster, const AString & a_Name); + +private: + struct sAttributesStruct; + struct sMonsterConfigState; + sMonsterConfigState* m_pState; + void Initialize(); +} ; + + + + diff --git a/src/Noise.cpp b/src/Noise.cpp new file mode 100644 index 000000000..729641961 --- /dev/null +++ b/src/Noise.cpp @@ -0,0 +1,951 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Noise.h" + + + + + +#if NOISE_USE_SSE + #include <smmintrin.h> //_mm_mul_epi32 +#endif + +#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x)) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Globals: + +void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase) +{ + const int BUF_SIZE = 512; + ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed + + // Save in XY cuts: + cFile f1; + if (f1.Open(Printf("%s_XY (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int z = 0; z < a_SizeZ; z++) + { + for (int y = 0; y < a_SizeY; y++) + { + int idx = y * a_SizeX + z * a_SizeX * a_SizeY; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f1.Write(buf, a_SizeX); + } // for y + unsigned char buf[BUF_SIZE]; + memset(buf, 0, a_SizeX); + f1.Write(buf, a_SizeX); + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open(Printf("%s_XZ (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int y = 0; y < a_SizeY; y++) + { + for (int z = 0; z < a_SizeZ; z++) + { + int idx = y * a_SizeX + z * a_SizeX * a_SizeY; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f2.Write(buf, a_SizeX); + } // for z + unsigned char buf[BUF_SIZE]; + memset(buf, 0, a_SizeX); + f2.Write(buf, a_SizeX); + } // for y + } // if (XZ file open) +} + + + + + +void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase) +{ + const int BUF_SIZE = 512; + ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed + + cFile f1; + if (f1.Open(Printf("%s (%d).grab", a_FileNameBase.c_str(), a_SizeX), cFile::fmWrite)) + { + for (int y = 0; y < a_SizeY; y++) + { + int idx = y * a_SizeX; + unsigned char buf[BUF_SIZE]; + for (int x = 0; x < a_SizeX; x++) + { + buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++])))); + } + f1.Write(buf, a_SizeX); + } // for y + } // if (file open) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCubicCell2D: + +class cCubicCell2D +{ +public: + cCubicCell2D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values + ); + + /// Uses current m_WorkRnds[] to generate part of the array + void Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY + ); + + /// Initializes m_WorkRnds[] with the specified Floor values + void InitWorkRnds(int a_FloorX, int a_FloorY); + + /// Updates m_WorkRnds[] for the new Floor values. + void Move(int a_NewFloorX, int a_NewFloorY); + +protected: + typedef NOISE_DATATYPE Workspace[4][4]; + + const cNoise & m_Noise; + + Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) + Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move() + Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move() + int m_CurFloorX; + int m_CurFloorY; + + NOISE_DATATYPE * m_Array; + int m_SizeX, m_SizeY; + const NOISE_DATATYPE * m_FracX; + const NOISE_DATATYPE * m_FracY; +} ; + + + + + +cCubicCell2D::cCubicCell2D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values +) : + m_Noise(a_Noise), + m_WorkRnds(&m_Workspace1), + m_Array(a_Array), + m_SizeX(a_SizeX), + m_SizeY(a_SizeY), + m_FracX(a_FracX), + m_FracY(a_FracY) +{ +} + + + + + +void cCubicCell2D::Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY +) +{ + for (int y = a_FromY; y < a_ToY; y++) + { + NOISE_DATATYPE Interp[4]; + NOISE_DATATYPE FracY = m_FracY[y]; + Interp[0] = cNoise::CubicInterpolate((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], (*m_WorkRnds)[0][2], (*m_WorkRnds)[0][3], FracY); + Interp[1] = cNoise::CubicInterpolate((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], (*m_WorkRnds)[1][2], (*m_WorkRnds)[1][3], FracY); + Interp[2] = cNoise::CubicInterpolate((*m_WorkRnds)[2][0], (*m_WorkRnds)[2][1], (*m_WorkRnds)[2][2], (*m_WorkRnds)[2][3], FracY); + Interp[3] = cNoise::CubicInterpolate((*m_WorkRnds)[3][0], (*m_WorkRnds)[3][1], (*m_WorkRnds)[3][2], (*m_WorkRnds)[3][3], FracY); + int idx = y * m_SizeX + a_FromX; + for (int x = a_FromX; x < a_ToX; x++) + { + m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]); + } // for x + } // for y +} + + + + + +void cCubicCell2D::InitWorkRnds(int a_FloorX, int a_FloorY) +{ + m_CurFloorX = a_FloorX; + m_CurFloorY = a_FloorY; + for (int x = 0; x < 4; x++) + { + int cx = a_FloorX + x - 1; + for (int y = 0; y < 4; y++) + { + int cy = a_FloorY + y - 1; + (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy); + } + } +} + + + + + +void cCubicCell2D::Move(int a_NewFloorX, int a_NewFloorY) +{ + // Swap the doublebuffer: + int OldFloorX = m_CurFloorX; + int OldFloorY = m_CurFloorY; + Workspace * OldWorkRnds = m_WorkRnds; + m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1; + + // Reuse as much of the old workspace as possible: + int DiffX = OldFloorX - a_NewFloorX; + int DiffY = OldFloorY - a_NewFloorY; + for (int x = 0; x < 4; x++) + { + int cx = a_NewFloorX + x - 1; + int OldX = x - DiffX; // Where would this X be in the old grid? + for (int y = 0; y < 4; y++) + { + int cy = a_NewFloorY + y - 1; + int OldY = y - DiffY; // Where would this Y be in the old grid? + if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4)) + { + (*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY]; + } + else + { + (*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy); + } + } + } + m_CurFloorX = a_NewFloorX; + m_CurFloorY = a_NewFloorY; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCubicCell3D: + +class cCubicCell3D +{ +public: + cCubicCell3D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values + const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values + ); + + /// Uses current m_WorkRnds[] to generate part of the array + void Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY, + int a_FromZ, int a_ToZ + ); + + /// Initializes m_WorkRnds[] with the specified Floor values + void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ); + + /// Updates m_WorkRnds[] for the new Floor values. + void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ); + +protected: + typedef NOISE_DATATYPE Workspace[4][4][4]; + + const cNoise & m_Noise; + + Workspace * m_WorkRnds; ///< The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) + Workspace m_Workspace1; ///< Buffer 1 for workspace doublebuffering, used in Move() + Workspace m_Workspace2; ///< Buffer 2 for workspace doublebuffering, used in Move() + int m_CurFloorX; + int m_CurFloorY; + int m_CurFloorZ; + + NOISE_DATATYPE * m_Array; + int m_SizeX, m_SizeY, m_SizeZ; + const NOISE_DATATYPE * m_FracX; + const NOISE_DATATYPE * m_FracY; + const NOISE_DATATYPE * m_FracZ; +} ; + + + + + +cCubicCell3D::cCubicCell3D( + const cNoise & a_Noise, ///< Noise to use for generating the random values + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values + const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values + const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values +) : + m_Noise(a_Noise), + m_WorkRnds(&m_Workspace1), + m_Array(a_Array), + m_SizeX(a_SizeX), + m_SizeY(a_SizeY), + m_SizeZ(a_SizeZ), + m_FracX(a_FracX), + m_FracY(a_FracY), + m_FracZ(a_FracZ) +{ +} + + + + + +void cCubicCell3D::Generate( + int a_FromX, int a_ToX, + int a_FromY, int a_ToY, + int a_FromZ, int a_ToZ +) +{ + for (int z = a_FromZ; z < a_ToZ; z++) + { + int idxZ = z * m_SizeX * m_SizeY; + NOISE_DATATYPE Interp2[4][4]; + NOISE_DATATYPE FracZ = m_FracZ[z]; + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 4; y++) + { + Interp2[x][y] = cNoise::CubicInterpolate((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], (*m_WorkRnds)[x][y][2], (*m_WorkRnds)[x][y][3], FracZ); + } + } + for (int y = a_FromY; y < a_ToY; y++) + { + NOISE_DATATYPE Interp[4]; + NOISE_DATATYPE FracY = m_FracY[y]; + Interp[0] = cNoise::CubicInterpolate(Interp2[0][0], Interp2[0][1], Interp2[0][2], Interp2[0][3], FracY); + Interp[1] = cNoise::CubicInterpolate(Interp2[1][0], Interp2[1][1], Interp2[1][2], Interp2[1][3], FracY); + Interp[2] = cNoise::CubicInterpolate(Interp2[2][0], Interp2[2][1], Interp2[2][2], Interp2[2][3], FracY); + Interp[3] = cNoise::CubicInterpolate(Interp2[3][0], Interp2[3][1], Interp2[3][2], Interp2[3][3], FracY); + int idx = idxZ + y * m_SizeX + a_FromX; + for (int x = a_FromX; x < a_ToX; x++) + { + m_Array[idx++] = cNoise::CubicInterpolate(Interp[0], Interp[1], Interp[2], Interp[3], m_FracX[x]); + } // for x + } // for y + } // for z +} + + + + + +void cCubicCell3D::InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ) +{ + m_CurFloorX = a_FloorX; + m_CurFloorY = a_FloorY; + m_CurFloorZ = a_FloorZ; + for (int x = 0; x < 4; x++) + { + int cx = a_FloorX + x - 1; + for (int y = 0; y < 4; y++) + { + int cy = a_FloorY + y - 1; + for (int z = 0; z < 4; z++) + { + int cz = a_FloorZ + z - 1; + (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz); + } + } + } +} + + + + + +void cCubicCell3D::Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ) +{ + // Swap the doublebuffer: + int OldFloorX = m_CurFloorX; + int OldFloorY = m_CurFloorY; + int OldFloorZ = m_CurFloorZ; + Workspace * OldWorkRnds = m_WorkRnds; + m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1; + + // Reuse as much of the old workspace as possible: + int DiffX = OldFloorX - a_NewFloorX; + int DiffY = OldFloorY - a_NewFloorY; + int DiffZ = OldFloorZ - a_NewFloorZ; + for (int x = 0; x < 4; x++) + { + int cx = a_NewFloorX + x - 1; + int OldX = x - DiffX; // Where would this X be in the old grid? + for (int y = 0; y < 4; y++) + { + int cy = a_NewFloorY + y - 1; + int OldY = y - DiffY; // Where would this Y be in the old grid? + for (int z = 0; z < 4; z++) + { + int cz = a_NewFloorZ + z - 1; + int OldZ = z - DiffZ; + if ((OldX >= 0) && (OldX < 4) && (OldY >= 0) && (OldY < 4) && (OldZ >= 0) && (OldZ < 4)) + { + (*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ]; + } + else + { + (*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz); + } + } // for z + } // for y + } // for x + m_CurFloorX = a_NewFloorX; + m_CurFloorY = a_NewFloorY; + m_CurFloorZ = a_NewFloorZ; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise: + +cNoise::cNoise(unsigned int a_Seed) : + m_Seed(a_Seed) +{ +} + + + + + +cNoise::cNoise(const cNoise & a_Noise) : + m_Seed(a_Noise.m_Seed) +{ +} + + + + + +NOISE_DATATYPE cNoise::LinearNoise1D(NOISE_DATATYPE a_X) const +{ + int BaseX = FAST_FLOOR(a_X); + NOISE_DATATYPE FracX = a_X - BaseX; + return LinearInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX); +} + + + + + +NOISE_DATATYPE cNoise::CosineNoise1D(NOISE_DATATYPE a_X) const +{ + int BaseX = FAST_FLOOR(a_X); + NOISE_DATATYPE FracX = a_X - BaseX; + return CosineInterpolate(IntNoise1D(BaseX), IntNoise1D(BaseX + 1), FracX); +} + + + + + +NOISE_DATATYPE cNoise::CubicNoise1D(NOISE_DATATYPE a_X) const +{ + int BaseX = FAST_FLOOR(a_X); + NOISE_DATATYPE FracX = a_X - BaseX; + return CubicInterpolate(IntNoise1D(BaseX - 1), IntNoise1D(BaseX), IntNoise1D(BaseX + 1), IntNoise1D(BaseX + 2), FracX); +} + + + + + +NOISE_DATATYPE cNoise::SmoothNoise1D(int a_X) const +{ + return IntNoise1D(a_X) / 2 + IntNoise1D(a_X - 1) / 4 + IntNoise1D(a_X + 1) / 4; +} + + + + + +NOISE_DATATYPE cNoise::CubicNoise2D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const +{ + const int BaseX = FAST_FLOOR(a_X); + const int BaseY = FAST_FLOOR(a_Y); + + const NOISE_DATATYPE points[4][4] = + { + IntNoise2D(BaseX - 1, BaseY - 1), IntNoise2D(BaseX, BaseY - 1), IntNoise2D(BaseX + 1, BaseY - 1), IntNoise2D(BaseX + 2, BaseY - 1), + IntNoise2D(BaseX - 1, BaseY), IntNoise2D(BaseX, BaseY), IntNoise2D(BaseX + 1, BaseY), IntNoise2D(BaseX + 2, BaseY), + IntNoise2D(BaseX - 1, BaseY + 1), IntNoise2D(BaseX, BaseY + 1), IntNoise2D(BaseX + 1, BaseY + 1), IntNoise2D(BaseX + 2, BaseY + 1), + IntNoise2D(BaseX - 1, BaseY + 2), IntNoise2D(BaseX, BaseY + 2), IntNoise2D(BaseX + 1, BaseY + 2), IntNoise2D(BaseX + 2, BaseY + 2), + }; + + const NOISE_DATATYPE FracX = a_X - BaseX; + const NOISE_DATATYPE interp1 = CubicInterpolate(points[0][0], points[0][1], points[0][2], points[0][3], FracX); + const NOISE_DATATYPE interp2 = CubicInterpolate(points[1][0], points[1][1], points[1][2], points[1][3], FracX); + const NOISE_DATATYPE interp3 = CubicInterpolate(points[2][0], points[2][1], points[2][2], points[2][3], FracX); + const NOISE_DATATYPE interp4 = CubicInterpolate(points[3][0], points[3][1], points[3][2], points[3][3], FracX); + + + const NOISE_DATATYPE FracY = a_Y - BaseY; + return CubicInterpolate(interp1, interp2, interp3, interp4, FracY); +} + + + + + +NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const +{ + const int BaseX = FAST_FLOOR(a_X); + const int BaseY = FAST_FLOOR(a_Y); + const int BaseZ = FAST_FLOOR(a_Z); + + const NOISE_DATATYPE points1[4][4] = { + IntNoise3D(BaseX - 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY - 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY - 1, BaseZ - 1), + IntNoise3D(BaseX - 1, BaseY, BaseZ - 1), IntNoise3D(BaseX, BaseY, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY, BaseZ - 1), + IntNoise3D(BaseX - 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 1, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 1, BaseZ - 1), + IntNoise3D(BaseX - 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 1, BaseY + 2, BaseZ - 1), IntNoise3D(BaseX + 2, BaseY + 2, BaseZ - 1), + }; + + const NOISE_DATATYPE FracX = (a_X) - BaseX; + const NOISE_DATATYPE x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX ); + const NOISE_DATATYPE x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX ); + const NOISE_DATATYPE x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX ); + const NOISE_DATATYPE x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX ); + + const NOISE_DATATYPE points2[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ), + IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ), + }; + + const NOISE_DATATYPE x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX ); + const NOISE_DATATYPE x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX ); + const NOISE_DATATYPE x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX ); + const NOISE_DATATYPE x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX ); + + const NOISE_DATATYPE points3[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ), + }; + + const NOISE_DATATYPE x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX ); + const NOISE_DATATYPE x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX ); + const NOISE_DATATYPE x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX ); + const NOISE_DATATYPE x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX ); + + const NOISE_DATATYPE points4[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ), + }; + + const NOISE_DATATYPE x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX ); + const NOISE_DATATYPE x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX ); + const NOISE_DATATYPE x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX ); + const NOISE_DATATYPE x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX ); + + const NOISE_DATATYPE FracY = (a_Y) - BaseY; + const NOISE_DATATYPE yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY ); + const NOISE_DATATYPE yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY ); + const NOISE_DATATYPE yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY ); + const NOISE_DATATYPE yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY ); + + const NOISE_DATATYPE FracZ = (a_Z) - BaseZ; + return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCubicNoise: + +#ifdef _DEBUG + int cCubicNoise::m_NumSingleX = 0; + int cCubicNoise::m_NumSingleXY = 0; + int cCubicNoise::m_NumSingleY = 0; + int cCubicNoise::m_NumCalls = 0; +#endif // _DEBUG + +cCubicNoise::cCubicNoise(int a_Seed) : + m_Noise(a_Seed) +{ +} + + + + + +void cCubicNoise::Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Size of the array (num doubles), in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction +) const +{ + ASSERT(a_SizeX < MAX_SIZE); + ASSERT(a_SizeY < MAX_SIZE); + ASSERT(a_StartX < a_EndX); + ASSERT(a_StartY < a_EndY); + + // Calculate the integral and fractional parts of each coord: + int FloorX[MAX_SIZE]; + int FloorY[MAX_SIZE]; + NOISE_DATATYPE FracX[MAX_SIZE]; + NOISE_DATATYPE FracY[MAX_SIZE]; + int SameX[MAX_SIZE]; + int SameY[MAX_SIZE]; + int NumSameX, NumSameY; + CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX); + CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY); + + cCubicCell2D Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY); + + Cell.InitWorkRnds(FloorX[0], FloorY[0]); + + #ifdef _DEBUG + // Statistics on the noise-space coords: + if (NumSameX == 1) + { + m_NumSingleX++; + if (NumSameY == 1) + { + m_NumSingleXY++; + } + } + if (NumSameY == 1) + { + m_NumSingleY++; + } + m_NumCalls++; + #endif // _DEBUG + + // Calculate query values using Cell: + int FromY = 0; + for (int y = 0; y < NumSameY; y++) + { + int ToY = FromY + SameY[y]; + int FromX = 0; + int CurFloorY = FloorY[FromY]; + for (int x = 0; x < NumSameX; x++) + { + int ToX = FromX + SameX[x]; + Cell.Generate(FromX, ToX, FromY, ToY); + Cell.Move(FloorX[ToX], CurFloorY); + FromX = ToX; + } + Cell.Move(FloorX[0], FloorY[ToY]); + FromY = ToY; + } +} + + + + + +void cCubicNoise::Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Size of the array (num doubles), in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Y direction +) const +{ + ASSERT(a_SizeX < MAX_SIZE); + ASSERT(a_SizeY < MAX_SIZE); + ASSERT(a_SizeZ < MAX_SIZE); + ASSERT(a_StartX < a_EndX); + ASSERT(a_StartY < a_EndY); + ASSERT(a_StartZ < a_EndZ); + + // Calculate the integral and fractional parts of each coord: + int FloorX[MAX_SIZE]; + int FloorY[MAX_SIZE]; + int FloorZ[MAX_SIZE]; + NOISE_DATATYPE FracX[MAX_SIZE]; + NOISE_DATATYPE FracY[MAX_SIZE]; + NOISE_DATATYPE FracZ[MAX_SIZE]; + int SameX[MAX_SIZE]; + int SameY[MAX_SIZE]; + int SameZ[MAX_SIZE]; + int NumSameX, NumSameY, NumSameZ; + CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX); + CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY); + CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ); + + cCubicCell3D Cell( + m_Noise, a_Array, + a_SizeX, a_SizeY, a_SizeZ, + FracX, FracY, FracZ + ); + + Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]); + + // Calculate query values using Cell: + int FromZ = 0; + for (int z = 0; z < NumSameZ; z++) + { + int ToZ = FromZ + SameZ[z]; + int CurFloorZ = FloorZ[FromZ]; + int FromY = 0; + for (int y = 0; y < NumSameY; y++) + { + int ToY = FromY + SameY[y]; + int CurFloorY = FloorY[FromY]; + int FromX = 0; + for (int x = 0; x < NumSameX; x++) + { + int ToX = FromX + SameX[x]; + Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ); + Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ); + FromX = ToX; + } + Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ); + FromY = ToY; + } // for y + Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]); + FromZ = ToZ; + } // for z +} + + + + + +void cCubicNoise::CalcFloorFrac( + int a_Size, + NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, + int * a_Floor, NOISE_DATATYPE * a_Frac, + int * a_Same, int & a_NumSame +) const +{ + NOISE_DATATYPE val = a_Start; + NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1); + for (int i = 0; i < a_Size; i++) + { + a_Floor[i] = FAST_FLOOR(val); + a_Frac[i] = val - a_Floor[i]; + val += dif; + } + + // Mark up the same floor values into a_Same / a_NumSame: + int CurFloor = a_Floor[0]; + int LastSame = 0; + a_NumSame = 0; + for (int i = 1; i < a_Size; i++) + { + if (a_Floor[i] != CurFloor) + { + a_Same[a_NumSame] = i - LastSame; + LastSame = i; + a_NumSame += 1; + CurFloor = a_Floor[i]; + } + } // for i - a_Floor[] + if (LastSame < a_Size) + { + a_Same[a_NumSame] = a_Size - LastSame; + a_NumSame += 1; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cPerlinNoise: + +cPerlinNoise::cPerlinNoise(void) : + m_Seed(0) +{ +} + + + + + +cPerlinNoise::cPerlinNoise(int a_Seed) : + m_Seed(a_Seed) +{ +} + + + + + +void cPerlinNoise::SetSeed(int a_Seed) +{ + m_Seed = a_Seed; +} + + + + + +void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude) +{ + m_Octaves.push_back(cOctave(m_Seed * (m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude)); +} + + + + + +void cPerlinNoise::Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash +) const +{ + if (m_Octaves.empty()) + { + // No work to be done + ASSERT(!"Perlin: No octaves to generate!"); + return; + } + + bool ShouldFreeWorkspace = (a_Workspace == NULL); + int ArrayCount = a_SizeX * a_SizeY; + if (ShouldFreeWorkspace) + { + a_Workspace = new NOISE_DATATYPE[ArrayCount]; + } + + // Generate the first octave directly into array: + m_Octaves.front().m_Noise.Generate2D( + a_Workspace, a_SizeX, a_SizeY, + a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency, + a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency + ); + NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] *= Amplitude; + } + + // Add each octave: + for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) + { + // Generate cubic noise for the octave: + itr->m_Noise.Generate2D( + a_Workspace, a_SizeX, a_SizeY, + a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency, + a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency + ); + // Add the cubic noise into the output: + NOISE_DATATYPE Amplitude = itr->m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] += a_Workspace[i] * Amplitude; + } + } + + if (ShouldFreeWorkspace) + { + delete[] a_Workspace; + } +} + + + + + +void cPerlinNoise::Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction + NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash +) const +{ + if (m_Octaves.empty()) + { + // No work to be done + ASSERT(!"Perlin: No octaves to generate!"); + return; + } + + bool ShouldFreeWorkspace = (a_Workspace == NULL); + int ArrayCount = a_SizeX * a_SizeY * a_SizeZ; + if (ShouldFreeWorkspace) + { + a_Workspace = new NOISE_DATATYPE[ArrayCount]; + } + + // Generate the first octave directly into array: + m_Octaves.front().m_Noise.Generate3D( + a_Workspace, a_SizeX, a_SizeY, a_SizeZ, + a_StartX * m_Octaves.front().m_Frequency, a_EndX * m_Octaves.front().m_Frequency, + a_StartY * m_Octaves.front().m_Frequency, a_EndY * m_Octaves.front().m_Frequency, + a_StartZ * m_Octaves.front().m_Frequency, a_EndZ * m_Octaves.front().m_Frequency + ); + NOISE_DATATYPE Amplitude = m_Octaves.front().m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] = a_Workspace[i] * Amplitude; + } + + // Add each octave: + for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) + { + // Generate cubic noise for the octave: + itr->m_Noise.Generate3D( + a_Workspace, a_SizeX, a_SizeY, a_SizeZ, + a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency, + a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency, + a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency + ); + // Add the cubic noise into the output: + NOISE_DATATYPE Amplitude = itr->m_Amplitude; + for (int i = 0; i < ArrayCount; i++) + { + a_Array[i] += a_Workspace[i] * Amplitude; + } + } + + if (ShouldFreeWorkspace) + { + delete[] a_Workspace; + } +} + + + + diff --git a/src/Noise.h b/src/Noise.h new file mode 100644 index 000000000..ea72c64e9 --- /dev/null +++ b/src/Noise.h @@ -0,0 +1,308 @@ + +// Noise.h + +// Declares the cNoise, cCubicNoise and cPerlinNoise classes for generating noise + +#pragma once + +// Some settings +#define NOISE_DATATYPE float + + + + + +#ifdef _MSC_VER + #define INLINE __forceinline +#else + #define INLINE inline +#endif + + + + + +class cNoise +{ +public: + cNoise(unsigned int a_Seed); + cNoise(const cNoise & a_Noise); + + // The following functions, if not marked INLINE, are about 20 % slower + INLINE NOISE_DATATYPE IntNoise1D(int a_X) const; + INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const; + INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const; + + // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify. + INLINE int IntNoise1DInt(int a_X) const; + INLINE int IntNoise2DInt(int a_X, int a_Y) const; + INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const; + + NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const; + NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const; + NOISE_DATATYPE CubicNoise1D (NOISE_DATATYPE a_X) const; + NOISE_DATATYPE SmoothNoise1D(int a_X) const; + + NOISE_DATATYPE CubicNoise2D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y) const; + + NOISE_DATATYPE CubicNoise3D (NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z) const; + + void SetSeed(unsigned int a_Seed) { m_Seed = a_Seed; } + + INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct); + INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); + INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct); + +private: + unsigned int m_Seed; +} ; + + + + + +class cCubicNoise +{ +public: + static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays. + + + cCubicNoise(int a_Seed); + + + void Generate1D( + NOISE_DATATYPE * a_Array, ///< Array to generate into + int a_SizeX, ///< Count of the array + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array + ) const; + + + void Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction + ) const; + + + void Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction + ) const; + +protected: + typedef NOISE_DATATYPE Workspace1D[4]; + typedef NOISE_DATATYPE Workspace2D[4][4]; + + cNoise m_Noise; // Used for integral rnd values + + #ifdef _DEBUG + // Statistics on the noise-space coords: + static int m_NumSingleX; + static int m_NumSingleXY; + static int m_NumSingleY; + static int m_NumCalls; + #endif // _DEBUG + + /// Calculates the integral and fractional parts along one axis. + void CalcFloorFrac( + int a_Size, + NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End, + int * a_Floor, NOISE_DATATYPE * a_Frac, + int * a_Same, int & a_NumSame + ) const; + + void UpdateWorkRnds2DX( + Workspace2D & a_WorkRnds, + Workspace1D & a_Interps, + int a_LastFloorX, int a_NewFloorX, + int a_FloorY, + NOISE_DATATYPE a_FractionY + ) const; +} ; + + + + + +class cPerlinNoise +{ +public: + cPerlinNoise(void); + cPerlinNoise(int a_Seed); + + + void SetSeed(int a_Seed); + + void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude); + + void Generate1D( + NOISE_DATATYPE * a_Array, ///< Array to generate into + int a_SizeX, ///< Count of the array + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array + NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash + ) const; + + + void Generate2D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y] + int a_SizeX, int a_SizeY, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash + ) const; + + + void Generate3D( + NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z] + int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction + NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction + NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction + NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction + NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash + ) const; + +protected: + class cOctave + { + public: + cCubicNoise m_Noise; + + NOISE_DATATYPE m_Frequency; // Coord multiplier + NOISE_DATATYPE m_Amplitude; // Value multiplier + + cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) : + m_Noise(a_Seed), + m_Frequency(a_Frequency), + m_Amplitude(a_Amplitude) + { + } + } ; + + typedef std::vector<cOctave> cOctaves; + + int m_Seed; + cOctaves m_Octaves; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Inline function definitions: +// These need to be in the header, otherwise linker error occur in MSVC + +NOISE_DATATYPE cNoise::IntNoise1D(int a_X) const +{ + int x = ((a_X * m_Seed) << 13) ^ a_X; + return (1 - (NOISE_DATATYPE)((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); + // returns a float number in the range of [-1, 1] +} + + + + + +NOISE_DATATYPE cNoise::IntNoise2D(int a_X, int a_Y) const +{ + int n = a_X + a_Y * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + return (1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824); + // returns a float number in the range of [-1, 1] +} + + + + + +NOISE_DATATYPE cNoise::IntNoise3D(int a_X, int a_Y, int a_Z) const +{ + int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; + n = (n << 13) ^ n; + return ((NOISE_DATATYPE)1 - (NOISE_DATATYPE)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); + // returns a float number in the range of [-1, 1] +} + + + + + +int cNoise::IntNoise1DInt(int a_X) const +{ + int x = ((a_X * m_Seed) << 13) ^ a_X; + return ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +int cNoise::IntNoise2DInt(int a_X, int a_Y) const +{ + int n = a_X + a_Y * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +int cNoise::IntNoise3DInt(int a_X, int a_Y, int a_Z) const +{ + int n = a_X + a_Y * 57 + a_Z * 57 * 57 + m_Seed * 57 * 57 * 57; + n = (n << 13) ^ n; + return ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +NOISE_DATATYPE cNoise::CubicInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct) +{ + NOISE_DATATYPE P = (a_D - a_C) - (a_A - a_B); + NOISE_DATATYPE Q = (a_A - a_B) - P; + NOISE_DATATYPE R = a_C - a_A; + NOISE_DATATYPE S = a_B; + + return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S; +} + + + + + +NOISE_DATATYPE cNoise::CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) +{ + const NOISE_DATATYPE ft = a_Pct * (NOISE_DATATYPE)3.1415927; + const NOISE_DATATYPE f = (1 - cos(ft)) * (NOISE_DATATYPE)0.5; + return a_A * (1 - f) + a_B * f; +} + + + + + +NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) +{ + return a_A * (1 - a_Pct) + a_B * a_Pct; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Global functions: + +extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase); +extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); + + + + diff --git a/src/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp new file mode 100644 index 000000000..55454a4b5 --- /dev/null +++ b/src/OSSupport/BlockingTCPLink.cpp @@ -0,0 +1,149 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BlockingTCPLink.h" + + + + + +#ifdef _WIN32 + #define MSG_NOSIGNAL (0) +#endif +#ifdef __MACH__ + #define MSG_NOSIGNAL (0) +#endif + + + + + +cBlockingTCPLink::cBlockingTCPLink(void) +{ +} + + + + + +cBlockingTCPLink::~cBlockingTCPLink() +{ + CloseSocket(); +} + + + + + +void cBlockingTCPLink::CloseSocket() +{ + if (!m_Socket.IsValid()) + { + m_Socket.CloseSocket(); + } +} + + + + + +bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort) +{ + ASSERT(!m_Socket.IsValid()); + if (m_Socket.IsValid()) + { + LOGWARN("WARNING: cTCPLink Connect() called while still connected."); + m_Socket.CloseSocket(); + } + + struct hostent *hp; + unsigned int addr; + struct sockaddr_in server; + + m_Socket = socket(AF_INET, SOCK_STREAM, 0); + if (!m_Socket.IsValid()) + { + LOGERROR("cTCPLink: Cannot create a socket"); + return false; + } + + addr = inet_addr(iAddress); + hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); + if (hp == NULL) + { + //LOGWARN("cTCPLink: gethostbyaddr returned NULL"); + hp = gethostbyname(iAddress); + if (hp == NULL) + { + LOGWARN("cTCPLink: Could not resolve %s", iAddress); + CloseSocket(); + return false; + } + } + + server.sin_addr.s_addr = *((unsigned long *)hp->h_addr); + server.sin_family = AF_INET; + server.sin_port = htons( (unsigned short)iPort); + if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server))) + { + LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort, cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); + CloseSocket(); + return false; + } + + return true; +} + + + + + +int cBlockingTCPLink::Send(char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ ) +{ + ASSERT(m_Socket.IsValid()); + if (!m_Socket.IsValid()) + { + LOGERROR("cBlockingTCPLink: Trying to send data without a valid connection!"); + return -1; + } + return m_Socket.Send(a_Data, a_Size); +} + + + + + +int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ ) +{ + ASSERT(m_Socket.IsValid()); + if (!m_Socket.IsValid()) + { + LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!"); + return -1; + } + return m_Socket.Send(a_Message, strlen(a_Message)); +} + + + + + +void cBlockingTCPLink::ReceiveData(AString & oData) +{ + ASSERT(m_Socket.IsValid()); + if (!m_Socket.IsValid()) + { + return; + } + + int Received = 0; + char Buffer[256]; + while ((Received = recv(m_Socket, Buffer, sizeof(Buffer), 0)) > 0) + { + oData.append(Buffer, Received); + } +} + + + + diff --git a/src/OSSupport/BlockingTCPLink.h b/src/OSSupport/BlockingTCPLink.h new file mode 100644 index 000000000..cb5f9e3f4 --- /dev/null +++ b/src/OSSupport/BlockingTCPLink.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "Socket.h" + + + + + +class cBlockingTCPLink // tolua_export +{ // tolua_export +public: // tolua_export + cBlockingTCPLink(void); // tolua_export + ~cBlockingTCPLink(); // tolua_export + + bool Connect( const char* a_Address, unsigned int a_Port ); // tolua_export + int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); // tolua_export + int SendMessage( const char* a_Message, int a_Flags = 0 ); // tolua_export + void CloseSocket(); // tolua_export + void ReceiveData(AString & oData); // tolua_export +protected: + + cSocket m_Socket; +}; // tolua_export + + + + diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp new file mode 100644 index 000000000..bda97e3a1 --- /dev/null +++ b/src/OSSupport/CriticalSection.cpp @@ -0,0 +1,188 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "IsThread.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCriticalSection: + +cCriticalSection::cCriticalSection() +{ + #ifdef _WIN32 + InitializeCriticalSection(&m_CriticalSection); + #else + pthread_mutexattr_init(&m_Attributes); + pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE); + + if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0) + { + LOGERROR("Could not initialize Critical Section!"); + } + #endif + + #ifdef _DEBUG + m_IsLocked = 0; + #endif // _DEBUG +} + + + + + +cCriticalSection::~cCriticalSection() +{ + #ifdef _WIN32 + DeleteCriticalSection(&m_CriticalSection); + #else + if (pthread_mutex_destroy(&m_CriticalSection) != 0) + { + LOGWARNING("Could not destroy Critical Section!"); + } + pthread_mutexattr_destroy(&m_Attributes); + #endif +} + + + + + +void cCriticalSection::Lock() +{ + #ifdef _WIN32 + EnterCriticalSection(&m_CriticalSection); + #else + pthread_mutex_lock(&m_CriticalSection); + #endif + + #ifdef _DEBUG + m_IsLocked += 1; + m_OwningThreadID = cIsThread::GetCurrentID(); + #endif // _DEBUG +} + + + + + +void cCriticalSection::Unlock() +{ + #ifdef _DEBUG + ASSERT(m_IsLocked > 0); + m_IsLocked -= 1; + #endif // _DEBUG + + #ifdef _WIN32 + LeaveCriticalSection(&m_CriticalSection); + #else + pthread_mutex_unlock(&m_CriticalSection); + #endif +} + + + + + +#ifdef _DEBUG +bool cCriticalSection::IsLocked(void) +{ + return (m_IsLocked > 0); +} + + + + + +bool cCriticalSection::IsLockedByCurrentThread(void) +{ + return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID())); +} +#endif // _DEBUG + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCSLock + +cCSLock::cCSLock(cCriticalSection * a_CS) + : m_CS(a_CS) + , m_IsLocked(false) +{ + Lock(); +} + + + + + +cCSLock::cCSLock(cCriticalSection & a_CS) + : m_CS(&a_CS) + , m_IsLocked(false) +{ + Lock(); +} + + + + + +cCSLock::~cCSLock() +{ + if (!m_IsLocked) + { + return; + } + Unlock(); +} + + + + + +void cCSLock::Lock(void) +{ + ASSERT(!m_IsLocked); + m_IsLocked = true; + m_CS->Lock(); +} + + + + + +void cCSLock::Unlock(void) +{ + ASSERT(m_IsLocked); + m_IsLocked = false; + m_CS->Unlock(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCSUnlock: + +cCSUnlock::cCSUnlock(cCSLock & a_Lock) : + m_Lock(a_Lock) +{ + m_Lock.Unlock(); +} + + + + + +cCSUnlock::~cCSUnlock() +{ + m_Lock.Lock(); +} + + + + diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h new file mode 100644 index 000000000..1bfe81439 --- /dev/null +++ b/src/OSSupport/CriticalSection.h @@ -0,0 +1,80 @@ + +#pragma once + + + + + +class cCriticalSection +{ +public: + cCriticalSection(void); + ~cCriticalSection(); + + void Lock(void); + void Unlock(void); + + #ifdef _DEBUG + bool IsLocked(void); + bool IsLockedByCurrentThread(void); + #endif // _DEBUG + +private: + #ifdef _DEBUG + int m_IsLocked; // Number of times this CS is locked + unsigned long m_OwningThreadID; + #endif // _DEBUG + + #ifdef _WIN32 + CRITICAL_SECTION m_CriticalSection; + #else // _WIN32 + pthread_mutex_t m_CriticalSection; + pthread_mutexattr_t m_Attributes; + #endif // else _WIN32 +} ALIGN_8; + + + + +/// RAII for cCriticalSection - locks the CS on creation, unlocks on destruction +class cCSLock +{ + cCriticalSection * m_CS; + + // Unlike a cCriticalSection, this object should be used from a single thread, therefore access to m_IsLocked is not threadsafe + // In Windows, it is an error to call cCriticalSection::Unlock() multiple times if the lock is not held, + // therefore we need to check this value whether we are locked or not. + bool m_IsLocked; + +public: + cCSLock(cCriticalSection * a_CS); + cCSLock(cCriticalSection & a_CS); + ~cCSLock(); + + // Temporarily unlock or re-lock: + void Lock(void); + void Unlock(void); + +private: + DISALLOW_COPY_AND_ASSIGN(cCSLock); +} ; + + + + + +/// Temporary RAII unlock for a cCSLock. Useful for unlock-wait-relock scenarios +class cCSUnlock +{ + cCSLock & m_Lock; +public: + cCSUnlock(cCSLock & a_Lock); + ~cCSUnlock(); + +private: + DISALLOW_COPY_AND_ASSIGN(cCSUnlock); +} ; + + + + diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp new file mode 100644 index 000000000..cbacbba17 --- /dev/null +++ b/src/OSSupport/Event.cpp @@ -0,0 +1,118 @@ + +// Event.cpp + +// Implements the cEvent object representing an OS-specific synchronization primitive that can be waited-for +// Implemented as an Event on Win and as a 1-semaphore on *nix + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Event.h" + + + + + +cEvent::cEvent(void) +{ +#ifdef _WIN32 + m_Event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (m_Event == NULL) + { + LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError()); + abort(); + } +#else // *nix + m_bIsNamed = false; + m_Event = new sem_t; + if (sem_init(m_Event, 0, 0)) + { + // This path is used by MacOS, because it doesn't support unnamed semaphores. + delete m_Event; + m_bIsNamed = true; + + AString EventName; + Printf(EventName, "cEvent%p", this); + m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0 ); + if (m_Event == SEM_FAILED) + { + LOGERROR("cEvent: Cannot create event, errno = %i. Aborting server.", errno); + abort(); + } + // Unlink the semaphore immediately - it will continue to function but will not pollute the namespace + // We don't store the name, so can't call this in the destructor + if (sem_unlink(EventName.c_str()) != 0) + { + LOGWARN("ERROR: Could not unlink cEvent. (%i)", errno); + } + } +#endif // *nix +} + + + + + +cEvent::~cEvent() +{ +#ifdef _WIN32 + CloseHandle(m_Event); +#else + if (m_bIsNamed) + { + if (sem_close(m_Event) != 0) + { + LOGERROR("ERROR: Could not close cEvent. (%i)", errno); + } + } + else + { + sem_destroy(m_Event); + delete m_Event; + } +#endif +} + + + + + +void cEvent::Wait(void) +{ + #ifdef _WIN32 + DWORD res = WaitForSingleObject(m_Event, INFINITE); + if (res != WAIT_OBJECT_0) + { + LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError()); + } + #else + int res = sem_wait(m_Event); + if (res != 0 ) + { + LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno); + } + #endif +} + + + + + +void cEvent::Set(void) +{ + #ifdef _WIN32 + if (!SetEvent(m_Event)) + { + LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError()); + } + #else + int res = sem_post(m_Event); + if (res != 0) + { + LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno); + } + #endif +} + + + + diff --git a/src/OSSupport/Event.h b/src/OSSupport/Event.h new file mode 100644 index 000000000..71f418c0c --- /dev/null +++ b/src/OSSupport/Event.h @@ -0,0 +1,47 @@ + +// Event.h + +// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for +// Implemented as an Event on Win and as a 1-semaphore on *nix + + + + + +#pragma once +#ifndef CEVENT_H_INCLUDED +#define CEVENT_H_INCLUDED + + + + + +class cEvent +{ +public: + cEvent(void); + ~cEvent(); + + void Wait(void); + void Set (void); + +private: + + #ifdef _WIN32 + HANDLE m_Event; + #else + sem_t * m_Event; + bool m_bIsNamed; + #endif +} ; + + + + + + +#endif // CEVENT_H_INCLUDED + + + + diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp new file mode 100644 index 000000000..9f7c0d439 --- /dev/null +++ b/src/OSSupport/File.cpp @@ -0,0 +1,452 @@ + +// cFile.cpp + +// Implements the cFile class providing an OS-independent abstraction of a file. + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "File.h" +#include <fstream> + + + + + +cFile::cFile(void) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + // Nothing needed yet +} + + + + + +cFile::cFile(const AString & iFileName, eMode iMode) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + Open(iFileName, iMode); +} + + + + + +cFile::~cFile() +{ + if (IsOpen()) + { + Close(); + } +} + + + + + +bool cFile::Open(const AString & iFileName, eMode iMode) +{ + ASSERT(!IsOpen()); // You should close the file before opening another one + + if (IsOpen()) + { + Close(); + } + + const char * Mode = NULL; + switch (iMode) + { + case fmRead: Mode = "rb"; break; + case fmWrite: Mode = "wb"; break; + case fmReadWrite: Mode = "rb+"; break; + default: + { + ASSERT(!"Unhandled file mode"); + return false; + } + } + m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), Mode); + if ((m_File == NULL) && (iMode == fmReadWrite)) + { + // Fix for MS not following C spec, opening "a" mode files for writing at the end only + // The file open operation has been tried with "read update", fails if file not found + // So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents. + // Simply re-open for read-writing, erasing existing contents: + m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), "wb+"); + } + return (m_File != NULL); +} + + + + + +void cFile::Close(void) +{ + if (!IsOpen()) + { + // Closing an unopened file is a legal nop + return; + } + + fclose(m_File); + m_File = NULL; +} + + + + + +bool cFile::IsOpen(void) const +{ + return (m_File != NULL); +} + + + + + +bool cFile::IsEOF(void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + // Unopened files behave as at EOF + return true; + } + + return (feof(m_File) != 0); +} + + + + + +int cFile::Read (void * iBuffer, int iNumBytes) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count +} + + + + + +int cFile::Write(const void * iBuffer, int iNumBytes) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count + return res; +} + + + + + +int cFile::Seek (int iPosition) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + if (fseek(m_File, iPosition, SEEK_SET) != 0) + { + return -1; + } + return ftell(m_File); +} + + + + + + +int cFile::Tell (void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return ftell(m_File); +} + + + + + +int cFile::GetSize(void) const +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int CurPos = ftell(m_File); + if (CurPos < 0) + { + return -1; + } + if (fseek(m_File, 0, SEEK_END) != 0) + { + return -1; + } + int res = ftell(m_File); + if (fseek(m_File, CurPos, SEEK_SET) != 0) + { + return -1; + } + return res; +} + + + + + +int cFile::ReadRestOfFile(AString & a_Contents) +{ + ASSERT(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int DataSize = GetSize() - Tell(); + + // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly + a_Contents.assign(DataSize, '\0'); + return Read((void *)a_Contents.data(), DataSize); +} + + + + + +bool cFile::Exists(const AString & a_FileName) +{ + cFile test(a_FileName, fmRead); + return test.IsOpen(); +} + + + + + +bool cFile::Delete(const AString & a_FileName) +{ + return (remove(a_FileName.c_str()) == 0); +} + + + + + +bool cFile::Rename(const AString & a_OrigFileName, const AString & a_NewFileName) +{ + return (rename(a_OrigFileName.c_str(), a_NewFileName.c_str()) == 0); +} + + + + + +bool cFile::Copy(const AString & a_SrcFileName, const AString & a_DstFileName) +{ + #ifdef _WIN32 + return (CopyFile(a_SrcFileName.c_str(), a_DstFileName.c_str(), true) != 0); + #else + // Other OSs don't have a direct CopyFile equivalent, do it the harder way: + std::ifstream src(a_SrcFileName.c_str(), std::ios::binary); + std::ofstream dst(a_DstFileName.c_str(), std::ios::binary); + if (dst.good()) + { + dst << src.rdbuf(); + return true; + } + else + { + return false; + } + #endif +} + + + + + +bool cFile::IsFolder(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISDIR(st.st_mode)); + #endif +} + + + + + +bool cFile::IsFile(const AString & a_Path) +{ + #ifdef _WIN32 + DWORD FileAttrib = GetFileAttributes(a_Path.c_str()); + return ((FileAttrib != INVALID_FILE_ATTRIBUTES) && ((FileAttrib & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)); + #else + struct stat st; + return ((stat(a_Path.c_str(), &st) == 0) && S_ISREG(st.st_mode)); + #endif +} + + + + + +int cFile::GetSize(const AString & a_FileName) +{ + struct stat st; + if (stat(a_FileName.c_str(), &st) == 0) + { + return st.st_size; + } + return -1; +} + + + + + +bool cFile::CreateFolder(const AString & a_FolderPath) +{ + #ifdef _WIN32 + return (CreateDirectory(a_FolderPath.c_str(), NULL) != 0); + #else + return (mkdir(a_FolderPath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0); + #endif +} + + + + + +AStringVector cFile::GetFolderContents(const AString & a_Folder) +{ + AStringVector AllFiles; + + #ifdef _WIN32 + + // If the folder name doesn't contain the terminating slash / backslash, add it: + AString FileFilter = a_Folder; + if ( + !FileFilter.empty() && + (FileFilter[FileFilter.length() - 1] != '\\') && + (FileFilter[FileFilter.length() - 1] != '/') + ) + { + FileFilter.push_back('\\'); + } + + // Find all files / folders: + FileFilter.append("*.*"); + HANDLE hFind; + WIN32_FIND_DATA FindFileData; + if ((hFind = FindFirstFile(FileFilter.c_str(), &FindFileData)) != INVALID_HANDLE_VALUE) + { + do + { + AllFiles.push_back(FindFileData.cFileName); + } while (FindNextFile(hFind, &FindFileData)); + FindClose(hFind); + } + + #else // _WIN32 + + DIR * dp; + struct dirent *dirp; + AString Folder = a_Folder; + if (Folder.empty()) + { + Folder = "."; + } + if ((dp = opendir(Folder.c_str())) == NULL) + { + LOGERROR("Error (%i) opening directory \"%s\"\n", errno, Folder.c_str()); + } + else + { + while ((dirp = readdir(dp)) != NULL) + { + AllFiles.push_back(dirp->d_name); + } + closedir(dp); + } + + #endif // else _WIN32 + + return AllFiles; +} + + + + + +AString cFile::ReadWholeFile(const AString & a_FileName) +{ + cFile f; + if (!f.Open(a_FileName, fmRead)) + { + return ""; + } + AString Contents; + f.ReadRestOfFile(Contents); + return Contents; +} + + + + + +int cFile::Printf(const char * a_Fmt, ...) +{ + AString buf; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(buf, a_Fmt, args); + va_end(args); + return Write(buf.c_str(), buf.length()); +} + + + + diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h new file mode 100644 index 000000000..01663a229 --- /dev/null +++ b/src/OSSupport/File.h @@ -0,0 +1,144 @@ + +// cFile.h + +// Interfaces to the cFile class providing an OS-independent abstraction of a file. + +/* +The object is optimized towards binary reads. +The object has no multithreading locks, don't use from multiple threads! +Usage: +1, Construct a cFile instance (no-param constructor) +2, Open a file using Open(), check return value for success +3, Read / write +4, Destroy the instance + +-- OR -- + +1, Construct a cFile instance opening the file (filename-param constructor) +2, Check if the file was opened using IsOpen() +3, Read / write +4, Destroy the instance +*/ + + + + + +#pragma once + + + + + +#ifndef _WIN32 + #define USE_STDIO_FILE +#endif // _WIN32 + +// DEBUG: +#define USE_STDIO_FILE + + + + + +// tolua_begin + +class cFile +{ +public: + + // tolua_end + + #ifdef _WIN32 + static const char PathSeparator = '\\'; + #else + static const char PathSeparator = '/'; + #endif + + /// The mode in which to open the file + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning + } ; + + /// Simple constructor - creates an unopened file object, use Open() to open / create a real file + cFile(void); + + /// Constructs and opens / creates the file specified, use IsOpen() to check for success + cFile(const AString & iFileName, eMode iMode); + + /// Auto-closes the file, if open + ~cFile(); + + bool Open(const AString & iFileName, eMode iMode); + void Close(void); + bool IsOpen(void) const; + bool IsEOF(void) const; + + /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open + int Read (void * iBuffer, int iNumBytes); + + /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open + int Write(const void * iBuffer, int iNumBytes); + + /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open + int Seek (int iPosition); + + /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open + int Tell (void) const; + + /// Returns the size of file, in bytes, or -1 for failure; asserts if not open + int GetSize(void) const; + + /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error + int ReadRestOfFile(AString & a_Contents); + + // tolua_begin + + /// Returns true if the file specified exists + static bool Exists(const AString & a_FileName); + + /// Deletes a file, returns true if successful + static bool Delete(const AString & a_FileName); + + /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! + static bool Rename(const AString & a_OrigPath, const AString & a_NewPath); + + /// Copies a file, returns true if successful. + static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName); + + /// Returns true if the specified path is a folder + static bool IsFolder(const AString & a_Path); + + /// Returns true if the specified path is a regular file + static bool IsFile(const AString & a_Path); + + /// Returns the size of the file, or a negative number on error + static int GetSize(const AString & a_FileName); + + /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute + static bool CreateFolder(const AString & a_FolderPath); + + /// Returns the entire contents of the specified file as a string. Returns empty string on error. + static AString ReadWholeFile(const AString & a_FileName); + + // tolua_end + + /// Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). + static AStringVector GetFolderContents(const AString & a_Folder); // Exported in ManualBindings.cpp + + int Printf(const char * a_Fmt, ...); + +private: + #ifdef USE_STDIO_FILE + FILE * m_File; + #else + HANDLE m_File; + #endif +} ; // tolua_export + + + + diff --git a/src/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp new file mode 100644 index 000000000..cbf6be6c4 --- /dev/null +++ b/src/OSSupport/GZipFile.cpp @@ -0,0 +1,107 @@ + +// GZipFile.cpp + +// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + +#include "Globals.h" +#include "GZipFile.h" + + + + + +cGZipFile::cGZipFile(void) : + m_File(NULL) +{ +} + + + + + +cGZipFile::~cGZipFile() +{ + Close(); +} + + + + + +bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode) +{ + if (m_File != NULL) + { + ASSERT(!"A file is already open in this object"); + return false; + } + m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w"); + m_Mode = a_Mode; + return (m_File != NULL); +} + + + + + +void cGZipFile::Close(void) +{ + if (m_File != NULL) + { + gzclose(m_File); + m_File = NULL; + } +} + + + + + +int cGZipFile::ReadRestOfFile(AString & a_Contents) +{ + if (m_File == NULL) + { + ASSERT(!"No file has been opened"); + return -1; + } + + if (m_Mode != fmRead) + { + ASSERT(!"Bad file mode, cannot read"); + return -1; + } + + // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! + int NumBytesRead = 0; + char Buffer[64 KiB]; + while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) + { + a_Contents.append(Buffer, NumBytesRead); + } + return NumBytesRead; +} + + + + + +bool cGZipFile::Write(const char * a_Contents, int a_Size) +{ + if (m_File == NULL) + { + ASSERT(!"No file has been opened"); + return false; + } + + if (m_Mode != fmWrite) + { + ASSERT(!"Bad file mode, cannot write"); + return false; + } + + return (gzwrite(m_File, a_Contents, a_Size) != 0); +} + + + + diff --git a/src/OSSupport/GZipFile.h b/src/OSSupport/GZipFile.h new file mode 100644 index 000000000..dcc171a2a --- /dev/null +++ b/src/OSSupport/GZipFile.h @@ -0,0 +1,52 @@ + +// GZipFile.h + +// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + + + + + +#pragma once + +#include "lib/zlib/zlib.h" + + + + + +class cGZipFile +{ +public: + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + } ; + + cGZipFile(void); + ~cGZipFile(); + + /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object. + bool Open(const AString & a_FileName, eMode a_Mode); + + /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode + void Close(void); + + /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error + int ReadRestOfFile(AString & a_Contents); + + /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. + bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); } + + bool Write(const char * a_Data, int a_Size); + +protected: + gzFile m_File; + eMode m_Mode; +} ; + + + + + diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp new file mode 100644 index 000000000..4da9f9949 --- /dev/null +++ b/src/OSSupport/IsThread.cpp @@ -0,0 +1,172 @@ + +// IsThread.cpp + +// Implements the cIsThread class representing an OS-independent wrapper for a class that implements a thread. +// This class will eventually suupersede the old cThread class + +#include "Globals.h" + +#include "IsThread.h" + + + + + +// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: +#if defined(_MSC_VER) && defined(_DEBUG) +// +// Usage: SetThreadName (-1, "MainThread"); +// + +static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) +{ + struct + { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero + } info; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} +#endif // _MSC_VER && _DEBUG + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cIsThread: + +cIsThread::cIsThread(const AString & iThreadName) : + m_ThreadName(iThreadName), + m_ShouldTerminate(false), + m_Handle(NULL_HANDLE) +{ +} + + + + + +cIsThread::~cIsThread() +{ + m_ShouldTerminate = true; + Wait(); +} + + + + + +bool cIsThread::Start(void) +{ + ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread? + #ifdef _WIN32 + // Create the thread suspended, so that the mHandle variable is valid in the thread procedure + DWORD ThreadID = 0; + m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); + if (m_Handle == NULL) + { + LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError()); + return false; + } + ResumeThread(m_Handle); + + #if defined(_DEBUG) && defined(_MSC_VER) + // Thread naming is available only in MSVC + if (!m_ThreadName.empty()) + { + SetThreadName(ThreadID, m_ThreadName.c_str()); + } + #endif // _DEBUG and _MSC_VER + + #else // _WIN32 + if (pthread_create(&m_Handle, NULL, thrExecute, this)) + { + LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); + return false; + } + #endif // else _WIN32 + + return true; +} + + + + + +void cIsThread::Stop(void) +{ + if (m_Handle == NULL_HANDLE) + { + return; + } + m_ShouldTerminate = true; + Wait(); +} + + + + + +bool cIsThread::Wait(void) +{ + if (m_Handle == NULL) + { + return true; + } + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Waiting for thread %s to finish", m_ThreadName.c_str()); + #endif // LOGD + + #ifdef _WIN32 + int res = WaitForSingleObject(m_Handle, INFINITE); + m_Handle = NULL; + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Thread %s finished", m_ThreadName.c_str()); + #endif // LOGD + + return (res == WAIT_OBJECT_0); + #else // _WIN32 + int res = pthread_join(m_Handle, NULL); + m_Handle = NULL; + + #ifdef LOGD // ProtoProxy doesn't have LOGD + LOGD("Thread %s finished", m_ThreadName.c_str()); + #endif // LOGD + + return (res == 0); + #endif // else _WIN32 +} + + + + + +unsigned long cIsThread::GetCurrentID(void) +{ + #ifdef _WIN32 + return (unsigned long) GetCurrentThreadId(); + #else + return (unsigned long) pthread_self(); + #endif +} + + + + diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h new file mode 100644 index 000000000..b8784ea33 --- /dev/null +++ b/src/OSSupport/IsThread.h @@ -0,0 +1,100 @@ + +// IsThread.h + +// Interfaces to the cIsThread class representing an OS-independent wrapper for a class that implements a thread. +// This class will eventually suupersede the old cThread class + +/* +Usage: +To have a new thread, declare a class descending from cIsClass. +Then override its Execute() method to provide your thread processing. +In the descending class' constructor call the Start() method to start the thread once you're finished with initialization. +*/ + + + + + +#pragma once +#ifndef CISTHREAD_H_INCLUDED +#define CISTHREAD_H_INCLUDED + + + + + +class cIsThread +{ +protected: + /// This is the main thread entrypoint + virtual void Execute(void) = 0; + + /// The overriden Execute() method should check this value periodically and terminate if this is true + volatile bool m_ShouldTerminate; + +public: + cIsThread(const AString & iThreadName); + ~cIsThread(); + + /// Starts the thread; returns without waiting for the actual start + bool Start(void); + + /// Signals the thread to terminate and waits until it's finished + void Stop(void); + + /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag + bool Wait(void); + + /// Returns the OS-dependent thread ID for the caller's thread + static unsigned long GetCurrentID(void); + +protected: + AString m_ThreadName; + + // Value used for "no handle": + #ifdef _WIN32 + #define NULL_HANDLE NULL + #else + #define NULL_HANDLE 0 + #endif + + #ifdef _WIN32 + + HANDLE m_Handle; + + static DWORD_PTR __stdcall thrExecute(LPVOID a_Param) + { + // Create a window so that the thread can be identified by 3rd party tools: + HWND IdentificationWnd = CreateWindow("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL); + + // Run the thread: + ((cIsThread *)a_Param)->Execute(); + + // Destroy the identification window: + DestroyWindow(IdentificationWnd); + + return 0; + } + + #else // _WIN32 + + pthread_t m_Handle; + + static void * thrExecute(void * a_Param) + { + ((cIsThread *)a_Param)->Execute(); + return NULL; + } + + #endif // else _WIN32 +} ; + + + + + +#endif // CISTHREAD_H_INCLUDED + + + + diff --git a/src/OSSupport/ListenThread.cpp b/src/OSSupport/ListenThread.cpp new file mode 100644 index 000000000..ba3198764 --- /dev/null +++ b/src/OSSupport/ListenThread.cpp @@ -0,0 +1,238 @@ + +// ListenThread.cpp + +// Implements the cListenThread class representing the thread that listens for client connections + +#include "Globals.h" +#include "ListenThread.h" + + + + + +cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : + super(Printf("ListenThread %s", a_ServiceName.c_str())), + m_Callback(a_Callback), + m_Family(a_Family), + m_ShouldReuseAddr(false), + m_ServiceName(a_ServiceName) +{ +} + + + + + +cListenThread::~cListenThread() +{ + Stop(); +} + + + + + +bool cListenThread::Initialize(const AString & a_PortsString) +{ + ASSERT(m_Sockets.empty()); // Not yet started + + if (!CreateSockets(a_PortsString)) + { + return false; + } + + return true; +} + + + + + +bool cListenThread::Start(void) +{ + if (m_Sockets.empty()) + { + // There are no sockets listening, either forgotten to initialize or the user specified no listening ports + // Report as successful, though + return true; + } + return super::Start(); +} + + + + + +void cListenThread::Stop(void) +{ + if (m_Sockets.empty()) + { + // No sockets means no thread was running in the first place + return; + } + + m_ShouldTerminate = true; + + // Close one socket to wake the thread up from the select() call + m_Sockets[0].CloseSocket(); + + // Wait for the thread to finish + super::Wait(); + + // Close all the listening sockets: + for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr) + { + itr->CloseSocket(); + } // for itr - m_Sockets[] + m_Sockets.clear(); +} + + + + + +void cListenThread::SetReuseAddr(bool a_Reuse) +{ + ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet + + m_ShouldReuseAddr = a_Reuse; +} + + + + + +bool cListenThread::CreateSockets(const AString & a_PortsString) +{ + AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); + + if (Ports.empty()) + { + return false; + } + + AString FamilyStr = m_ServiceName; + switch (m_Family) + { + case cSocket::IPv4: FamilyStr.append(" IPv4"); break; + case cSocket::IPv6: FamilyStr.append(" IPv6"); break; + default: + { + ASSERT(!"Unknown address family"); + break; + } + } + + for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) + { + int Port = atoi(itr->c_str()); + if ((Port <= 0) || (Port > 65535)) + { + LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); + continue; + } + m_Sockets.push_back(cSocket::CreateSocket(m_Family)); + if (!m_Sockets.back().IsValid()) + { + LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (m_ShouldReuseAddr) + { + if (!m_Sockets.back().SetReuseAddress()) + { + LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + } + } + + // Bind to port: + bool res = false; + switch (m_Family) + { + case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; + case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; + default: + { + ASSERT(!"Unknown address family"); + res = false; + } + } + if (!res) + { + LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (!m_Sockets.back().Listen()) + { + LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); + } // for itr - Ports[] + + return !(m_Sockets.empty()); +} + + + + + +void cListenThread::Execute(void) +{ + if (m_Sockets.empty()) + { + LOGD("Empty cListenThread, ending thread now."); + return; + } + + // Find the highest socket number: + cSocket::xSocket Highest = m_Sockets[0].GetSocket(); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->GetSocket() > Highest) + { + Highest = itr->GetSocket(); + } + } // for itr - m_Sockets[] + + while (!m_ShouldTerminate) + { + // Put all sockets into a FD set: + fd_set fdRead; + FD_ZERO(&fdRead); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + FD_SET(itr->GetSocket(), &fdRead); + } // for itr - m_Sockets[] + + timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: + tv.tv_sec = 1; + tv.tv_usec = 0; + if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1) + { + LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) + { + cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); + if (Client.IsValid()) + { + m_Callback.OnConnectionAccepted(Client); + } + } + } // for itr - m_Sockets[] + } // while (!m_ShouldTerminate) +} + + + + diff --git a/src/OSSupport/ListenThread.h b/src/OSSupport/ListenThread.h new file mode 100644 index 000000000..4e337d814 --- /dev/null +++ b/src/OSSupport/ListenThread.h @@ -0,0 +1,83 @@ + +// ListenThread.h + +// Declares the cListenThread class representing the thread that listens for client connections + + + + + +#pragma once + +#include "IsThread.h" +#include "Socket.h" + + + + + +// fwd: +class cServer; + + + + + +class cListenThread : + public cIsThread +{ + typedef cIsThread super; + +public: + /// Used as the callback for connection events + class cCallback + { + public: + /// This callback is called whenever a socket connection is accepted + virtual void OnConnectionAccepted(cSocket & a_Socket) = 0; + } ; + + cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = ""); + ~cListenThread(); + + /// Creates all the sockets, returns trus if successful, false if not. + bool Initialize(const AString & a_PortsString); + + bool Start(void); + + void Stop(void); + + /// Call before Initialize() to set the "reuse" flag on the sockets + void SetReuseAddr(bool a_Reuse = true); + +protected: + typedef std::vector<cSocket> cSockets; + + /// The callback which to notify of incoming connections + cCallback & m_Callback; + + /// Socket address family to use + cSocket::eFamily m_Family; + + /// Sockets that are being monitored + cSockets m_Sockets; + + /// If set to true, the SO_REUSEADDR socket option is set to true + bool m_ShouldReuseAddr; + + /// Name of the service that's listening on the ports; for logging purposes only + AString m_ServiceName; + + + /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString. + Returns true if successful and at least one socket has been created + */ + bool CreateSockets(const AString & a_PortsString); + + // cIsThread override: + virtual void Execute(void) override; +} ; + + + + diff --git a/src/OSSupport/Semaphore.cpp b/src/OSSupport/Semaphore.cpp new file mode 100644 index 000000000..468de6858 --- /dev/null +++ b/src/OSSupport/Semaphore.cpp @@ -0,0 +1,91 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + + + + + +cSemaphore::cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount /* = 0 */ ) +#ifndef _WIN32 + : m_bNamed( false ) +#endif +{ +#ifndef _WIN32 + (void)a_MaxCount; + m_Handle = new sem_t; + if (sem_init( (sem_t*)m_Handle, 0, 0)) + { + LOG("WARNING cSemaphore: Could not create unnamed semaphore, fallback to named."); + delete (sem_t*)m_Handle; // named semaphores return their own address + m_bNamed = true; + + AString Name; + Printf(Name, "cSemaphore%p", this ); + m_Handle = sem_open(Name.c_str(), O_CREAT, 777, a_InitialCount); + if( m_Handle == SEM_FAILED ) + { + LOG("ERROR: Could not create Semaphore. (%i)", errno ); + } + else + { + if( sem_unlink(Name.c_str()) != 0 ) + { + LOG("ERROR: Could not unlink cSemaphore. (%i)", errno); + } + } + } +#else + m_Handle = CreateSemaphore( + NULL, // security attribute + a_InitialCount, // initial count + a_MaxCount, // maximum count + 0 // name (optional) + ); +#endif +} + +cSemaphore::~cSemaphore() +{ +#ifdef _WIN32 + CloseHandle( m_Handle ); +#else + if( m_bNamed ) + { + if( sem_close( (sem_t*)m_Handle ) != 0 ) + { + LOG("ERROR: Could not close cSemaphore. (%i)", errno); + } + } + else + { + sem_destroy( (sem_t*)m_Handle ); + delete (sem_t*)m_Handle; + } + m_Handle = 0; + +#endif +} + +void cSemaphore::Wait() +{ +#ifndef _WIN32 + if( sem_wait( (sem_t*)m_Handle ) != 0) + { + LOG("ERROR: Could not wait for cSemaphore. (%i)", errno); + } +#else + WaitForSingleObject( m_Handle, INFINITE); +#endif +} + +void cSemaphore::Signal() +{ +#ifndef _WIN32 + if( sem_post( (sem_t*)m_Handle ) != 0 ) + { + LOG("ERROR: Could not signal cSemaphore. (%i)", errno); + } +#else + ReleaseSemaphore( m_Handle, 1, NULL ); +#endif +} diff --git a/src/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h new file mode 100644 index 000000000..fbe8907f1 --- /dev/null +++ b/src/OSSupport/Semaphore.h @@ -0,0 +1,17 @@ +#pragma once + +class cSemaphore +{ +public: + cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount = 0 ); + ~cSemaphore(); + + void Wait(); + void Signal(); +private: + void* m_Handle; // HANDLE pointer + +#ifndef _WIN32 + bool m_bNamed; +#endif +}; diff --git a/src/OSSupport/Sleep.cpp b/src/OSSupport/Sleep.cpp new file mode 100644 index 000000000..70fb06b40 --- /dev/null +++ b/src/OSSupport/Sleep.cpp @@ -0,0 +1,19 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 + #include <unistd.h> +#endif + + + + + +void cSleep::MilliSleep( unsigned int a_MilliSeconds ) +{ +#ifdef _WIN32 + Sleep(a_MilliSeconds); // Don't tick too much +#else + usleep(a_MilliSeconds*1000); +#endif +} diff --git a/src/OSSupport/Sleep.h b/src/OSSupport/Sleep.h new file mode 100644 index 000000000..5298c15da --- /dev/null +++ b/src/OSSupport/Sleep.h @@ -0,0 +1,7 @@ +#pragma once + +class cSleep +{ +public: + static void MilliSleep( unsigned int a_MilliSeconds ); +};
\ No newline at end of file diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp new file mode 100644 index 000000000..f25f800c2 --- /dev/null +++ b/src/OSSupport/Socket.cpp @@ -0,0 +1,393 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Socket.h" + +#ifndef _WIN32 + #include <netdb.h> + #include <unistd.h> + #include <arpa/inet.h> //inet_ntoa() +#else + #define socklen_t int +#endif + + + + + +cSocket::cSocket(xSocket a_Socket) + : m_Socket(a_Socket) +{ +} + + + + + +cSocket::~cSocket() +{ + // Do NOT close the socket; this class is an API wrapper, not a RAII! +} + + + + + +cSocket::operator cSocket::xSocket() const +{ + return m_Socket; +} + + + + + +cSocket::xSocket cSocket::GetSocket() const +{ + return m_Socket; +} + + + + + +bool cSocket::IsValidSocket(cSocket::xSocket a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + +void cSocket::CloseSocket() +{ + #ifdef _WIN32 + + closesocket(m_Socket); + + #else // _WIN32 + + if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH); + { + LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); + } + if (close(m_Socket) != 0) + { + LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str()); + } + + #endif // else _WIN32 + + // Invalidate the socket so that this object can be re-used for another connection + m_Socket = INVALID_SOCKET; +} + + + + + +AString cSocket::GetErrorString( int a_ErrNo ) +{ + char buffer[ 1024 ]; + AString Out; + + #ifdef _WIN32 + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL); + Printf(Out, "%d: %s", a_ErrNo, buffer); + if (!Out.empty() && (Out[Out.length() - 1] == '\n')) + { + Out.erase(Out.length() - 2); + } + return Out; + + #else // _WIN32 + + // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r(): + + #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r() + + char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); + if( res != NULL ) + { + Printf(Out, "%d: %s", a_ErrNo, res); + return Out; + } + + #else // XSI version of strerror_r(): + + int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) ); + if( res == 0 ) + { + Printf(Out, "%d: %s", a_ErrNo, buffer); + return Out; + } + + #endif // strerror_r() version + + else + { + Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo); + return Out; + } + + #endif // else _WIN32 +} + + + + +int cSocket::GetLastError() +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + + + + + +bool cSocket::SetReuseAddress(void) +{ + #if defined(_WIN32) || defined(ANDROID_NDK) + char yes = 1; + #else + int yes = 1; + #endif + return (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == 0); +} + + + + + +int cSocket::WSAStartup(void) +{ +#ifdef _WIN32 + WSADATA wsaData; + memset(&wsaData, 0, sizeof(wsaData)); + return ::WSAStartup(MAKEWORD(2, 2),&wsaData); +#else + return 0; +#endif +} + + + + + +cSocket cSocket::CreateSocket(eFamily a_Family) +{ + return socket((int)a_Family, SOCK_STREAM, 0); +} + + + + + +bool cSocket::BindToAnyIPv4(unsigned short a_Port) +{ + sockaddr_in local; + memset(&local, 0, sizeof(local)); + + local.sin_family = AF_INET; + local.sin_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::BindToAnyIPv6(unsigned short a_Port) +{ + sockaddr_in6 local; + memset(&local, 0, sizeof(local)); + + local.sin6_family = AF_INET6; + local.sin6_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr *)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::BindToLocalhostIPv4(unsigned short a_Port) +{ + sockaddr_in local; + memset(&local, 0, sizeof(local)); + + local.sin_family = AF_INET;; + local.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + local.sin_port = htons((u_short)a_Port); + + return (bind(m_Socket, (sockaddr*)&local, sizeof(local)) == 0); +} + + + + + +bool cSocket::Listen(int a_Backlog) +{ + return (listen(m_Socket, a_Backlog) == 0); +} + + + + + +cSocket cSocket::AcceptIPv4(void) +{ + sockaddr_in from; + socklen_t fromlen = sizeof(from); + + cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); + + if (SClient.IsValid() && (from.sin_addr.s_addr != 0)) // Get IP in string form + { + SClient.m_IPString = inet_ntoa(from.sin_addr); + } + return SClient; +} + + + + + +cSocket cSocket::AcceptIPv6(void) +{ + sockaddr_in6 from; + socklen_t fromlen = sizeof(from); + + cSocket SClient = accept(m_Socket, (sockaddr *)&from, &fromlen); + + // Get IP in string form: + if (SClient.IsValid()) + { + #if defined(_WIN32) + // Windows XP doesn't have inet_ntop, so we need to improvise. And MSVC has different headers than GCC + #ifdef _MSC_VER + // MSVC version + Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", + from.sin6_addr.u.Word[0], + from.sin6_addr.u.Word[1], + from.sin6_addr.u.Word[2], + from.sin6_addr.u.Word[3], + from.sin6_addr.u.Word[4], + from.sin6_addr.u.Word[5], + from.sin6_addr.u.Word[6], + from.sin6_addr.u.Word[7] + ); + #else // _MSC_VER + // MinGW + Printf(SClient.m_IPString, "%x:%x:%x:%x:%x:%x:%x:%x", + from.sin6_addr.s6_addr16[0], + from.sin6_addr.s6_addr16[1], + from.sin6_addr.s6_addr16[2], + from.sin6_addr.s6_addr16[3], + from.sin6_addr.s6_addr16[4], + from.sin6_addr.s6_addr16[5], + from.sin6_addr.s6_addr16[6], + from.sin6_addr.s6_addr16[7] + ); + #endif // else _MSC_VER + #else + char buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(from.sin6_addr), buffer, sizeof(buffer)); + SClient.m_IPString.assign(buffer); + #endif // _WIN32 + } + return SClient; +} + + + + + +bool cSocket::ConnectToLocalhostIPv4(unsigned short a_Port) +{ + sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server.sin_port = htons(a_Port); + return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); +} + + + + + +bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port) +{ + // First try IP Address string to hostent conversion, because it's faster + unsigned long addr = inet_addr(a_HostNameOrAddr.c_str()); + if (addr == INADDR_NONE) + { + // It is not an IP Address string, but rather a regular hostname, resolve: + hostent * hp = gethostbyname(a_HostNameOrAddr.c_str()); + if (hp == NULL) + { + LOGWARNING("%s: Could not resolve hostname \"%s\"", __FUNCTION__, a_HostNameOrAddr.c_str()); + CloseSocket(); + return false; + } + addr = *((unsigned long*)hp->h_addr); + } + + sockaddr_in server; + server.sin_addr.s_addr = addr; + server.sin_family = AF_INET; + server.sin_port = htons((unsigned short)a_Port); + return (connect(m_Socket, (sockaddr *)&server, sizeof(server)) == 0); +} + + + + + +int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags) +{ + return recv(m_Socket, a_Buffer, a_Length, a_Flags); +} + + + + + +int cSocket::Send(const char * a_Buffer, unsigned int a_Length) +{ + return send(m_Socket, a_Buffer, a_Length, 0); +} + + + + + +unsigned short cSocket::GetPort(void) const +{ + ASSERT(IsValid()); + + sockaddr_in Addr; + socklen_t AddrSize = sizeof(Addr); + if (getsockname(m_Socket, (sockaddr *)&Addr, &AddrSize) != 0) + { + return 0; + } + return ntohs(Addr.sin_port); +} + + + + diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h new file mode 100644 index 000000000..81bfd28fc --- /dev/null +++ b/src/OSSupport/Socket.h @@ -0,0 +1,102 @@ + +#pragma once + + + + + +class cSocket +{ +public: + enum eFamily + { + IPv4 = AF_INET, + IPv6 = AF_INET6, + } ; + +#ifdef _WIN32 + typedef SOCKET xSocket; +#else + typedef int xSocket; + static const int INVALID_SOCKET = -1; +#endif + + cSocket(void) : m_Socket(INVALID_SOCKET) {} + cSocket(xSocket a_Socket); + ~cSocket(); + + bool IsValid(void) const { return IsValidSocket(m_Socket); } + void CloseSocket(void); + + operator xSocket(void) const; + xSocket GetSocket(void) const; + + bool operator == (const cSocket & a_Other) {return m_Socket == a_Other.m_Socket; } + + void SetSocket(xSocket a_Socket); + + /// Sets the address-reuse socket flag; returns true on success + bool SetReuseAddress(void); + + /// Initializes the network stack. Returns 0 on success, or another number as an error code. + static int WSAStartup(void); + + static AString GetErrorString(int a_ErrNo); + static int GetLastError(); + static AString GetLastErrorString(void) + { + return GetErrorString(GetLastError()); + } + + /// Creates a new socket of the specified address family + static cSocket CreateSocket(eFamily a_Family); + + inline static bool IsSocketError(int a_ReturnedValue) + { + #ifdef _WIN32 + return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0); + #else + return (a_ReturnedValue <= 0); + #endif + } + + static bool IsValidSocket(xSocket a_Socket); + + static const unsigned short ANY_PORT = 0; // When given to Bind() functions, they will find a free port + static const int DEFAULT_BACKLOG = 10; + + /// Binds to the specified port on "any" interface (0.0.0.0). Returns true if successful. + bool BindToAnyIPv4(unsigned short a_Port); + + /// Binds to the specified port on "any" interface (::/128). Returns true if successful. + bool BindToAnyIPv6(unsigned short a_Port); + + /// Binds to the specified port on localhost interface (127.0.0.1) through IPv4. Returns true if successful. + bool BindToLocalhostIPv4(unsigned short a_Port); + + /// Sets the socket to listen for incoming connections. Returns true if successful. + bool Listen(int a_Backlog = DEFAULT_BACKLOG); + + /// Accepts an IPv4 incoming connection. Blocks if none available. + cSocket AcceptIPv4(void); + + /// Accepts an IPv6 incoming connection. Blocks if none available. + cSocket AcceptIPv6(void); + + /// Connects to a localhost socket on the specified port using IPv4; returns true if successful. + bool ConnectToLocalhostIPv4(unsigned short a_Port); + + /// Connects to the specified host or string IP address and port, using IPv4. Returns true if successful. + bool ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port); + + int Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags); + int Send (const char * a_Buffer, unsigned int a_Length); + + unsigned short GetPort(void) const; // Returns 0 on failure + + const AString & GetIPString(void) const { return m_IPString; } + +private: + xSocket m_Socket; + AString m_IPString; +};
\ No newline at end of file diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp new file mode 100644 index 000000000..3e505616c --- /dev/null +++ b/src/OSSupport/SocketThreads.cpp @@ -0,0 +1,675 @@ + +// cSocketThreads.cpp + +// Implements the cSocketThreads class representing the heart of MCS's client networking. +// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support +// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 + +#include "Globals.h" +#include "SocketThreads.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSocketThreads: + +cSocketThreads::cSocketThreads(void) +{ +} + + + + + +cSocketThreads::~cSocketThreads() +{ + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + delete *itr; + } // for itr - m_Threads[] + m_Threads.clear(); +} + + + + + + +bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client) +{ + // Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client + + // Try to add to existing threads: + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->IsValid() && (*itr)->HasEmptySlot()) + { + (*itr)->AddClient(a_Socket, a_Client); + return true; + } + } + + // No thread has free space, create a new one: + LOGD("Creating a new cSocketThread (currently have %d)", m_Threads.size()); + cSocketThread * Thread = new cSocketThread(this); + if (!Thread->Start()) + { + // There was an error launching the thread (but it was already logged along with the reason) + LOGERROR("A new cSocketThread failed to start"); + delete Thread; + return false; + } + Thread->AddClient(a_Socket, a_Client); + m_Threads.push_back(Thread); + return true; +} + + + + + +/* +void cSocketThreads::RemoveClient(const cSocket * a_Socket) +{ + // Remove the socket (and associated client) from processing + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->RemoveSocket(a_Socket)) + { + return; + } + } // for itr - m_Threads[] + + // Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime + // ASSERT(!"Removing an unknown socket"); +} +*/ + + + + + +void cSocketThreads::RemoveClient(const cCallback * a_Client) +{ + // Remove the associated socket and the client from processing + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->RemoveClient(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + ASSERT(!"Removing an unknown client"); +} + + + + + +void cSocketThreads::NotifyWrite(const cCallback * a_Client) +{ + // Notifies the thread responsible for a_Client that the client has something to write + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->NotifyWrite(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + // Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too + // ASSERT(!"Notifying write to an unknown client"); +} + + + + + +void cSocketThreads::Write(const cCallback * a_Client, const AString & a_Data) +{ + // Puts a_Data into outgoing data queue for a_Client + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->Write(a_Client, a_Data)) + { + return; + } + } // for itr - m_Threads[] + + // This may be perfectly legal, if the socket has been destroyed and the client is finishing up + // ASSERT(!"Writing to an unknown socket"); +} + + + + + +/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made +void cSocketThreads::StopReading(const cCallback * a_Client) +{ + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->StopReading(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + // Cannot assert, this normally happens if the socket is closed before the client deinitializes + // ASSERT(!"Stopping reading on an unknown client"); +} + + + + + +/// Queues the socket for closing, as soon as its outgoing data is sent +void cSocketThreads::QueueClose(const cCallback * a_Client) +{ + LOGD("QueueClose(client %p)", a_Client); + + cCSLock Lock(m_CS); + for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr) + { + if ((*itr)->QueueClose(a_Client)) + { + return; + } + } // for itr - m_Threads[] + + ASSERT(!"Queueing close of an unknown client"); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cSocketThreads::cSocketThread: + +cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) : + cIsThread("cSocketThread"), + m_Parent(a_Parent), + m_NumSlots(0) +{ + // Nothing needed yet +} + + + + + +cSocketThreads::cSocketThread::~cSocketThread() +{ + m_ShouldTerminate = true; + + // Notify the thread: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("a", 1); + + // Wait for the thread to finish: + Wait(); + + // Close the control sockets: + m_ControlSocket1.CloseSocket(); + m_ControlSocket2.CloseSocket(); +} + + + + + +void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallback * a_Client) +{ + ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding + + m_Slots[m_NumSlots].m_Client = a_Client; + m_Slots[m_NumSlots].m_Socket = a_Socket; + m_Slots[m_NumSlots].m_Outgoing.clear(); + m_Slots[m_NumSlots].m_ShouldClose = false; + m_Slots[m_NumSlots].m_ShouldCallClient = true; + m_NumSlots++; + + // Notify the thread of the change: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("a", 1); +} + + + + + +bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client) +{ + // Returns true if removed, false if not found + + if (m_NumSlots == 0) + { + return false; + } + + for (int i = m_NumSlots - 1; i >= 0 ; --i) + { + if (m_Slots[i].m_Client != a_Client) + { + continue; + } + + // Found, remove it: + m_Slots[i] = m_Slots[--m_NumSlots]; + + // Notify the thread of the change: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("r", 1); + return true; + } // for i - m_Slots[] + + // Not found + return false; +} + + + + + +bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket) +{ + // Returns true if removed, false if not found + + for (int i = m_NumSlots - 1; i >= 0 ; --i) + { + if (m_Slots[i].m_Socket != *a_Socket) + { + continue; + } + + // Found, remove it: + m_Slots[i] = m_Slots[--m_NumSlots]; + + // Notify the thread of the change: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("r", 1); + return true; + } // for i - m_Slots[] + + // Not found + return false; +} + + + + + +bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const +{ + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const +{ + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Socket == *a_Socket) + { + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client) +{ + if (HasClient(a_Client)) + { + // Notify the thread that there's another packet in the queue: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("q", 1); + return true; + } + return false; +} + + + + + +bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AString & a_Data) +{ + // Returns true if socket handled by this thread + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + m_Slots[i].m_Outgoing.append(a_Data); + + // Notify the thread that there's data in the queue: + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("q", 1); + + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::StopReading (const cCallback * a_Client) +{ + // Returns true if client handled by this thread + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + m_Slots[i].m_ShouldCallClient = false; + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::QueueClose(const cCallback * a_Client) +{ + // Returns true if socket handled by this thread + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (m_Slots[i].m_Client == a_Client) + { + m_Slots[i].m_ShouldClose = true; + + // Notify the thread that there's a close queued (in case its conditions are already met): + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("c", 1); + + return true; + } + } // for i - m_Slots[] + return false; +} + + + + + +bool cSocketThreads::cSocketThread::Start(void) +{ + // Create the control socket listener + m_ControlSocket2 = cSocket::CreateSocket(cSocket::IPv4); + if (!m_ControlSocket2.IsValid()) + { + LOGERROR("Cannot create a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + return false; + } + if (!m_ControlSocket2.BindToLocalhostIPv4(cSocket::ANY_PORT)) + { + LOGERROR("Cannot bind a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + if (!m_ControlSocket2.Listen(1)) + { + LOGERROR("Cannot listen on a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + if (m_ControlSocket2.GetPort() == 0) + { + LOGERROR("Cannot determine Control socket port (\"%s\"); conitnuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + + // Start the thread + if (!super::Start()) + { + LOGERROR("Cannot start new cSocketThread"); + m_ControlSocket2.CloseSocket(); + return false; + } + + // Finish connecting the control socket by accepting connection from the thread's socket + cSocket tmp = m_ControlSocket2.AcceptIPv4(); + if (!tmp.IsValid()) + { + LOGERROR("Cannot link Control sockets for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return false; + } + m_ControlSocket2.CloseSocket(); + m_ControlSocket2 = tmp; + + return true; +} + + + + + +void cSocketThreads::cSocketThread::Execute(void) +{ + // Connect the "client" part of the Control socket: + m_ControlSocket1 = cSocket::CreateSocket(cSocket::IPv4); + ASSERT(m_ControlSocket2.GetPort() != 0); // We checked in the Start() method, but let's be sure + if (!m_ControlSocket1.ConnectToLocalhostIPv4(m_ControlSocket2.GetPort())) + { + LOGERROR("Cannot connect Control sockets for a cSocketThread (\"%s\"); continuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str()); + m_ControlSocket2.CloseSocket(); + return; + } + + // The main thread loop: + while (!m_ShouldTerminate) + { + // Put all sockets into the Read set: + fd_set fdRead; + cSocket::xSocket Highest = m_ControlSocket1.GetSocket(); + + PrepareSet(&fdRead, Highest); + + // Wait for the sockets: + if (select(Highest + 1, &fdRead, NULL, NULL, NULL) == -1) + { + LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + + ReadFromSockets(&fdRead); + + // Test sockets for writing: + fd_set fdWrite; + Highest = m_ControlSocket1.GetSocket(); + PrepareSet(&fdWrite, Highest); + timeval Timeout; + Timeout.tv_sec = 0; + Timeout.tv_usec = 0; + if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1) + { + LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + + WriteToSockets(&fdWrite); + } // while (!mShouldTerminate) +} + + + + + +void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest) +{ + FD_ZERO(a_Set); + FD_SET(m_ControlSocket1.GetSocket(), a_Set); + + cCSLock Lock(m_Parent->m_CS); + for (int i = m_NumSlots - 1; i >= 0; --i) + { + if (!m_Slots[i].m_Socket.IsValid()) + { + continue; + } + cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket(); + FD_SET(s, a_Set); + if (s > a_Highest) + { + a_Highest = s; + } + } // for i - m_Slots[] +} + + + + + +void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read) +{ + // Read on available sockets: + + // Reset Control socket state: + if (FD_ISSET(m_ControlSocket1.GetSocket(), a_Read)) + { + char Dummy[128]; + m_ControlSocket1.Receive(Dummy, sizeof(Dummy), 0); + } + + // Read from clients: + cCSLock Lock(m_Parent->m_CS); + for (int i = m_NumSlots - 1; i >= 0; --i) + { + cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); + if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Read)) + { + continue; + } + char Buffer[1024]; + int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0); + if (Received == 0) + { + // The socket has been closed by the remote party, close our socket and let it be removed after we process all reading + m_Slots[i].m_Socket.CloseSocket(); + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->SocketClosed(); + } + } + else if (Received > 0) + { + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->DataReceived(Buffer, Received); + } + } + else + { + // The socket has encountered an error, close it and let it be removed after we process all reading + m_Slots[i].m_Socket.CloseSocket(); + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->SocketClosed(); + } + } + } // for i - m_Slots[] +} + + + + + +void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write) +{ + // Write to available client sockets: + cCSLock Lock(m_Parent->m_CS); + for (int i = m_NumSlots - 1; i >= 0; --i) + { + cSocket::xSocket Socket = m_Slots[i].m_Socket.GetSocket(); + if (!cSocket::IsValidSocket(Socket) || !FD_ISSET(Socket, a_Write)) + { + continue; + } + if (m_Slots[i].m_Outgoing.empty()) + { + // Request another chunk of outgoing data: + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing); + } + if (m_Slots[i].m_Outgoing.empty()) + { + // Nothing ready + if (m_Slots[i].m_ShouldClose) + { + // Socket was queued for closing and there's no more data to send, close it now: + + // DEBUG + LOGD("Socket was queued for closing, closing now. Slot %d, client %p, socket %d", i, m_Slots[i].m_Client, m_Slots[i].m_Socket.GetSocket()); + + m_Slots[i].m_Socket.CloseSocket(); + // The slot must be freed actively by the client, using RemoveClient() + } + continue; + } + } // if (outgoing data is empty) + + int Sent = m_Slots[i].m_Socket.Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size()); + if (Sent < 0) + { + int Err = cSocket::GetLastError(); + LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), cSocket::GetErrorString(Err).c_str()); + m_Slots[i].m_Socket.CloseSocket(); + if (m_Slots[i].m_ShouldCallClient) + { + m_Slots[i].m_Client->SocketClosed(); + } + return; + } + m_Slots[i].m_Outgoing.erase(0, Sent); + + // _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled + // This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread) + /* + // If there's any data left, signalize the Control socket: + if (!m_Slots[i].m_Outgoing.empty()) + { + ASSERT(m_ControlSocket2.IsValid()); + m_ControlSocket2.Send("q", 1); + } + */ + } // for i - m_Slots[i] +} + + + + diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h new file mode 100644 index 000000000..ecbac3aeb --- /dev/null +++ b/src/OSSupport/SocketThreads.h @@ -0,0 +1,169 @@ + +// SocketThreads.h + +// Interfaces to the cSocketThreads class representing the heart of MCS's client networking. +// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support +// For more detail, see http://forum.mc-server.org/showthread.php?tid=327 + +/* +Additional details: +When a client is terminating a connection: +- they call the StopReading() method to disable callbacks for the incoming data +- they call the Write() method to queue any outstanding outgoing data +- they call the QueueClose() method to queue the socket to close after outgoing data has been sent. +When a socket slot is marked as having no callback, it is kept alive until its outgoing data queue is empty and its m_ShouldClose flag is set. +This means that the socket can be written to several times before finally closing it via QueueClose() +*/ + + + + + +/// How many clients should one thread handle? (must be less than FD_SETSIZE for your platform) +#define MAX_SLOTS 63 + + + + + +#pragma once +#ifndef CSOCKETTHREADS_H_INCLUDED +#define CSOCKETTHREADS_H_INCLUDED + +#include "Socket.h" +#include "IsThread.h" + + + + +// Check MAX_SLOTS: +#if MAX_SLOTS >= FD_SETSIZE + #error "MAX_SLOTS must be less than FD_SETSIZE for your platform! (otherwise select() won't work)" +#endif + + + + + +// fwd: +class cSocket; +class cClientHandle; + + + + + +class cSocketThreads +{ +public: + + // Clients of cSocketThreads must implement this interface to be able to communicate + class cCallback + { + public: + /// Called when data is received from the remote party + virtual void DataReceived(const char * a_Data, int a_Size) = 0; + + /// Called when data can be sent to remote party; the function is supposed to append outgoing data to a_Data + virtual void GetOutgoingData(AString & a_Data) = 0; + + /// Called when the socket has been closed for any reason + virtual void SocketClosed(void) = 0; + } ; + + + cSocketThreads(void); + ~cSocketThreads(); + + /// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful + bool AddClient(const cSocket & a_Socket, cCallback * a_Client); + + /// Remove the associated socket and the client from processing. The socket is left to send its data and is removed only after all its m_OutgoingData is sent + void RemoveClient(const cCallback * a_Client); + + /// Notify the thread responsible for a_Client that the client has something to write + void NotifyWrite(const cCallback * a_Client); + + /// Puts a_Data into outgoing data queue for a_Client + void Write(const cCallback * a_Client, const AString & a_Data); + + /// Stops reading from the client - when this call returns, no more calls to the callbacks are made + void StopReading(const cCallback * a_Client); + + /// Queues the client for closing, as soon as its outgoing data is sent + void QueueClose(const cCallback * a_Client); + +private: + + class cSocketThread : + public cIsThread + { + typedef cIsThread super; + + public: + + cSocketThread(cSocketThreads * a_Parent); + ~cSocketThread(); + + // All these methods assume parent's m_CS is locked + bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } + bool IsEmpty (void) const {return m_NumSlots == 0; } + + void AddClient (const cSocket & a_Socket, cCallback * a_Client); // Takes ownership of the socket + bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found + bool RemoveSocket(const cSocket * a_Socket); // Returns true if removed, false if not found + bool HasClient (const cCallback * a_Client) const; + bool HasSocket (const cSocket * a_Socket) const; + bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread + bool Write (const cCallback * a_Client, const AString & a_Data); // Returns true if client handled by this thread + bool StopReading (const cCallback * a_Client); // Returns true if client handled by this thread + bool QueueClose (const cCallback * a_Client); // Returns true if client handled by this thread + + bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket + + bool IsValid(void) const {return m_ControlSocket2.IsValid(); } // If the Control socket dies, the thread is not valid anymore + + private: + + cSocketThreads * m_Parent; + + // Two ends of the control socket, the first is select()-ed, the second is written to for notifications + cSocket m_ControlSocket1; + cSocket m_ControlSocket2; + + // Socket-client-packetqueues triplets. + // Manipulation with these assumes that the parent's m_CS is locked + struct sSlot + { + cSocket m_Socket; // The socket is primarily owned by this + cCallback * m_Client; + AString m_Outgoing; // If sending writes only partial data, the rest is stored here for another send + bool m_ShouldClose; // If true, the socket is to be closed after sending all outgoing data + bool m_ShouldCallClient; // If true, the client callbacks are called. Set to false in StopReading() + } ; + sSlot m_Slots[MAX_SLOTS]; + int m_NumSlots; // Number of slots actually used + + virtual void Execute(void) override; + + void PrepareSet (fd_set * a_Set, cSocket::xSocket & a_Highest); // Puts all sockets into the set, along with m_ControlSocket1 + void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read + void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write + } ; + + typedef std::list<cSocketThread *> cSocketThreadList; + + + cCriticalSection m_CS; + cSocketThreadList m_Threads; +} ; + + + + + +#endif // CSOCKETTHREADS_H_INCLUDED + + + + diff --git a/src/OSSupport/Thread.cpp b/src/OSSupport/Thread.cpp new file mode 100644 index 000000000..3df75f0e7 --- /dev/null +++ b/src/OSSupport/Thread.cpp @@ -0,0 +1,128 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + + + + + +// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: +#ifdef _MSC_VER +// +// Usage: SetThreadName (-1, "MainThread"); +// +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +} THREADNAME_INFO; + +void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +} +#endif // _MSC_VER + + + + + +cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */ ) + : m_ThreadFunction( a_ThreadFunction ) + , m_Param( a_Param ) + , m_Event( new cEvent() ) + , m_StopEvent( 0 ) +{ + if( a_ThreadName ) + { + m_ThreadName.assign(a_ThreadName); + } +} + + + + + +cThread::~cThread() +{ + delete m_Event; + + if( m_StopEvent ) + { + m_StopEvent->Wait(); + delete m_StopEvent; + } +} + + + + + +void cThread::Start( bool a_bWaitOnDelete /* = true */ ) +{ + if( a_bWaitOnDelete ) + m_StopEvent = new cEvent(); + +#ifndef _WIN32 + pthread_t SndThread; + if( pthread_create( &SndThread, NULL, MyThread, this) ) + LOGERROR("ERROR: Could not create thread!"); +#else + DWORD ThreadID = 0; + HANDLE hThread = CreateThread( 0 // security + ,0 // stack size + , (LPTHREAD_START_ROUTINE) MyThread // function name + ,this // parameters + ,0 // flags + ,&ThreadID ); // thread id + CloseHandle( hThread ); + + #ifdef _MSC_VER + if (!m_ThreadName.empty()) + { + SetThreadName(ThreadID, m_ThreadName.c_str()); + } + #endif // _MSC_VER +#endif + + // Wait until thread has actually been created + m_Event->Wait(); +} + + + + + +#ifdef _WIN32 +unsigned long cThread::MyThread(void* a_Param ) +#else +void *cThread::MyThread( void *a_Param ) +#endif +{ + cThread* self = (cThread*)a_Param; + cEvent* StopEvent = self->m_StopEvent; + + ThreadFunc* ThreadFunction = self->m_ThreadFunction; + void* ThreadParam = self->m_Param; + + // Set event to let other thread know this thread has been created and it's safe to delete the cThread object + self->m_Event->Set(); + + ThreadFunction( ThreadParam ); + + if( StopEvent ) StopEvent->Set(); + return 0; +} diff --git a/src/OSSupport/Thread.h b/src/OSSupport/Thread.h new file mode 100644 index 000000000..3c9316424 --- /dev/null +++ b/src/OSSupport/Thread.h @@ -0,0 +1,26 @@ +#pragma once + +class cThread +{ +public: + typedef void (ThreadFunc)(void*); + cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0 ); + ~cThread(); + + void Start( bool a_bWaitOnDelete = true ); + void WaitForThread(); +private: + ThreadFunc* m_ThreadFunction; + +#ifdef _WIN32 + static unsigned long MyThread(void* a_Param ); +#else + static void *MyThread( void *lpParam ); +#endif + + void* m_Param; + cEvent* m_Event; + cEvent* m_StopEvent; + + AString m_ThreadName; +};
\ No newline at end of file diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp new file mode 100644 index 000000000..ed16f9e3a --- /dev/null +++ b/src/OSSupport/Timer.cpp @@ -0,0 +1,37 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Timer.h" + + + + + + +cTimer::cTimer(void) +{ + #ifdef _WIN32 + QueryPerformanceFrequency(&m_TicksPerSecond); + #endif +} + + + + + +long long cTimer::GetNowTime(void) +{ + #ifdef _WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart); + #else + struct timeval now; + gettimeofday(&now, NULL); + return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000); + #endif +} + + + + diff --git a/src/OSSupport/Timer.h b/src/OSSupport/Timer.h new file mode 100644 index 000000000..a059daa41 --- /dev/null +++ b/src/OSSupport/Timer.h @@ -0,0 +1,32 @@ + +// Timer.h + +// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy + + + + + +#pragma once + + + + + +class cTimer +{ +public: + cTimer(void); + + // Returns the current time expressed in milliseconds + long long GetNowTime(void); +private: + + #ifdef _WIN32 + LARGE_INTEGER m_TicksPerSecond; + #endif +} ; + + + + diff --git a/src/Piston.cpp b/src/Piston.cpp new file mode 100644 index 000000000..048cc355d --- /dev/null +++ b/src/Piston.cpp @@ -0,0 +1,291 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Piston.h" +#include "ChunkDef.h" +#include "Entities/Pickup.h" +#include "Item.h" +#include "Root.h" +#include "ClientHandle.h" +#include "World.h" +#include "Server.h" +#include "Blocks/BlockHandler.h" + + + + + +/// Number of ticks that the piston extending / retracting waits before setting the block +const int PISTON_TICK_DELAY = 6; + + + + + +cPiston::cPiston(cWorld * a_World) + : m_World(a_World) +{ + +} + + + + + +int cPiston::FirstPassthroughBlock(int pistonX, int pistonY, int pistonZ, NIBBLETYPE pistonmeta) +{ + // Examine each of the 12 blocks ahead of the piston: + for (int ret = 0; ret < 12; ret++) + { + BLOCKTYPE currBlock; + NIBBLETYPE currMeta; + AddDir(pistonX, pistonY, pistonZ, pistonmeta, 1); + m_World->GetBlockTypeMeta(pistonX, pistonY, pistonZ, currBlock, currMeta); + if (CanBreakPush(currBlock, currMeta)) + { + // This block breaks when pushed, extend up to here + return ret; + } + if (!CanPush(currBlock, currMeta)) + { + // This block cannot be pushed at all, the piston can't extend + return -1; + } + } + // There is no space for the blocks to move, piston can't extend + return -1; +} + + + + + +void cPiston::ExtendPiston(int pistx, int pisty, int pistz) +{ + BLOCKTYPE pistonBlock; + NIBBLETYPE pistonMeta; + m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); + + if (IsExtended(pistonMeta)) + { + // Already extended, bail out + return; + } + + int dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); + if (dist < 0) + { + // FirstPassthroughBlock says piston can't push anything, bail out + return; + } + + m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, pistonBlock); + m_World->BroadcastSoundEffect("tile.piston.out", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); + + // Drop the breakable block in the line, if appropriate: + AddDir(pistx, pisty, pistz, pistonMeta, dist + 1); // "pist" now at the breakable / empty block + BLOCKTYPE currBlock; + NIBBLETYPE currMeta; + m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currMeta); + if (currBlock != E_BLOCK_AIR) + { + cBlockHandler * Handler = BlockHandler(currBlock); + if (Handler->DoesDropOnUnsuitable()) + { + Handler->DropBlock(m_World, NULL, pistx, pisty, pistz); + } + } + + // Push blocks, from the furthest to the nearest: + int oldx = pistx, oldy = pisty, oldz = pistz; + NIBBLETYPE currBlockMeta; + for (int i = dist + 1; i > 1; i--) + { + AddDir(pistx, pisty, pistz, pistonMeta, -1); + m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currBlockMeta); + m_World->QueueSetBlock( oldx, oldy, oldz, currBlock, currBlockMeta, PISTON_TICK_DELAY); + oldx = pistx; + oldy = pisty; + oldz = pistz; + } + + int extx = pistx; + int exty = pisty; + int extz = pistz; + AddDir(pistx, pisty, pistz, pistonMeta, -1); + // "pist" now at piston body, "ext" at future extension + + m_World->SetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8); + m_World->QueueSetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), PISTON_TICK_DELAY); +} + + + + + +void cPiston::RetractPiston(int pistx, int pisty, int pistz) +{ + BLOCKTYPE pistonBlock; + NIBBLETYPE pistonMeta; + m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); + if (!IsExtended(pistonMeta)) + { + // Already retracted, bail out + return; + } + + m_World->BroadcastBlockAction(pistx, pisty, pistz, 1, pistonMeta & ~(8), pistonBlock); + m_World->BroadcastSoundEffect("tile.piston.in", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); + m_World->SetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8)); + + // Check the extension: + AddDir(pistx, pisty, pistz, pistonMeta, 1); + if (m_World->GetBlock(pistx, pisty, pistz) != E_BLOCK_PISTON_EXTENSION) + { + LOGD("%s: Piston without an extension?", __FUNCTION__); + return; + } + + // Retract the extension, pull block if appropriate + if (IsSticky(pistonBlock)) + { + int tempx = pistx, tempy = pisty, tempz = pistz; + AddDir( tempx, tempy, tempz, pistonMeta, 1); + BLOCKTYPE tempBlock; + NIBBLETYPE tempMeta; + m_World->GetBlockTypeMeta(tempx, tempy, tempz, tempBlock, tempMeta); + if (CanPull(tempBlock, tempMeta)) + { + // Pull the block + m_World->QueueSetBlock(pistx, pisty, pistz, tempBlock, tempMeta, PISTON_TICK_DELAY); + m_World->QueueSetBlock(tempx, tempy, tempz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); + } + else + { + // Retract without pulling + m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); + } + } + else + { + m_World->QueueSetBlock(pistx, pisty, pistz, E_BLOCK_AIR, 0, PISTON_TICK_DELAY); + } +} + + + + + +bool cPiston::IsExtended(NIBBLETYPE a_PistonMeta) +{ + return ((a_PistonMeta & 0x8) != 0x0); +} + + + + + +bool cPiston::IsSticky(BLOCKTYPE a_BlockType) +{ + return (a_BlockType == E_BLOCK_STICKY_PISTON); +} + + + + + +bool cPiston::CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + switch (a_BlockType) + { + case E_BLOCK_ANVIL: + case E_BLOCK_BED: + case E_BLOCK_BEDROCK: + case E_BLOCK_BREWING_STAND: + case E_BLOCK_CHEST: + case E_BLOCK_COMMAND_BLOCK: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_ENCHANTMENT_TABLE: + case E_BLOCK_END_PORTAL: + case E_BLOCK_END_PORTAL_FRAME: + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + case E_BLOCK_HOPPER: + case E_BLOCK_JUKEBOX: + case E_BLOCK_MOB_SPAWNER: + case E_BLOCK_NETHER_PORTAL: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_OBSIDIAN: + case E_BLOCK_PISTON_EXTENSION: + { + return false; + } + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_PISTON: + { + // A piston can only be pushed if retracted: + return !IsExtended(a_BlockMeta); + } + } + return true; +} + + + + + +bool cPiston::CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + return g_BlockPistonBreakable[a_BlockType]; +} + + + + + +bool cPiston::CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + switch (a_BlockType) + { + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + return false; + } + } + + if (CanBreakPush(a_BlockType, a_BlockMeta)) + { + return false; // CanBreakPush returns true, but we need false to prevent pulling + } + + return CanPush(a_BlockType, a_BlockMeta); +} + + + + + +void cPiston::AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount) +{ + switch (a_PistonMeta & 0x07) + { + case 0: a_BlockY -= a_Amount; break; + case 1: a_BlockY += a_Amount; break; + case 2: a_BlockZ -= a_Amount; break; + case 3: a_BlockZ += a_Amount; break; + case 4: a_BlockX -= a_Amount; break; + case 5: a_BlockX += a_Amount; break; + default: + { + LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, a_PistonMeta & 0x07); + break; + } + } +} + + + + diff --git a/src/Piston.h b/src/Piston.h new file mode 100644 index 000000000..92ddf6938 --- /dev/null +++ b/src/Piston.h @@ -0,0 +1,91 @@ + +#pragma once + + + + + +// fwd: World.h +class cWorld; + + + + + +class cPiston +{ +public: + + cPiston(cWorld * a_World); + + static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch) + { + if (a_Pitch >= 50) + { + return 0x1; + } + else if (a_Pitch <= -50) + { + return 0x0; + } + else + { + a_Rotation += 90 + 45; // So its not aligned with axis + + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + if ((a_Rotation >= 0) && (a_Rotation < 90)) + { + return 0x4; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x5; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + } + + void ExtendPiston( int, int, int ); + void RetractPiston( int, int, int ); + + /// Returns true if the piston (specified by blocktype) is a sticky piston + static bool IsSticky(BLOCKTYPE a_BlockType); + + /// Returns true if the piston (with the specified meta) is extended + static bool IsExtended(NIBBLETYPE a_PistonMeta); + + /// Returns true if the specified block can be pushed by a piston (and left intact) + static bool CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Returns true if the specified block can be pushed by a piston and broken / replaced + static bool CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Returns true if the specified block can be pulled by a sticky piston + static bool CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Updates the coords by the specified amount in the direction a piston of the specified meta is facing + static void AddDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_PistonMeta, int a_Amount); + + + cWorld * m_World; + +private: + void ChainMove( int, int, int, char, unsigned short * ); + + /// Returns how many blocks the piston has to push (where the first free space is); <0 when unpushable + int FirstPassthroughBlock(int a_PistonX, int a_PistonY, int a_PistonZ, NIBBLETYPE a_PistonMeta); +} ; + + + + diff --git a/src/Plugin.cpp b/src/Plugin.cpp new file mode 100644 index 000000000..98ccfb88c --- /dev/null +++ b/src/Plugin.cpp @@ -0,0 +1,38 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Plugin.h" + + + + + +cPlugin::cPlugin(const AString & a_PluginDirectory) : + m_Language(E_CPP), + m_Name(a_PluginDirectory), + m_Version(0), + m_Directory(a_PluginDirectory) +{ +} + + + + + +cPlugin::~cPlugin() +{ + LOGD("Destroying plugin \"%s\".", m_Name.c_str()); +} + + + + + +AString cPlugin::GetLocalFolder(void) const +{ + return std::string("Plugins/") + m_Directory; +} + + + + diff --git a/src/Plugin.h b/src/Plugin.h new file mode 100644 index 000000000..06e5819df --- /dev/null +++ b/src/Plugin.h @@ -0,0 +1,149 @@ + +#pragma once + +#include "Item.h" +#include "PluginManager.h" + + + + + +class cClientHandle; +class cPlayer; +class cPickup; +class cItem; +class cEntity; +class cWorld; +class cChunkDesc; +struct TakeDamageInfo; + +// fwd: cPlayer.h +class cPlayer; + +// fwd: CraftingRecipes.h +class cCraftingGrid; +class cCraftingRecipe; + + + + + +// tolua_begin +class cPlugin +{ +public: + // tolua_end + + cPlugin( const AString & a_PluginDirectory ); + virtual ~cPlugin(); + + virtual void OnDisable(void) {} + virtual bool Initialize(void) = 0; + + // Called each tick + virtual void Tick(float a_Dt) = 0; + + /** + * On all these functions, return true if you want to override default behavior and not call other plugins on that callback. + * You can also return false, so default behavior is used. + **/ + virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) = 0; + virtual bool OnChat (cPlayer * a_Player, AString & a_Message) = 0; + virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; + virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) = 0; + virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; + virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; + virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0; + virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0; + virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; + virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; + virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; + virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) = 0; + virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) = 0; + virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) = 0; + virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) = 0; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) = 0; + virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) = 0; + virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerEating (cPlayer & a_Player) = 0; + virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; + virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; + virtual bool OnPlayerMoved (cPlayer & a_Player) = 0; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0; + virtual bool OnPlayerShooting (cPlayer & a_Player) = 0; + virtual bool OnPlayerSpawned (cPlayer & a_Player) = 0; + virtual bool OnPlayerTossingItem (cPlayer & a_Player) = 0; + virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0; + virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0; + virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) = 0; + virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) = 0; + virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) = 0; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) = 0; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) = 0; + virtual bool OnWeatherChanged (cWorld & a_World) = 0; + virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0; + virtual bool OnWorldTick (cWorld & a_World, float a_Dt) = 0; + + /** Handles the command split into a_Split, issued by player a_Player. + Command permissions have already been checked. + Returns true if command handled successfully + */ + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) = 0; + + /** Handles the console command split into a_Split. + Returns true if command handled successfully. Output is to be sent to the a_Output callback. + */ + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0; + + /// All bound commands are to be removed, do any language-dependent cleanup here + virtual void ClearCommands(void) {} ; + + /// All bound console commands are to be removed, do any language-dependent cleanup here + virtual void ClearConsoleCommands(void) {} ; + + // tolua_begin + const AString & GetName(void) const { return m_Name; } + void SetName(const AString & a_Name) { m_Name = a_Name; } + + int GetVersion(void) const { return m_Version; } + void SetVersion(int a_Version) { m_Version = a_Version; } + + const AString & GetDirectory(void) const {return m_Directory; } + AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead + AString GetLocalFolder(void) const; + // tolua_end + + + /* This should not be exposed to scripting languages */ + enum PluginLanguage + { + E_CPP, + E_LUA, + E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history + }; + PluginLanguage GetLanguage() { return m_Language; } + void SetLanguage( PluginLanguage a_Language ) { m_Language = a_Language; } + +private: + PluginLanguage m_Language; + AString m_Name; + int m_Version; + + AString m_Directory; +}; // tolua_export + + + + diff --git a/src/PluginLua.cpp b/src/PluginLua.cpp new file mode 100644 index 000000000..0afd3844a --- /dev/null +++ b/src/PluginLua.cpp @@ -0,0 +1,1471 @@ + +// PluginLua.cpp + +// Implements the cPluginLua class representing a plugin written in Lua + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#define LUA_USE_POSIX +#include "PluginLua.h" +#include "CommandOutput.h" + +extern "C" +{ + #include"lib/lua/src/lualib.h" +} + +#include "tolua++.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cPluginLua: + +cPluginLua::cPluginLua(const AString & a_PluginDirectory) : + cPlugin(a_PluginDirectory), + m_LuaState(Printf("plugin %s", a_PluginDirectory.c_str())) +{ +} + + + + + +cPluginLua::~cPluginLua() +{ + cCSLock Lock(m_CriticalSection); + Close(); +} + + + + + +void cPluginLua::Close(void) +{ + if (m_LuaState.IsValid()) + { + // Release all the references in the hook map: + for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) + { + for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) + { + delete *itrR; + } // for itrR - itrH->second[] + } // for itrH - m_HookMap[] + m_HookMap.clear(); + + m_LuaState.Close(); + } + else + { + ASSERT(m_HookMap.empty()); + } +} + + + + + +bool cPluginLua::Initialize(void) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + m_LuaState.Create(); + + // Inject the identification global variables into the state: + lua_pushlightuserdata(m_LuaState, this); + 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 + GetLocalFolder() + "/"; + + // Load all files for this plugin, and execute them + AStringVector Files = cFile::GetFolderContents(PluginPath.c_str()); + for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + { + if (itr->rfind(".lua") == AString::npos) + { + continue; + } + AString Path = PluginPath + *itr; + if (!m_LuaState.LoadFile(Path)) + { + Close(); + return false; + } + } // for itr - Files[] + + // Call intialize function + bool res = false; + if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res)) + { + LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str()); + Close(); + return false; + } + + if (!res) + { + LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str()); + Close(); + return false; + } + + return true; +} + + + + + +void cPluginLua::OnDisable(void) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.HasFunction("OnDisable")) + { + return; + } + m_LuaState.Call("OnDisable"); +} + + + + + +void cPluginLua::Tick(float a_Dt) +{ + cCSLock Lock(m_CriticalSection); + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Dt); + } +} + + + + + +bool cPluginLua::OnBlockToPickups(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChat(cPlayer * a_Player, AString & a_Message) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Message, cLuaState::Return, res, a_Message); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnCollectingPickup(cPlayer * a_Player, cPickup * a_Pickup) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Pickup, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), (cPlayer *)a_Player, a_Grid, a_Recipe, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Split, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + switch (a_Source) + { + case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res); break; + case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res); break; + case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; + case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res); break; + case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esWitherSkullBlack: + case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res); break; + default: + { + ASSERT(!"Unhandled ExplosionSource"); + return false; + } + } + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + switch (a_Source) + { + case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherSkullBlack: + case esWitherSkullBlue: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherBirth: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPlugin: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + default: + { + ASSERT(!"Unhandled ExplosionSource"); + return false; + } + } + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnHandshake(cClientHandle * a_Client, const AString & a_Username) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Client, a_Username, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Victim, a_Killer, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_Animation, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerEating(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerMoved(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, &a_Entity, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_Player, a_Grid, a_Recipe, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Entity, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, &a_Monster, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnUpdatedSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnUpdatingSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnWeatherChanged(cWorld & a_World) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + +bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + int NewWeather = a_NewWeather; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, NewWeather, cLuaState::Return, res, NewWeather); + if (res) + { + a_NewWeather = (eWeather)NewWeather; + return true; + } + } + a_NewWeather = (eWeather)NewWeather; + return false; +} + + + + + +bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt) +{ + cCSLock Lock(m_CriticalSection); + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_World, a_Dt); + } + return false; +} + + + + + +bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) +{ + ASSERT(!a_Split.empty()); + CommandMap::iterator cmd = m_Commands.find(a_Split[0]); + if (cmd == m_Commands.end()) + { + LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); + return false; + } + + cCSLock Lock(m_CriticalSection); + bool res = false; + m_LuaState.Call(cmd->second, a_Split, a_Player, cLuaState::Return, res); + return res; +} + + + + + +bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +{ + ASSERT(!a_Split.empty()); + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); + if (cmd == m_ConsoleCommands.end()) + { + LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", + a_Split[0].c_str(), GetName().c_str() + ); + return false; + } + + cCSLock Lock(m_CriticalSection); + bool res = false; + AString str; + m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str); + if (res && !str.empty()) + { + a_Output.Out(str); + } + return res; +} + + + + + +void cPluginLua::ClearCommands(void) +{ + cCSLock Lock(m_CriticalSection); + + // Unreference the bound functions so that Lua can GC them + if (m_LuaState != NULL) + { + for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); + } + } + m_Commands.clear(); +} + + + + + +void cPluginLua::ClearConsoleCommands(void) +{ + cCSLock Lock(m_CriticalSection); + + // Unreference the bound functions so that Lua can GC them + if (m_LuaState != NULL) + { + for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); + } + } + m_ConsoleCommands.clear(); +} + + + + + +bool cPluginLua::CanAddOldStyleHook(int a_HookType) +{ + const char * FnName = GetHookFnName(a_HookType); + if (FnName == NULL) + { + // Unknown hook ID + LOGWARNING("Plugin %s wants to add an unknown hook ID (%d). The plugin need not work properly.", + GetName().c_str(), a_HookType + ); + m_LuaState.LogStackTrace(); + return false; + } + + // Check if the function is available + if (m_LuaState.HasFunction(FnName)) + { + return true; + } + + LOGWARNING("Plugin %s wants to add a hook (%d), but it doesn't provide the callback function \"%s\" for it. The plugin need not work properly.", + GetName().c_str(), a_HookType, FnName + ); + m_LuaState.LogStackTrace(); + return false; +} + + + + + +const char * cPluginLua::GetHookFnName(int a_HookType) +{ + switch (a_HookType) + { + case cPluginManager::HOOK_BLOCK_TO_PICKUPS: return "OnBlockToPickups"; + case cPluginManager::HOOK_CHAT: return "OnChat"; + case cPluginManager::HOOK_CHUNK_AVAILABLE: return "OnChunkAvailable"; + case cPluginManager::HOOK_CHUNK_GENERATED: return "OnChunkGenerated"; + case cPluginManager::HOOK_CHUNK_GENERATING: return "OnChunkGenerating"; + case cPluginManager::HOOK_CHUNK_UNLOADED: return "OnChunkUnloaded"; + case cPluginManager::HOOK_CHUNK_UNLOADING: return "OnChunkUnloading"; + case cPluginManager::HOOK_COLLECTING_PICKUP: return "OnCollectingPickup"; + case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe"; + case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; + case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; + case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; + case cPluginManager::HOOK_KILLING: return "OnKilling"; + case cPluginManager::HOOK_LOGIN: return "OnLogin"; + case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; + case cPluginManager::HOOK_PLAYER_BREAKING_BLOCK: return "OnPlayerBreakingBlock"; + case cPluginManager::HOOK_PLAYER_BROKEN_BLOCK: return "OnPlayerBrokenBlock"; + case cPluginManager::HOOK_PLAYER_EATING: return "OnPlayerEating"; + case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined"; + case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick"; + case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving"; + case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock"; + case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock"; + case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick"; + case cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY: return "OnPlayerRightClickingEntity"; + case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting"; + case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned"; + case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem"; + case cPluginManager::HOOK_PLAYER_USED_BLOCK: return "OnPlayerUsedBlock"; + case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem"; + case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock"; + case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem"; + case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting"; + case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting"; + case cPluginManager::HOOK_SPAWNED_ENTITY: return "OnSpawnedEntity"; + case cPluginManager::HOOK_SPAWNED_MONSTER: return "OnSpawnedMonster"; + case cPluginManager::HOOK_SPAWNING_ENTITY: return "OnSpawningEntity"; + case cPluginManager::HOOK_SPAWNING_MONSTER: return "OnSpawningMonster"; + case cPluginManager::HOOK_TAKE_DAMAGE: return "OnTakeDamage"; + case cPluginManager::HOOK_TICK: return "OnTick"; + case cPluginManager::HOOK_UPDATED_SIGN: return "OnUpdatedSign"; + case cPluginManager::HOOK_UPDATING_SIGN: return "OnUpdatingSign"; + case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged"; + case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging"; + case cPluginManager::HOOK_WORLD_TICK: return "OnWorldTick"; + default: return NULL; + } // switch (a_Hook) +} + + + + + +bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) +{ + ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? + + // Check if the function reference is valid: + cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx); + if ((Ref == NULL) || !Ref->IsValid()) + { + LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType); + m_LuaState.LogStackTrace(); + delete Ref; + return false; + } + + m_HookMap[a_HookType].push_back(Ref); + return true; +} + + + + + +AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request ) +{ + cCSLock Lock(m_CriticalSection); + std::string RetVal = ""; + + std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request); + std::string SafeTabName = TabName.second; + if (SafeTabName.empty()) + { + return ""; + } + + sWebPluginTab * Tab = 0; + for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr) + { + if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr + { + Tab = *itr; + break; + } + } + + if (Tab != NULL) + { + AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str()); + if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents)) + { + return "Lua encountered error while processing the page request"; + } + + RetVal += Contents; + } + + return RetVal; +} + + + + + +bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference) +{ + cCSLock Lock(m_CriticalSection); + if (a_LuaState != m_LuaState) + { + LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); + return false; + } + sWebPluginTab * Tab = new sWebPluginTab(); + Tab->Title = a_Title; + Tab->SafeTitle = SafeString(a_Title); + + Tab->UserData = a_FunctionReference; + + GetTabs().push_back(Tab); + return true; +} + + + + + +void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef) +{ + ASSERT(m_Commands.find(a_Command) == m_Commands.end()); + m_Commands[a_Command] = a_FnRef; +} + + + + + +void cPluginLua::BindConsoleCommand(const AString & a_Command, int a_FnRef) +{ + ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end()); + m_ConsoleCommands[a_Command] = a_FnRef; +} + + + + + +void cPluginLua::Unreference(int a_LuaRef) +{ + cCSLock Lock(m_CriticalSection); + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, a_LuaRef); +} + + + + + +bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse) +{ + ASSERT(a_FnRef != LUA_REFNIL); + + cCSLock Lock(m_CriticalSection); + bool res; + m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res); + return res; +} + + + + + +void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum) +{ + ASSERT(a_FnRef != LUA_REFNIL); + + cCSLock Lock(m_CriticalSection); + m_LuaState.Call(a_FnRef, &a_Window, a_SlotNum); +} + + + + diff --git a/src/PluginLua.h b/src/PluginLua.h new file mode 100644 index 000000000..908466966 --- /dev/null +++ b/src/PluginLua.h @@ -0,0 +1,202 @@ + +// PluginLua.h + +// Declares the cPluginLua class representing a plugin written in Lua + + + + + +#pragma once + +#include "Plugin.h" +#include "WebPlugin.h" +#include "LuaState.h" + +// Names for the global variables through which the plugin is identified in its LuaState +#define LUA_PLUGIN_NAME_VAR_NAME "_MCServerInternal_PluginName" +#define LUA_PLUGIN_INSTANCE_VAR_NAME "_MCServerInternal_PluginInstance" + + + + +// fwd: UI/Window.h +class cWindow; + + + + + +// tolua_begin +class cPluginLua : + public cPlugin, + public cWebPlugin +{ +public: + // tolua_end + + cPluginLua( const AString & a_PluginDirectory ); + ~cPluginLua(); + + virtual void OnDisable(void) override; + virtual bool Initialize(void) override; + + virtual void Tick(float a_Dt) override; + + virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override; + virtual bool OnChat (cPlayer * a_Player, AString & a_Message) override; + virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override; + virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override; + virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; + virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; + virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; + virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; + virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override; + virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) override; + virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) override; + virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; + virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) override; + virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerEating (cPlayer & a_Player) override; + virtual bool OnPlayerJoined (cPlayer & a_Player) override; + virtual bool OnPlayerMoved (cPlayer & a_Player) override; + virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; + virtual bool OnPlayerShooting (cPlayer & a_Player) override; + virtual bool OnPlayerSpawned (cPlayer & a_Player) override; + virtual bool OnPlayerTossingItem (cPlayer & a_Player) override; + virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override; + virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) override; + virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) override; + virtual bool OnSpawningMonster (cWorld & a_World, cMonster & a_Monster) override; + virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) override; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnWeatherChanged (cWorld & a_World) override; + virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override; + virtual bool OnWorldTick (cWorld & a_World, float a_Dt) override; + + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override; + + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override; + + virtual void ClearCommands(void) override; + + virtual void ClearConsoleCommands(void) override; + + /// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) + bool CanAddOldStyleHook(int a_HookType); + + // cWebPlugin override + virtual const AString GetWebTitle(void) const {return GetName(); } + + // cWebPlugin and WebAdmin stuff + virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override; + bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS << + + /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. + void BindCommand(const AString & a_Command, int a_FnRef); + + /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. + void BindConsoleCommand(const AString & a_Command, int a_FnRef); + + cLuaState & GetLuaState(void) { return m_LuaState; } + + cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; } + + /// Removes a previously referenced object (luaL_unref()) + void Unreference(int a_LuaRef); + + /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true + bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse); + + /// Calls the plugin-specified "cLuaWindow slot changed" callback. + void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum); + + /// Returns the name of Lua function that should handle the specified hook type in the older (#121) API + static const char * GetHookFnName(int a_HookType); + + /** Adds a Lua function to be called for the specified hook. + The function has to be on the Lua stack at the specified index a_FnRefIdx + Returns true if the hook was added successfully. + */ + bool AddHookRef(int a_HookType, int a_FnRefIdx); + + // The following templates allow calls to arbitrary Lua functions residing in the plugin: + + /// Call a Lua function with 0 args + template <typename FnT> bool Call(FnT a_Fn) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn); + } + + /// Call a Lua function with 1 arg + template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0); + } + + /// Call a Lua function with 2 args + template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1); + } + + /// Call a Lua function with 3 args + template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2); + } + + /// Call a Lua function with 4 args + template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) + { + cCSLock Lock(m_CriticalSection); + return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); + } + +protected: + /// Maps command name into Lua function reference + typedef std::map<AString, int> CommandMap; + + /// Provides an array of Lua function references + typedef std::vector<cLuaState::cRef *> cLuaRefs; + + /// Maps hook types into arrays of Lua function references to call for each hook type + typedef std::map<int, cLuaRefs> cHookMap; + + cCriticalSection m_CriticalSection; + cLuaState m_LuaState; + + CommandMap m_Commands; + CommandMap m_ConsoleCommands; + + cHookMap m_HookMap; + + /// Releases all Lua references and closes the LuaState + void Close(void); +} ; // tolua_export + + + + diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp new file mode 100644 index 000000000..f9d8d7d02 --- /dev/null +++ b/src/PluginManager.cpp @@ -0,0 +1,1668 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PluginManager.h" +#include "Plugin.h" +#include "PluginLua.h" +#include "WebAdmin.h" +#include "Item.h" +#include "Root.h" +#include "Server.h" +#include "CommandOutput.h" + +#include "lib/iniFile/iniFile.h" +#include "tolua++.h" +#include "Entities/Player.h" + + + + + +cPluginManager * cPluginManager::Get(void) +{ + return cRoot::Get()->GetPluginManager(); +} + + + + + +cPluginManager::cPluginManager(void) : + m_bReloadPlugins(false) +{ +} + + + + + +cPluginManager::~cPluginManager() +{ + UnloadPluginsNow(); +} + + + + + +void cPluginManager::ReloadPlugins(void) +{ + m_bReloadPlugins = true; +} + + + + + +void cPluginManager::FindPlugins(void) +{ + AString PluginsPath = FILE_IO_PREFIX + AString( "Plugins/" ); + + // First get a clean list of only the currently running plugins, we don't want to mess those up + for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) + { + if (itr->second == NULL) + { + PluginMap::iterator thiz = itr; + ++thiz; + m_Plugins.erase( itr ); + itr = thiz; + continue; + } + ++itr; + } + + AStringVector Files = cFile::GetFolderContents(PluginsPath.c_str()); + for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + { + if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) + { + // 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()) + { + m_Plugins[*itr] = NULL; + } + } +} + + + + + +void cPluginManager::ReloadPluginsNow(void) +{ + cIniFile a_SettingsIni; + a_SettingsIni.ReadFile("settings.ini"); + ReloadPluginsNow(a_SettingsIni); +} + + + + + +void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) +{ + LOG("-- Loading Plugins --"); + m_bReloadPlugins = false; + UnloadPluginsNow(); + + FindPlugins(); + + cServer::BindBuiltInConsoleCommands(); + + // Check if the Plugins section exists. + int KeyNum = a_SettingsIni.FindKey("Plugins"); + + // If it does, how many plugins are there? + unsigned int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0); + + if (KeyNum == -1) + { + InsertDefaultPlugins(a_SettingsIni); + } + else if (NumPlugins > 0) + { + for(unsigned int i = 0; i < NumPlugins; i++) + { + AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); + if (ValueName.compare("Plugin") == 0) + { + AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); + if (!PluginFile.empty()) + { + if (m_Plugins.find(PluginFile) != m_Plugins.end()) + { + LoadPlugin( PluginFile ); + } + } + } + } + } + + if (GetNumPlugins() == 0) + { + LOG("-- No Plugins Loaded --"); + } + else if (GetNumPlugins() > 1) + { + LOG("-- Loaded %i Plugins --", GetNumPlugins()); + } + else + { + LOG("-- Loaded 1 Plugin --"); + } +} + + + + + +void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) +{ + a_SettingsIni.AddKeyName("Plugins"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx"); + a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump"); + a_SettingsIni.SetValue("Plugins", "Plugin", "Core"); + a_SettingsIni.SetValue("Plugins", "Plugin", "TransAPI"); + a_SettingsIni.SetValue("Plugins", "Plugin", "ChatLog"); +} + + + + + +void cPluginManager::Tick(float a_Dt) +{ + while (!m_DisablePluginList.empty()) + { + RemovePlugin(m_DisablePluginList.front()); + m_DisablePluginList.pop_front(); + } + + if (m_bReloadPlugins) + { + ReloadPluginsNow(); + } + + HookMap::iterator Plugins = m_Hooks.find(HOOK_TICK); + if (Plugins != m_Hooks.end()) + { + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + (*itr)->Tick(a_Dt); + } + } +} + + + + + +bool cPluginManager::CallHookBlockToPickups( + cWorld * a_World, cEntity * a_Digger, + int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + cItems & a_Pickups +) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_TO_PICKUPS); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_Pickups)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message) +{ + if (ExecuteCommand(a_Player, a_Message)) + { + return true; + } + + // Check if it was a standard command (starts with a slash) + if (!a_Message.empty() && (a_Message[0] == '/')) + { + AStringVector Split(StringSplit(a_Message, " ")); + ASSERT(!Split.empty()); // This should not happen - we know there's at least one char in the message so the split needs to be at least one item long + a_Player->SendMessage(Printf("Unknown Command: \"%s\"", Split[0].c_str())); + LOGINFO("Player \"%s\" issued an unknown command: \"%s\"", a_Player->GetName().c_str(), a_Message.c_str()); + return true; // Cancel sending + } + + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT); + if (Plugins == m_Hooks.end()) + { + return false; + } + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChat(a_Player, a_Message)) + { + return true; + } + } + + return false; +} + + + + + +bool cPluginManager::CallHookChunkAvailable(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_AVAILABLE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkAvailable(a_World, a_ChunkX, a_ChunkZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkGenerated(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkUnloaded(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkUnloaded(a_World, a_ChunkX, a_ChunkZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChunkUnloading(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_UNLOADING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookCollectingPickup(cPlayer * a_Player, cPickup & a_Pickup) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_COLLECTING_PICKUP); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnCollectingPickup(a_Player, &a_Pickup)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CRAFTING_NO_RECIPE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnDisconnect(a_Player, a_Reason)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_EXECUTE_COMMAND); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnExecuteCommand(a_Player, a_Split)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnExploded(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_EXPLODING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnExploding(a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const AString & a_Username) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HANDSHAKE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHandshake(a_ClientHandle, a_Username)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PULLING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHopperPullingItem(a_World, a_Hopper, a_DstSlotNum, a_SrcEntity, a_SrcSlotNum)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HOPPER_PUSHING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHopperPushingItem(a_World, a_Hopper, a_SrcSlotNum, a_DstEntity, a_DstSlotNum)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_KILLING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnKilling(a_Victim, a_Killer)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_LOGIN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerAnimation(cPlayer & a_Player, int a_Animation) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_ANIMATION); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerAnimation(a_Player, a_Animation)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BREAKING_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_BROKEN_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerBrokenBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_EATING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerEating(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_JOINED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerJoined(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_LEFT_CLICK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerLeftClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_MOVING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerMoved(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACED_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_PLACING_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerRightClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICKING_ENTITY); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerRightClickingEntity(a_Player, a_Entity)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SHOOTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerShooting(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerSpawned(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SPAWNED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerSpawned(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerTossingItem(cPlayer & a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_TOSSING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerTossingItem(a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USED_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsedItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_BLOCK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_USING_ITEM); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerUsingItem(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PRE_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_ENTITY); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawnedEntity(a_World, a_Entity)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookSpawnedMonster(cWorld & a_World, cMonster & a_Monster) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNED_MONSTER); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawnedMonster(a_World, a_Monster)) + { + return true; + } + } + return false; +} + + + + +bool cPluginManager::CallHookSpawningEntity(cWorld & a_World, cEntity & a_Entity) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_ENTITY); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawningEntity(a_World, a_Entity)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookSpawningMonster(cWorld & a_World, cMonster & a_Monster) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_SPAWNING_MONSTER); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnSpawningMonster(a_World, a_Monster)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_TAKE_DAMAGE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnTakeDamage(a_Receiver, a_TDI)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATING_SIGN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnUpdatingSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATED_SIGN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnUpdatedSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWeatherChanged(cWorld & a_World) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWeatherChanged(a_World)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWeatherChanging(a_World, a_NewWeather)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WORLD_TICK); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWorldTick(a_World, a_Dt)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions) +{ + ASSERT(a_Player != NULL); + + AStringVector Split(StringSplit(a_Command, " ")); + if (Split.empty()) + { + return false; + } + + CommandMap::iterator cmd = m_Commands.find(Split[0]); + if (cmd == m_Commands.end()) + { + // Command not found + return false; + } + + // Ask plugins first if a command is okay to execute the command: + if (CallHookExecuteCommand(a_Player, Split)) + { + LOGINFO("Player \"%s\" tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player->GetName().c_str(), Split[0].c_str()); + return false; + } + + if ( + a_ShouldCheckPermissions && + !cmd->second.m_Permission.empty() && + !a_Player->HasPermission(cmd->second.m_Permission) + ) + { + LOGINFO("Player \"%s\" tried to execute forbidden command \"%s\".", a_Player->GetName().c_str(), Split[0].c_str()); + return false; + } + + ASSERT(cmd->second.m_Plugin != NULL); + + return cmd->second.m_Plugin->HandleCommand(Split, a_Player); +} + + + + + +cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin ) const +{ + for( PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) + { + if (itr->second == NULL ) continue; + if (itr->second->GetName().compare(a_Plugin) == 0) + { + return itr->second; + } + } + return 0; +} + + + + + +const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const +{ + return m_Plugins; +} + + + + + +void cPluginManager::UnloadPluginsNow() +{ + m_Hooks.clear(); + + while (!m_Plugins.empty()) + { + RemovePlugin(m_Plugins.begin()->second); + } + + m_Commands.clear(); + m_ConsoleCommands.clear(); +} + + + + + +bool cPluginManager::DisablePlugin(const AString & a_PluginName) +{ + PluginMap::iterator itr = m_Plugins.find(a_PluginName); + if (itr == m_Plugins.end()) + { + return false; + } + + if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does? + { + m_DisablePluginList.push_back(itr->second); + itr->second = NULL; // Get rid of this thing right away + return true; + } + return false; +} + + + + + +bool cPluginManager::LoadPlugin(const AString & a_PluginName) +{ + return AddPlugin(new cPluginLua(a_PluginName.c_str())); +} + + + + + +void cPluginManager::RemoveHooks(cPlugin * a_Plugin) +{ + for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr) + { + itr->second.remove(a_Plugin); + } +} + + + + + +void cPluginManager::RemovePlugin(cPlugin * a_Plugin) +{ + for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + { + if (itr->second == a_Plugin) + { + m_Plugins.erase(itr); + break; + } + } + + RemovePluginCommands(a_Plugin); + RemovePluginConsoleCommands(a_Plugin); + RemoveHooks(a_Plugin); + if (a_Plugin != NULL) + { + a_Plugin->OnDisable(); + } + delete a_Plugin; +} + + + + + +void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) +{ + if (a_Plugin != NULL) + { + a_Plugin->ClearCommands(); + } + + for (CommandMap::iterator itr = m_Commands.begin(); itr != m_Commands.end();) + { + if (itr->second.m_Plugin == a_Plugin) + { + CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator + ++itr; + m_Commands.erase(EraseMe); + } + else + { + ++itr; + } + } // for itr - m_Commands[] +} + + + + + +bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) +{ + CommandMap::iterator cmd = m_Commands.find(a_Command); + if (cmd != m_Commands.end()) + { + LOGWARNING("Command \"%s\" is already bound to plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str()); + return false; + } + + m_Commands[a_Command].m_Plugin = a_Plugin; + m_Commands[a_Command].m_Permission = a_Permission; + m_Commands[a_Command].m_HelpString = a_HelpString; + return true; +} + + + + + +bool cPluginManager::ForEachCommand(cCommandEnumCallback & a_Callback) +{ + for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) + { + if (a_Callback.Command(itr->first, itr->second.m_Plugin, itr->second.m_Permission, itr->second.m_HelpString)) + { + return false; + } + } // for itr - m_Commands[] + return true; +} + + + + + +bool cPluginManager::IsCommandBound(const AString & a_Command) +{ + return (m_Commands.find(a_Command) != m_Commands.end()); +} + + + + + +AString cPluginManager::GetCommandPermission(const AString & a_Command) +{ + CommandMap::iterator cmd = m_Commands.find(a_Command); + return (cmd == m_Commands.end()) ? "" : cmd->second.m_Permission; +} + + + + + +bool cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Command) +{ + return HandleCommand(a_Player, a_Command, true); +} + + + + + +bool cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command) +{ + return HandleCommand(a_Player, a_Command, false); +} + + + + + +void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) +{ + if (a_Plugin != NULL) + { + a_Plugin->ClearConsoleCommands(); + } + + for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();) + { + if (itr->second.m_Plugin == a_Plugin) + { + CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator + ++itr; + m_ConsoleCommands.erase(EraseMe); + } + else + { + ++itr; + } + } // for itr - m_Commands[] +} + + + + + +bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString) +{ + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command); + if (cmd != m_ConsoleCommands.end()) + { + if (cmd->second.m_Plugin == NULL) + { + LOGWARNING("Console command \"%s\" is already bound internally by MCServer, cannot bind in plugin \"%s\".", a_Command.c_str(), a_Plugin->GetName().c_str()); + } + else + { + LOGWARNING("Console command \"%s\" is already bound to plugin \"%s\", cannot bind in plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str(), a_Plugin->GetName().c_str()); + } + return false; + } + + m_ConsoleCommands[a_Command].m_Plugin = a_Plugin; + m_ConsoleCommands[a_Command].m_Permission = ""; + m_ConsoleCommands[a_Command].m_HelpString = a_HelpString; + return true; +} + + + + + +bool cPluginManager::ForEachConsoleCommand(cCommandEnumCallback & a_Callback) +{ + for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) + { + if (a_Callback.Command(itr->first, itr->second.m_Plugin, "", itr->second.m_HelpString)) + { + return false; + } + } // for itr - m_Commands[] + return true; +} + + + + + +bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) +{ + return (m_ConsoleCommands.find(a_Command) != m_ConsoleCommands.end()); +} + + + + + +bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +{ + if (a_Split.empty()) + { + return false; + } + + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); + if (cmd == m_ConsoleCommands.end()) + { + // Command not found + return false; + } + + if (cmd->second.m_Plugin == NULL) + { + // This is a built-in command + return false; + } + + // Ask plugins first if a command is okay to execute the console command: + if (CallHookExecuteCommand(NULL, a_Split)) + { + a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); + return false; + } + + return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); +} + + + + + +void cPluginManager::TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player) +{ + for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) + { + if (NoCaseCompare(itr->first.substr(0, a_Text.length()), a_Text) != 0) + { + // Command name doesn't match + continue; + } + if ((a_Player != NULL) && !a_Player->HasPermission(itr->second.m_Permission)) + { + // Player doesn't have permission for the command + continue; + } + a_Results.push_back(itr->first); + } +} + + + + + +bool cPluginManager::IsValidHookType(int a_HookType) +{ + return ((a_HookType >= 0) && (a_HookType <= HOOK_MAX)); +} + + + + + +bool cPluginManager::AddPlugin(cPlugin * a_Plugin) +{ + m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; + if (a_Plugin->Initialize()) + { + // Initialization OK + return true; + } + + // Initialization failed + RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made + return false; +} + + + + + +void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) +{ + if (!a_Plugin) + { + LOGWARN("Called cPluginManager::AddHook() with a_Plugin == NULL"); + return; + } + PluginList & Plugins = m_Hooks[a_Hook]; + Plugins.remove(a_Plugin); + Plugins.push_back(a_Plugin); +} + + + + + +unsigned int cPluginManager::GetNumPlugins() const +{ + return m_Plugins.size(); +} + + + + diff --git a/src/PluginManager.h b/src/PluginManager.h new file mode 100644 index 000000000..4140bffb5 --- /dev/null +++ b/src/PluginManager.h @@ -0,0 +1,295 @@ + +#pragma once + +#include "Item.h" + + + + + +class cPlugin; + +// fwd: World.h +class cWorld; + +// fwd: ChunkDesc.h +class cChunkDesc; + +// fwd: Entities/Entity.h +class cEntity; + +// fwd: Mobs/Monster.h +class cMonster; + +// fwd: Player.h +class cPlayer; + +// fwd: CraftingRecipes.h +class cCraftingGrid; +class cCraftingRecipe; + +// fwd: Pickup.h +class cPickup; + +// fwd: Pawn.h +struct TakeDamageInfo; +class cPawn; + +// fwd: CommandOutput.h +class cCommandOutputCallback; + +// fwd: BlockEntities/HopperEntity.h +class cHopperEntity; + +// fwd: BlockEntities/BlockEntityWithItems.h +class cBlockEntityWithItems; + + + + + +class cPluginManager // tolua_export +{ // tolua_export +public: // tolua_export + + // Called each tick + virtual void Tick(float a_Dt); + + // tolua_begin + enum PluginHook + { + HOOK_BLOCK_TO_PICKUPS, + HOOK_CHAT, + HOOK_CHUNK_AVAILABLE, + HOOK_CHUNK_GENERATED, + HOOK_CHUNK_GENERATING, + HOOK_CHUNK_UNLOADED, + HOOK_CHUNK_UNLOADING, + HOOK_COLLECTING_PICKUP, + HOOK_CRAFTING_NO_RECIPE, + HOOK_DISCONNECT, + HOOK_EXECUTE_COMMAND, + HOOK_EXPLODED, + HOOK_EXPLODING, + HOOK_HANDSHAKE, + HOOK_HOPPER_PULLING_ITEM, + HOOK_HOPPER_PUSHING_ITEM, + HOOK_KILLING, + HOOK_LOGIN, + HOOK_PLAYER_ANIMATION, + HOOK_PLAYER_BREAKING_BLOCK, + HOOK_PLAYER_BROKEN_BLOCK, + HOOK_PLAYER_EATING, + HOOK_PLAYER_JOINED, + HOOK_PLAYER_LEFT_CLICK, + HOOK_PLAYER_MOVING, + HOOK_PLAYER_PLACED_BLOCK, + HOOK_PLAYER_PLACING_BLOCK, + HOOK_PLAYER_RIGHT_CLICK, + HOOK_PLAYER_RIGHT_CLICKING_ENTITY, + HOOK_PLAYER_SHOOTING, + HOOK_PLAYER_SPAWNED, + HOOK_PLAYER_TOSSING_ITEM, + HOOK_PLAYER_USED_BLOCK, + HOOK_PLAYER_USED_ITEM, + HOOK_PLAYER_USING_BLOCK, + HOOK_PLAYER_USING_ITEM, + HOOK_POST_CRAFTING, + HOOK_PRE_CRAFTING, + HOOK_SPAWNED_ENTITY, + HOOK_SPAWNED_MONSTER, + HOOK_SPAWNING_ENTITY, + HOOK_SPAWNING_MONSTER, + HOOK_TAKE_DAMAGE, + HOOK_TICK, + HOOK_UPDATED_SIGN, + HOOK_UPDATING_SIGN, + HOOK_WEATHER_CHANGED, + HOOK_WEATHER_CHANGING, + HOOK_WORLD_TICK, + + // Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants, + // and it definitely needs adding in cPluginLua::GetHookFnName() ! + + // Keep these two as the last items, they are used for validity checking and get their values automagically + HOOK_NUM_HOOKS, + HOOK_MAX = HOOK_NUM_HOOKS - 1, + } ; + // tolua_end + + /// Used as a callback for enumerating bound commands + class cCommandEnumCallback + { + public: + /** Called for each command; return true to abort enumeration + For console commands, a_Permission is not used (set to empty string) + */ + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; + } ; + + /// Returns the instance of the Plugin Manager (there is only ever one) + static cPluginManager * Get(void); // tolua_export + + typedef std::map< AString, cPlugin * > PluginMap; + typedef std::list< cPlugin * > PluginList; + cPlugin * GetPlugin( const AString & a_Plugin ) const; // tolua_export + const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << + + void FindPlugins(); // tolua_export + void ReloadPlugins(); // tolua_export + + /// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add + void AddHook(cPlugin * a_Plugin, int a_HookType); + + unsigned int GetNumPlugins() const; // tolua_export + + // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort + bool CallHookBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups); + bool CallHookChat (cPlayer * a_Player, AString & a_Message); + bool CallHookChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + bool CallHookChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); + bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc); + bool CallHookChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup); + bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason); + bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd + bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); + bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); + bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username); + bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum); + bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum); + bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer); + bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); + bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation); + bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerEating (cPlayer & a_Player); + bool CallHookPlayerJoined (cPlayer & a_Player); + bool CallHookPlayerMoving (cPlayer & a_Player); + bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); + bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity); + bool CallHookPlayerShooting (cPlayer & a_Player); + bool CallHookPlayerSpawned (cPlayer & a_Player); + bool CallHookPlayerTossingItem (cPlayer & a_Player); + bool CallHookPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity); + bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster); + bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity); + bool CallHookSpawningMonster (cWorld & a_World, cMonster & a_Monster); + bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI); + bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); + bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); + bool CallHookWeatherChanged (cWorld & a_World); + bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather); + bool CallHookWorldTick (cWorld & a_World, float a_Dt); + + bool DisablePlugin(const AString & a_PluginName); // tolua_export + bool LoadPlugin (const AString & a_PluginName); // tolua_export + + /// Removes all hooks the specified plugin has registered + void RemoveHooks(cPlugin * a_Plugin); + + /// Removes the plugin from the internal structures and deletes its object. + void RemovePlugin(cPlugin * a_Plugin); + + /// Removes all command bindings that the specified plugin has made + void RemovePluginCommands(cPlugin * a_Plugin); + + /// Binds a command to the specified plugin. Returns true if successful, false if command already bound. + bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + + /// Calls a_Callback for each bound command, returns true if all commands were enumerated + bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Returns true if the command is in the command map + bool IsCommandBound(const AString & a_Command); // tolua_export + + /// Returns the permission needed for the specified command; empty string if command not found + AString GetCommandPermission(const AString & a_Command); // tolua_export + + /// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. + bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export + + /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) + bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export + + /// Removes all console command bindings that the specified plugin has made + void RemovePluginConsoleCommands(cPlugin * a_Plugin); + + /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound. + bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + + /// Calls a_Callback for each bound console command, returns true if all commands were enumerated + bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Returns true if the console command is in the command map + bool IsConsoleCommandBound(const AString & a_Command); // tolua_export + + /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback + bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); + + /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. + If a_Player is not NULL, only commands for which the player has permissions are added. + */ + void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player); + + /// Returns true if the specified hook type is within the allowed range + static bool IsValidHookType(int a_HookType); + +private: + friend class cRoot; + + class cCommandReg + { + public: + cPlugin * m_Plugin; + AString m_Permission; // Not used for console commands + AString m_HelpString; + } ; + + typedef std::map<int, cPluginManager::PluginList> HookMap; + typedef std::map<AString, cCommandReg> CommandMap; + + PluginList m_DisablePluginList; + PluginMap m_Plugins; + HookMap m_Hooks; + CommandMap m_Commands; + CommandMap m_ConsoleCommands; + + bool m_bReloadPlugins; + + cPluginManager(); + ~cPluginManager(); + + /// Reloads all plugins, defaulting to settings.ini for settings location + void ReloadPluginsNow(void); + + /// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini + void ReloadPluginsNow(cIniFile & a_SettingsIni); + + /// Unloads all plugins + void UnloadPluginsNow(void); + + /// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini + void InsertDefaultPlugins(cIniFile & a_SettingsIni); + + /// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. + bool AddPlugin(cPlugin * a_Plugin); + + /// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. + bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); +} ; // tolua_export + + + + diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp new file mode 100644 index 000000000..5fa17c276 --- /dev/null +++ b/src/ProbabDistrib.cpp @@ -0,0 +1,142 @@ + +// ProbabDistrib.cpp + +// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator + +#include "Globals.h" +#include "ProbabDistrib.h" +#include "MersenneTwister.h" + + + + + + +cProbabDistrib::cProbabDistrib(int a_MaxValue) : + m_MaxValue(a_MaxValue), + m_Sum(-1) +{ +} + + + + + + +void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points) +{ + ASSERT(!a_Points.empty()); + m_Sum = 0; + m_Cumulative.clear(); + m_Cumulative.reserve(a_Points.size() + 1); + int ProbSum = 0; + int LastProb = 0; + int LastValue = -1; + if (a_Points[0].m_Value != 0) + { + m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds + LastValue = 0; + } + for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr) + { + if (itr->m_Value == LastValue) + { + continue; + } + + // Add the current trapezoid to the sum: + ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2; + LastProb = itr->m_Probability; + LastValue = itr->m_Value; + m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum)); + } // for itr - a_Points[] + if (LastValue != m_MaxValue) + { + m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds + } + m_Sum = ProbSum; +} + + + + + +bool cProbabDistrib::SetDefString(const AString & a_DefString) +{ + AStringVector Points = StringSplitAndTrim(a_DefString, ";"); + if (Points.empty()) + { + return false; + } + cPoints Pts; + for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr) + { + AStringVector Split = StringSplitAndTrim(*itr, ","); + if (Split.size() != 2) + { + // Bad format + return false; + } + int Value = atoi(Split[0].c_str()); + int Prob = atoi(Split[1].c_str()); + if ( + ((Value == 0) && (Split[0] != "0")) || + ((Prob == 0) && (Split[1] != "0")) + ) + { + // Number parse error + return false; + } + Pts.push_back(cPoint(Value, Prob)); + } // for itr - Points[] + + SetPoints(Pts); + return true; +} + + + + + +int cProbabDistrib::Random(MTRand & a_Rand) const +{ + int v = a_Rand.randInt(m_Sum); + return MapValue(v); +} + + + + + +int cProbabDistrib::MapValue(int a_OrigValue) const +{ + ASSERT(a_OrigValue >= 0); + ASSERT(a_OrigValue < m_Sum); + + // Binary search through m_Cumulative for placement: + size_t Lo = 0; + size_t Hi = m_Cumulative.size() - 1; + while (Hi - Lo > 1) + { + int Mid = (Lo + Hi) / 2; + int MidProbab = m_Cumulative[Mid].m_Probability; + if (MidProbab < a_OrigValue) + { + Lo = Mid; + } + else + { + Hi = Mid; + } + } + ASSERT(Hi - Lo == 1); + + // Linearly interpolate between Lo and Hi: + int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability; + int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value; + return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif; +} + + + + diff --git a/src/ProbabDistrib.h b/src/ProbabDistrib.h new file mode 100644 index 000000000..ddaadd9b7 --- /dev/null +++ b/src/ProbabDistrib.h @@ -0,0 +1,74 @@ + +// ProbabDistrib.h + +// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator + +/* +Usage: +1, Create a cProbabDistrib instance +2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string +3, Ask for random numbers in that probability distribution using the Random() function +*/ + + + + + +#pragma once + + + + + +// fwd: +class MTRand; + + + + + +class cProbabDistrib +{ +public: + class cPoint + { + public: + int m_Value; + int m_Probability; + + cPoint(int a_Value, int a_Probability) : + m_Value(a_Value), + m_Probability(a_Probability) + { + } + } ; + + typedef std::vector<cPoint> cPoints; + + + cProbabDistrib(int a_MaxValue); + + /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty. + void SetPoints(const cPoints & a_Points); + + /// Sets the distribution curve using a definition string; returns true on successful parse + bool SetDefString(const AString & a_DefString); + + /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value. + int Random(MTRand & a_Rand) const; + + /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability + int MapValue(int a_OrigValue) const; + + int GetSum(void) const { return m_Sum; } + +protected: + + int m_MaxValue; + cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup + int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set +} ; + + + + diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp new file mode 100644 index 000000000..d406b8b51 --- /dev/null +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -0,0 +1,176 @@ + +// ChunkDataSerializer.cpp + +// Implements the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "lib/zlib/zlib.h" + + + + +cChunkDataSerializer::cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData +) : + m_BlockTypes(a_BlockTypes), + m_BlockMetas(a_BlockMetas), + m_BlockLight(a_BlockLight), + m_BlockSkyLight(a_BlockSkyLight), + m_BiomeData(a_BiomeData) +{ +} + + + + +const AString & cChunkDataSerializer::Serialize(int a_Version) +{ + Serializations::const_iterator itr = m_Serializations.find(a_Version); + if (itr != m_Serializations.end()) + { + return itr->second; + } + + AString data; + switch (a_Version) + { + case RELEASE_1_2_5: Serialize29(data); break; + case RELEASE_1_3_2: Serialize39(data); break; + // TODO: Other protocol versions may serialize the data differently; implement here + + default: + { + LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); + ASSERT(!"Unknown chunk data serialization version"); + break; + } + } + m_Serializations[a_Version] = data; + return m_Serializations[a_Version]; +} + + + + + +void cChunkDataSerializer::Serialize29(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + Int32 UnusedInt32 = 0; + a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + + +void cChunkDataSerializer::Serialize39(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + // Unlike 29, 39 doesn't have the "unused" int + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h new file mode 100644 index 000000000..a42856356 --- /dev/null +++ b/src/Protocol/ChunkDataSerializer.h @@ -0,0 +1,48 @@ + +// ChunkDataSerializer.h + +// Interfaces to the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + + + + + +class cChunkDataSerializer +{ +protected: + const cChunkDef::BlockTypes & m_BlockTypes; + const cChunkDef::BlockNibbles & m_BlockMetas; + const cChunkDef::BlockNibbles & m_BlockLight; + const cChunkDef::BlockNibbles & m_BlockSkyLight; + const unsigned char * m_BiomeData; + + typedef std::map<int, AString> Serializations; + + Serializations m_Serializations; + + void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 + void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2 + +public: + enum + { + RELEASE_1_2_5 = 29, + RELEASE_1_3_2 = 39, + } ; + + cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData + ); + + const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] +} ; + + + + diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h new file mode 100644 index 000000000..9d8183361 --- /dev/null +++ b/src/Protocol/Protocol.h @@ -0,0 +1,218 @@ + +// Protocol.h + +// Interfaces to the cProtocol class representing the generic interface that a protocol +// parser and serializer must implement + + + + + +#pragma once + +#include "../Defines.h" +#include "../Endianness.h" + + + + +class cExpOrb; +class cPlayer; +class cEntity; +class cWindow; +class cInventory; +class cPawn; +class cPickup; +class cWorld; +class cMonster; +class cChunkDataSerializer; +class cFallingBlock; + + + + + +typedef unsigned char Byte; + + + + + +class cProtocol +{ +public: + cProtocol(cClientHandle * a_Client) : + m_Client(a_Client) + { + } + virtual ~cProtocol() {} + + /// Called when client sends some data + virtual void DataReceived(const char * a_Data, int a_Size) = 0; + + // Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; + virtual void SendChat (const AString & a_Message) = 0; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; + virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; + virtual void SendDisconnect (const AString & a_Reason) = 0; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0; + virtual void SendEntityLook (const cEntity & a_Entity) = 0; + virtual void SendEntityMetadata (const cEntity & a_Entity) = 0; + virtual void SendEntityProperties (const cEntity & a_Entity) = 0; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0; + virtual void SendEntityVelocity (const cEntity & a_Entity) = 0; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0; + virtual void SendGameMode (eGameMode a_GameMode) = 0; + virtual void SendHealth (void) = 0; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendKeepAlive (int a_PingID) = 0; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; + virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; + virtual void SendPlayerAbilities (void) = 0; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; + virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) + virtual void SendPlayerMoveLook (void) = 0; + virtual void SendPlayerPosition (void) = 0; + virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; + virtual void SendRespawn (void) = 0; + virtual void SendExperience (void) = 0; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; + virtual void SendSpawnMob (const cMonster & a_Mob) = 0; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0; + virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0; + virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0; + virtual void SendWeather (eWeather a_Weather) = 0; + virtual void SendWholeInventory (const cWindow & a_Window) = 0; + virtual void SendWindowClose (const cWindow & a_Window) = 0; + virtual void SendWindowOpen (const cWindow & a_Window) = 0; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0; + + /// Returns the ServerID used for authentication through session.minecraft.net + virtual AString GetAuthServerID(void) = 0; + +protected: + cClientHandle * m_Client; + cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once + + /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it + virtual void SendData(const char * a_Data, int a_Size) = 0; + + /// Called after writing each packet, enables descendants to flush their buffers + virtual void Flush(void) {}; + + // Helpers for writing partial packet data, write using SendData() + void WriteByte(Byte a_Value) + { + SendData((const char *)&a_Value, 1); + } + + void WriteShort(short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + + /* + void WriteShort(unsigned short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + */ + + void WriteInt(int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteUInt(unsigned int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteInt64 (Int64 a_Value) + { + a_Value = HostToNetwork8(&a_Value); + SendData((const char *)&a_Value, 8); + } + + void WriteFloat (float a_Value) + { + unsigned int val = HostToNetwork4(&a_Value); + SendData((const char *)&val, 4); + } + + void WriteDouble(double a_Value) + { + unsigned long long val = HostToNetwork8(&a_Value); + SendData((const char *)&val, 8); + } + + void WriteString(const AString & a_Value) + { + AString UTF16; + UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16); + WriteShort((unsigned short)(UTF16.size() / 2)); + SendData(UTF16.data(), UTF16.size()); + } + + void WriteBool(bool a_Value) + { + WriteByte(a_Value ? 1 : 0); + } + + void WriteVectorI(const Vector3i & a_Vector) + { + WriteInt(a_Vector.x); + WriteInt(a_Vector.y); + WriteInt(a_Vector.z); + } + + void WriteVarInt(UInt32 a_Value) + { + // A 32-bit integer can be encoded by at most 5 bytes: + unsigned char b[5]; + int idx = 0; + do + { + b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); + a_Value = a_Value >> 7; + idx++; + } while (a_Value > 0); + + SendData((const char *)b, idx); + } + + void WriteVarUTF8String(const AString & a_String) + { + WriteVarInt(a_String.size()); + SendData(a_String.data(), a_String.size()); + } +} ; + + + + + diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp new file mode 100644 index 000000000..b1dd17ea1 --- /dev/null +++ b/src/Protocol/Protocol125.cpp @@ -0,0 +1,1902 @@ + +// Protocol125.cpp + +// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29) +/* +Documentation: + - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513 + - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152 +*/ + +#include "Globals.h" + +#include "Protocol125.h" + +#include "../ClientHandle.h" +#include "../World.h" +#include "ChunkDataSerializer.h" +#include "../Entities/Entity.h" +#include "../Entities/ExpOrb.h" +#include "../Mobs/Monster.h" +#include "../Entities/Pickup.h" +#include "../Entities/Player.h" +#include "../ChatColor.h" +#include "../UI/Window.h" +#include "../Root.h" +#include "../Server.h" + +#include "../Entities/ProjectileEntity.h" +#include "../Entities/Minecart.h" +#include "../Entities/FallingBlock.h" + +#include "../Mobs/IncludeAllMonsters.h" + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_HANDSHAKE = 0x02, + PACKET_CHAT = 0x03, + PACKET_UPDATE_TIME = 0x04, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_USE_ENTITY = 0x07, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_RESPAWN = 0x09, + PACKET_PLAYER_ON_GROUND = 0x0a, + PACKET_PLAYER_POS = 0x0b, + PACKET_PLAYER_LOOK = 0x0c, + PACKET_PLAYER_MOVE_LOOK = 0x0d, + PACKET_BLOCK_DIG = 0x0e, + PACKET_BLOCK_PLACE = 0x0f, + PACKET_SLOT_SELECTED = 0x10, + PACKET_USE_BED = 0x11, + PACKET_ANIMATION = 0x12, + PACKET_PACKET_ENTITY_ACTION = 0x13, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_SPAWN_MOB = 0x18, + PACKET_ENTITY_VELOCITY = 0x1c, + PACKET_DESTROY_ENTITY = 0x1d, + PACKET_ENTITY = 0x1e, + PACKET_ENT_REL_MOVE = 0x1f, + PACKET_ENT_LOOK = 0x20, + PACKET_ENT_REL_MOVE_LOOK = 0x21, + PACKET_ENT_TELEPORT = 0x22, + PACKET_ENT_HEAD_LOOK = 0x23, + PACKET_ENT_STATUS = 0x26, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_METADATA = 0x28, + PACKET_SPAWN_EXPERIENCE_ORB = 0x1A, + PACKET_EXPERIENCE = 0x2b, + PACKET_PRE_CHUNK = 0x32, + PACKET_MAP_CHUNK = 0x33, + PACKET_MULTI_BLOCK = 0x34, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_EXPLOSION = 0x3C, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_CHANGE_GAME_STATE = 0x46, + PACKET_THUNDERBOLT = 0x47, + PACKET_WINDOW_OPEN = 0x64, + PACKET_WINDOW_CLOSE = 0x65, + PACKET_WINDOW_CLICK = 0x66, + PACKET_INVENTORY_SLOT = 0x67, + PACKET_INVENTORY_WHOLE = 0x68, + PACKET_WINDOW_PROPERTY = 0x69, + PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, + PACKET_UPDATE_SIGN = 0x82, + PACKET_PLAYER_LIST_ITEM = 0xC9, + PACKET_PLAYER_ABILITIES = 0xca, + PACKET_PLUGIN_MESSAGE = 0xfa, + PACKET_PING = 0xfe, + PACKET_DISCONNECT = 0xff +} ; + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +typedef unsigned char Byte; + + + + + +cProtocol125::cProtocol125(cClientHandle * a_Client) : + super(a_Client), + m_ReceivedData(32 KiB) +{ +} + + + + + +void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ATTACH_ENTITY); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + UNUSED(a_BlockType); + + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + Flush(); +} + + + + + +void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + // Not supported in this protocol version +} + + + + + +void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteByte(a_BlockType); + WriteByte(a_BlockMeta); + Flush(); +} + + + + + +void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + cCSLock Lock(m_CSPacket); + if (a_Changes.size() == 1) + { + // Special packet for single-block changes + const sSetBlock & blk = a_Changes.front(); + SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta); + return; + } + + WriteByte (PACKET_MULTI_BLOCK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteShort((unsigned short)a_Changes.size()); + WriteUInt (sizeof(int) * a_Changes.size()); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); + unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + WriteUInt(Coords << 16 | Blocks); + } + Flush(); +} + + + + + +void cProtocol125::SendChat(const AString & a_Message) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_CHAT); + WriteString(a_Message); + Flush(); +} + + + + + +void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Send the pre-chunk: + SendPreChunk(a_ChunkX, a_ChunkZ, true); + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); + WriteByte(PACKET_MAP_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDestroyEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITY); + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDisconnect(const AString & a_Reason) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_DISCONNECT); + WriteString(a_Reason); + Flush(); +} + + + + + +void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // This protocol version doesn't support this packet, sign editor is invoked by the client automatically + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); +} + + + + + +void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteShort(a_Item.m_ItemType); + WriteShort(a_Item.m_ItemDamage); + Flush(); +} + + + + + +void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_HEAD_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityMetadata(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_METADATA); + WriteInt (a_Entity.GetUniqueID()); + + WriteCommonMetadata(a_Entity); + if (a_Entity.IsMob()) + { + WriteMobMetadata(((const cMonster &)a_Entity)); + } + else + { + WriteEntityMetadata(a_Entity); + } + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol125::SendEntityProperties(const cEntity & a_Entity) +{ + // Not supported in this protocol version +} + + + + + +void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + Flush(); +} + + + + + +void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_STATUS); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_Status); + Flush(); +} + + + + + +void cProtocol125::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_VELOCITY); + WriteInt (a_Entity.GetUniqueID()); + WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20 + WriteShort((short) (a_Entity.GetSpeedY() * 400)); + WriteShort((short) (a_Entity.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_EXPLOSION); + WriteDouble (a_BlockX); + WriteDouble (a_BlockY); + 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 - 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); + Flush(); +} + + + + + +void cProtocol125::SendGameMode(eGameMode a_GameMode) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(3); + WriteByte((char)a_GameMode); + Flush(); +} + + + + + +void cProtocol125::SendHandshake(const AString & a_ConnectionHash) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_HANDSHAKE); + WriteString(a_ConnectionHash); + Flush(); +} + + + + + +void cProtocol125::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteShort((short)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_INVENTORY_SLOT); + WriteByte (a_WindowID); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol125::SendKeepAlive(int a_PingID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_KEEP_ALIVE); + WriteInt (a_PingID); + Flush(); +} + + + + + +void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + UNUSED(a_World); + cCSLock Lock(m_CSPacket); + + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString(""); // Username, not used + WriteString("default"); // Level type + WriteInt ((int)a_Player.GetGameMode()); + WriteInt ((int)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused + WriteByte (60); // Client list width or something + Flush(); +} + + + + + +void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteShort (a_Pickup.GetItem().m_ItemType); + WriteByte (a_Pickup.GetItem().m_ItemCount); + WriteShort (a_Pickup.GetItem().m_ItemDamage); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ANIMATION); + WriteInt (a_Player.GetUniqueID()); + WriteByte(a_Animation); + Flush(); +} + + + + + +void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + cCSLock Lock(m_CSPacket); + AString PlayerName(a_Player.GetColor()); + PlayerName.append(a_Player.GetName()); + if (PlayerName.length() > 14) + { + PlayerName.erase(14); + } + PlayerName += cChatColor::White; + + WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); + WriteString(PlayerName); + WriteBool (a_IsOnline); + WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); + Flush(); +} + + + + + +void cProtocol125::SendPlayerMaxSpeed(void) +{ + // Not supported by this protocol version +} + + + + + +void cProtocol125::SendPlayerMoveLook(void) +{ + cCSLock Lock(m_CSPacket); + + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + + WriteByte (PACKET_PLAYER_MOVE_LOOK); + cPlayer * Player = m_Client->GetPlayer(); + WriteDouble(Player->GetPosX()); + WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosZ()); + WriteFloat ((float)(Player->GetRotation())); + WriteFloat ((float)(Player->GetPitch())); + WriteBool (Player->IsOnGround()); + Flush(); +} + + + + + +void cProtocol125::SendPlayerPosition(void) +{ + cCSLock Lock(m_CSPacket); + LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now +} + + + + + +void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + Flush(); +} + + + + + +void cProtocol125::SendRespawn(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_RESPAWN); + WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension())); + WriteByte (2); // TODO: Difficulty; 2 = Normal + WriteByte ((char)m_Client->GetPlayer()->GetGameMode()); + WriteShort (256); // Current world height + WriteString("default"); +} + + + + + +void cProtocol125::SendExperience(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_EXPERIENCE); + WriteFloat (m_Client->GetPlayer()->GetXpPercentage()); + WriteShort (m_Client->GetPlayer()->GetXpLevel()); + WriteShort (m_Client->GetPlayer()->GetCurrentXp()); + Flush(); +} + + + + + +void cProtocol125::SendExperienceOrb(const cExpOrb & a_ExpOrb) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_EXPERIENCE_ORB); + WriteInt(a_ExpOrb.GetUniqueID()); + WriteInt((int) a_ExpOrb.GetPosX()); + WriteInt((int) a_ExpOrb.GetPosY()); + WriteInt((int) a_ExpOrb.GetPosZ()); + WriteShort(a_ExpOrb.GetReward()); + Flush(); +} + + + + + +void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + // Not needed in this protocol version +} + + + + + +void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + // Not implemented in this protocol version +} + + + + + +void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // This protocol version implements falling blocks using the spawn object / vehicle packet: + SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0); +} + + + + + +void cProtocol125::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte (0); + WriteByte (0); + WriteByte (0); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + UNUSED(a_Yaw); + UNUSED(a_Pitch); + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte (a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (a_VehicleSubType); + if (a_VehicleSubType != 0) + { + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results) +{ + // This protocol version doesn't support tab completion + UNUSED(a_Results); +} + + + + + +void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENT_TELEPORT); + WriteInt (a_Entity.GetUniqueID()); + WriteInt ((int)(floor(a_Entity.GetPosX() * 32))); + WriteInt ((int)(floor(a_Entity.GetPosY() * 32))); + WriteInt ((int)(floor(a_Entity.GetPosZ() * 32))); + WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_THUNDERBOLT); + WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one + WriteBool(true); // Unknown bool + WriteInt (a_BlockX * 32); + WriteInt (a_BlockY * 32); + WriteInt (a_BlockZ * 32); + Flush(); +} + + + + + +void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day: + WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000)); + Flush(); +} + + + + + +void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSPacket); + SendPreChunk(a_ChunkX, a_ChunkZ, false); +} + + + + + +void cProtocol125::SendUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 +) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_UPDATE_SIGN); + WriteInt (a_BlockX); + WriteShort ((short)a_BlockY); + WriteInt (a_BlockZ); + WriteString(a_Line1); + WriteString(a_Line2); + WriteString(a_Line3); + WriteString(a_Line4); + Flush(); +} + + + + + +void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_USE_BED); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(0); // Unknown byte only 0 has been observed + WriteInt (a_BlockX); + WriteByte(a_BlockY); + WriteInt (a_BlockZ); + Flush(); +} + + + + + +void cProtocol125::SendWeather(eWeather a_Weather) +{ + cCSLock Lock(m_CSPacket); + switch( a_Weather ) + { + case eWeather_Sunny: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(2); // Stop rain + WriteByte(0); // Unused + Flush(); + break; + } + + case eWeather_Rain: + case eWeather_ThunderStorm: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(1); // Begin rain + WriteByte(0); // Unused + Flush(); + break; + } + } +} + + + + + +void cProtocol125::SendWholeInventory(const cWindow & a_Window) +{ + cCSLock Lock(m_CSPacket); + cItems Slots; + a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); + SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0])); +} + + + + + +void cProtocol125::SendWindowClose(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() == cWindow::wtInventory) + { + // Do not send inventory-window-close + return; + } + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_WINDOW_CLOSE); + WriteByte(a_Window.GetWindowID()); + Flush(); +} + + + + + +void cProtocol125::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_Window.GetWindowID()); + WriteByte (a_Window.GetWindowType()); + WriteString(a_Window.GetWindowTitle()); + WriteByte (a_Window.GetNumNonInventorySlots()); + Flush(); +} + + + + + +void cProtocol125::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_PROPERTY); + WriteByte (a_Window.GetWindowID()); + WriteShort(a_Property); + WriteShort(a_Value); + Flush(); +} + + + + + +AString cProtocol125::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + // The server generates a random hash and that is used for all clients, unmodified + return cRoot::Get()->GetServer()->GetServerID(); +} + + + + + +void cProtocol125::SendData(const char * a_Data, int a_Size) +{ + m_Client->SendData(a_Data, a_Size); +} + + + + + +void cProtocol125::DataReceived(const char * a_Data, int a_Size) +{ + if (!m_ReceivedData.Write(a_Data, a_Size)) + { + // Too much data in the incoming queue, report to caller: + m_Client->PacketBufferFull(); + return; + } + + // Parse and handle all complete packets in m_ReceivedData: + while (m_ReceivedData.CanReadBytes(1)) + { + unsigned char PacketType; + m_ReceivedData.ReadByte(PacketType); + switch (ParsePacket(PacketType)) + { + case PARSE_UNKNOWN: + { + // An unknown packet has been received, notify the client and abort: + m_Client->PacketUnknown(PacketType); + return; + } + case PARSE_ERROR: + { + // An error occurred while parsing a known packet, notify the client and abort: + m_Client->PacketError(PacketType); + return; + } + case PARSE_INCOMPLETE: + { + // Incomplete packet, bail out and process with the next batch of data + m_ReceivedData.ResetRead(); + return; + } + default: + { + // Packet successfully parsed, commit the read data and try again one more packet + m_ReceivedData.CommitRead(); + break; + } + } + } +} + + + + + +int cProtocol125::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return PARSE_UNKNOWN; + case PACKET_ANIMATION: return ParseArmAnim(); + case PACKET_BLOCK_DIG: return ParseBlockDig(); + case PACKET_BLOCK_PLACE: return ParseBlockPlace(); + case PACKET_CHAT: return ParseChat(); + case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction(); + case PACKET_DISCONNECT: return ParseDisconnect(); + case PACKET_HANDSHAKE: return ParseHandshake(); + case PACKET_KEEP_ALIVE: return ParseKeepAlive(); + case PACKET_LOGIN: return ParseLogin(); + case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction(); + case PACKET_PING: return ParsePing(); + case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities(); + case PACKET_PLAYER_LOOK: return ParsePlayerLook(); + case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook(); + case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround(); + case PACKET_PLAYER_POS: return ParsePlayerPosition(); + case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage(); + case PACKET_RESPAWN: return ParseRespawn(); + case PACKET_SLOT_SELECTED: return ParseSlotSelected(); + case PACKET_UPDATE_SIGN: return ParseUpdateSign(); + case PACKET_USE_ENTITY: return ParseUseEntity(); + case PACKET_WINDOW_CLICK: return ParseWindowClick(); + case PACKET_WINDOW_CLOSE: return ParseWindowClose(); + } +} + + + + + +#define HANDLE_PACKET_PARSE(Packet) \ + { \ + int res = Packet.Parse(m_ReceivedData); \ + if (res < 0) \ + { \ + return res; \ + } \ + } + + + + + +int cProtocol125::ParseArmAnim(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, Animation); + m_Client->HandleAnimation(Animation); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockDig(void) +{ + HANDLE_PACKET_READ(ReadChar, char, Status); + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable. + m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseChat(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message); + m_Client->HandleChat(Message); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseCreativeInventoryAction(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + m_Client->HandleCreativeInventory(SlotNum, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseDisconnect(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason); + m_Client->HandleDisconnect(Reason); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + m_Client->HandleEntityAction(EntityID, ActionID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + + AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565" + if (UserData.empty()) + { + m_Client->Kick("Did not receive username"); + return PARSE_OK; + } + m_Username = UserData[0]; + + LOGD("HANDSHAKE %s", Username.c_str()); + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + SendHandshake(cRoot::Get()->GetServer()->GetServerID()); + LOGD("User \"%s\" was sent a handshake response", m_Username.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseKeepAlive(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID); + m_Client->HandleKeepAlive(KeepAliveID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseLogin(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + HANDLE_PACKET_READ(ReadBEInt, int, ServerMode); + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight); + HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers); + + if (ProtocolVersion < 29) + { + m_Client->Kick("Your client is outdated!"); + return PARSE_OK; + } + else if (ProtocolVersion > 29) + { + m_Client->Kick("Your client version is higher than the server!"); + return PARSE_OK; + } + + if (m_Username.compare(Username) != 0) + { + LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking", + Username.c_str(), + m_Username.c_str(), + m_Client->GetIPString().c_str() + ); + m_Client->Kick("Hacked client"); // Don't tell them why we don't want them + return PARSE_OK; + } + + m_Client->HandleLogin(ProtocolVersion, Username); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePing(void) +{ + // Packet has no more data + m_Client->HandlePing(); + return PARSE_OK; +} + + + + + + +int cProtocol125::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Invulnerable); + HANDLE_PACKET_READ(ReadBool, bool, IsFlying); + HANDLE_PACKET_READ(ReadBool, bool, CanFly); + HANDLE_PACKET_READ(ReadBool, bool, InstaMine); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerLook(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerMoveLook(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0); + m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerOnGround(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // TODO: m_Client->HandleFlying(IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerPosition(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePluginMessage(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); + HANDLE_PACKET_READ(ReadBEShort, short, Length); + AString Data; + if (!m_ReceivedData.ReadString(Data, Length)) + { + m_ReceivedData.CheckValid(); + return PARSE_INCOMPLETE; + } + m_ReceivedData.CheckValid(); + + // TODO: Process the data + LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseRespawn(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadChar, char, CreativeMode); + HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + m_Client->HandleRespawn(); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseSlotSelected(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + m_Client->HandleSlotSelected(SlotNum); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUpdateSign(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4); + m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUseEntity(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID); + HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID); + HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick); + m_Client->HandleUseEntity(TargetEntityID, IsLeftClick); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadBool, bool, IsRightClick); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols: + eClickAction Action; + if (IsRightClick) + { + if (IsShiftPressed) + { + Action = caShiftRightClick; + } + else + { + if (SlotNum == -999) + { + Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside; + } + else + { + Action = caRightClick; + } + } + } + else + { + // IsLeftClick + if (IsShiftPressed) + { + Action = caShiftLeftClick; + } + else + { + if (SlotNum == -999) + { + Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside; + } + else + { + Action = caLeftClick; + } + } + } + m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClose(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + m_Client->HandleWindowClose(WindowID); + return PARSE_OK; +} + + + + + +void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad) +{ + WriteByte(PACKET_PRE_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(a_ShouldLoad); + Flush(); +} + + + + + +void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items) +{ + WriteByte (PACKET_INVENTORY_WHOLE); + WriteByte (a_WindowID); + WriteShort((short)a_NumItems); + + for (int j = 0; j < a_NumItems; j++) + { + WriteItem(a_Items[j]); + } + Flush(); +} + + + + + +void cProtocol125::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + WriteShort(ItemType); + if (a_Item.IsEmpty()) + { + return; + } + + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (cItem::IsEnchantable(a_Item.m_ItemType)) + { + // TODO: Implement enchantments + WriteShort(-1); + } +} + + + + + +int cProtocol125::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + if (!cItem::IsEnchantable(ItemType)) + { + return PARSE_OK; + } + + HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); + + if (EnchantNumBytes <= 0) + { + return PARSE_OK; + } + + // TODO: Enchantment not implemented yet! + if (!m_ReceivedData.SkipRead(EnchantNumBytes)) + { + return PARSE_INCOMPLETE; + } + + return PARSE_OK; +} + + + + + +void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity) +{ + Byte CommonMetadata = 0; + + if (a_Entity.IsOnFire()) + { + CommonMetadata |= 0x1; + } + if (a_Entity.IsCrouched()) + { + CommonMetadata |= 0x2; + } + if (a_Entity.IsRiding()) + { + CommonMetadata |= 0x4; + } + if (a_Entity.IsSprinting()) + { + CommonMetadata |= 0x8; + } + if (a_Entity.IsRclking()) + { + CommonMetadata |= 0x10; + } + if (a_Entity.IsInvisible()) + { + CommonMetadata |= 0x20; + } + + WriteByte(0x0); + WriteByte(CommonMetadata); +} + + + + + +void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity) +{ + if (a_Entity.IsMinecart()) + { + WriteByte(0x51); + // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar + WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4 ); + WriteByte(0x52); + WriteInt(1); // Shaking direction, doesn't seem to affect anything + WriteByte(0x73); + WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer + + if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) + { + WriteByte(0x10); + WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled? + } + } + else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow)) + { + WriteByte(0x10); + WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow? + } +} + + + + + +void cProtocol125::WriteMobMetadata(const cMonster & a_Mob) +{ + switch (a_Mob.GetMobType()) + { + case cMonster::mtCreeper: + { + WriteByte(0x10); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up? + WriteByte(0x11); + WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged? + break; + } + case cMonster::mtBat: + { + WriteByte(0x10); + WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down? + break; + } + case cMonster::mtPig: + { + WriteByte(0x10); + WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled? + break; + } + case cMonster::mtVillager: + { + WriteByte(0x50); + WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE? + break; + } + case cMonster::mtZombie: + { + WriteByte(0xC); + WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Babby zombie? + WriteByte(0xD); + WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie? + WriteByte(0xE); + WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager? + break; + } + case cMonster::mtGhast: + { + WriteByte(0x10); + WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to eject un flamé-bol? :P + break; + } + case cMonster::mtWolf: + { + Byte WolfStatus = 0; + if (((const cWolf &)a_Mob).IsSitting()) + { + WolfStatus |= 0x1; + } + if (((const cWolf &)a_Mob).IsAngry()) + { + WolfStatus |= 0x2; + } + if (((const cWolf &)a_Mob).IsTame()) + { + WolfStatus |= 0x4; + } + WriteByte(0x10); + WriteByte(WolfStatus); + + WriteByte(0x72); + WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way) + WriteByte(0x13); + WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode? + break; + } + case cMonster::mtSheep: + { + // [1](1111) + // [] = Is sheared? () = Color, from 0 to 15 + + WriteByte(0x10); + Byte SheepMetadata = 0; + SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); // Fur colour + + if (((const cSheep &)a_Mob).IsSheared()) // Is sheared? + { + SheepMetadata |= 0x16; + } + WriteByte(SheepMetadata); + break; + } + case cMonster::mtEnderman: + { + WriteByte(0x10); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house + WriteByte(0x11); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house + WriteByte(0x12); + WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face? + break; + } + case cMonster::mtSkeleton: + { + WriteByte(0xD); + WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not + break; + } + case cMonster::mtWitch: + { + WriteByte(0x15); + WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything + break; + } + case cMonster::mtSlime: + case cMonster::mtMagmaCube: + { + WriteByte(0x10); + if (a_Mob.GetMobType() == cMonster::mtSlime) + { + WriteByte(((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME + } + else + { + WriteByte(((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME + } + break; + } + case cMonster::mtHorse: + { + int Flags = 0; + if (((const cHorse &)a_Mob).IsTame()) + { + Flags |= 0x2; + } + if (((const cHorse &)a_Mob).IsSaddled()) + { + Flags |= 0x4; + } + if (((const cHorse &)a_Mob).IsChested()) + { + Flags |= 0x8; + } + if (((const cHorse &)a_Mob).IsBaby()) + { + Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer + } + if (((const cHorse &)a_Mob).IsEating()) + { + Flags |= 0x20; + } + if (((const cHorse &)a_Mob).IsRearing()) + { + Flags |= 0x40; + } + if (((const cHorse &)a_Mob).IsMthOpen()) + { + Flags |= 0x80; + } + WriteByte(0x50); + WriteInt(Flags); + + WriteByte(0x13); + WriteByte(((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.) + + WriteByte(0x54); + int Appearance = 0; + Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF + Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256 + WriteInt(Appearance); + + WriteByte(0x56); + WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour + break; + } + } +} + + + + diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h new file mode 100644 index 000000000..5a9218f5b --- /dev/null +++ b/src/Protocol/Protocol125.h @@ -0,0 +1,159 @@ + +// Protocol125.h + +// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29) + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +class cProtocol125 : + public cProtocol +{ + typedef cProtocol super; +public: + cProtocol125(cClientHandle * a_Client); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + + virtual AString GetAuthServerID(void) override; + +protected: + /// Results of packet-parsing: + enum { + PARSE_OK = 1, + PARSE_ERROR = -1, + PARSE_UNKNOWN = -2, + PARSE_INCOMPLETE = -3, + } ; + + cByteBuffer m_ReceivedData; ///< Buffer for the received data + + AString m_Username; ///< Stored in ParseHandshake(), compared to Login username + + virtual void SendData(const char * a_Data, int a_Size) override; + + /// Sends the Handshake packet + void SendHandshake(const AString & a_ConnectionHash); + + /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() ) + virtual int ParsePacket(unsigned char a_PacketType); + + // Specific packet parsers: + virtual int ParseArmAnim (void); + virtual int ParseBlockDig (void); + virtual int ParseBlockPlace (void); + virtual int ParseChat (void); + virtual int ParseCreativeInventoryAction(void); + virtual int ParseDisconnect (void); + virtual int ParseEntityAction (void); + virtual int ParseHandshake (void); + virtual int ParseKeepAlive (void); + virtual int ParseLogin (void); + virtual int ParsePing (void); + virtual int ParsePlayerAbilities (void); + virtual int ParsePlayerLook (void); + virtual int ParsePlayerMoveLook (void); + virtual int ParsePlayerOnGround (void); + virtual int ParsePlayerPosition (void); + virtual int ParsePluginMessage (void); + virtual int ParseRespawn (void); + virtual int ParseSlotSelected (void); + virtual int ParseUpdateSign (void); + virtual int ParseUseEntity (void); + virtual int ParseWindowClick (void); + virtual int ParseWindowClose (void); + + // Utility functions: + /// Writes a "pre-chunk" packet + void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad); + + /// Writes a "set window items" packet with the specified params + void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items); + + /// Writes one item, "slot" as the protocol wiki calls it + virtual void WriteItem(const cItem & a_Item); + + /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes + virtual int ParseItem(cItem & a_Item); + + /// Writes the COMMON entity metadata + void WriteCommonMetadata(const cEntity & a_Entity); + + /// Writes normal entity metadata + void WriteEntityMetadata(const cEntity & a_Entity); + + /// Writes mobile entity metadata + void WriteMobMetadata(const cMonster & a_Mob); +} ; + + + + diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp new file mode 100644 index 000000000..8f7891e4b --- /dev/null +++ b/src/Protocol/Protocol132.cpp @@ -0,0 +1,950 @@ + +// Protocol132.cpp + +// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "lib/cryptopp/randpool.h" +#include "Protocol132.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../ClientHandle.h" +#include "../Item.h" +#include "../Entities/Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Entities/Pickup.h" +#include "../WorldStorage/FastNBT.h" +#include "../StringCompression.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +typedef unsigned char Byte; + + + + + +using namespace CryptoPP; + + + + + +const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_COMPASS = 0x06, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_CHUNK_DATA = 0x33, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_BLOCK_BREAK_ANIM = 0x37, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_TAB_COMPLETION = 0xcb, + PACKET_LOCALE_VIEW_DISTANCE = 0xcc, + PACKET_CLIENT_STATUSES = 0xcd, + PACKET_ENCRYPTION_KEY_RESP = 0xfc, +} ; + + + + + +// Converts a raw 160-bit SHA1 digest into a Java Hex representation +// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 +static void DigestToJava(byte a_Digest[20], AString & a_Out) +{ + bool IsNegative = (a_Digest[0] >= 0x80); + if (IsNegative) + { + // Two's complement: + bool carry = true; // Add one to the whole number + for (int i = 19; i >= 0; i--) + { + a_Digest[i] = ~a_Digest[i]; + if (carry) + { + carry = (a_Digest[i] == 0xff); + a_Digest[i]++; + } + } + } + a_Out.clear(); + a_Out.reserve(40); + for (int i = 0; i < 20; i++) + { + AppendPrintf(a_Out, "%02x", a_Digest[i]); + } + while ((a_Out.length() > 0) && (a_Out[0] == '0')) + { + a_Out.erase(0, 1); + } + if (IsNegative) + { + a_Out.insert(0, "-"); + } +} + + + + + +/* +// Self-test the hash formatting for known values: +// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 +// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 +// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 + +class Test +{ +public: + Test(void) + { + AString DigestNotch, DigestJeb, DigestSimon; + byte Digest[20]; + CryptoPP::SHA1 Checksum; + Checksum.Update((const byte *)"Notch", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestNotch); + Checksum.Restart(); + Checksum.Update((const byte *)"jeb_", 4); + Checksum.Final(Digest); + DigestToJava(Digest, DigestJeb); + Checksum.Restart(); + Checksum.Update((const byte *)"simon", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestSimon); + printf("Notch: \"%s\"", DigestNotch.c_str()); + printf("jeb_: \"%s\"", DigestJeb.c_str()); + printf("simon: \"%s\"", DigestSimon.c_str()); + } +} test; +*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol132: + +cProtocol132::cProtocol132(cClientHandle * a_Client) : + super(a_Client), + m_IsEncrypted(false) +{ +} + + + + + +cProtocol132::~cProtocol132() +{ + if (!m_DataToSend.empty()) + { + LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size()); + } +} + + + + + +void cProtocol132::DataReceived(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Decrypted[512]; + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + super::DataReceived((const char *)Decrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + WriteShort(a_BlockType); + Flush(); +} + + + + + +void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_BREAK_ANIM); + WriteInt (a_entityID); + WriteInt (a_BlockX); + WriteInt (a_BlockY); + WriteInt (a_BlockZ); + WriteByte (stage); + Flush(); +} + + + + + +void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte ((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteShort(a_BlockType); + WriteByte (a_BlockMeta); + Flush(); +} + + + + + +void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Pre-chunk not used in 1.3.2. Finally. + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); + + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + SendSoundEffect( + "random.pop", + (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), + 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) + ); +} + + + + + +void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) +{ + if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID()) + { + // Do not send "destroy self" to the client, the client would crash (FS #254) + return; + } + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITIES); + WriteByte(1); // entity count + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString("default"); // Level type + WriteByte ((int)a_Player.GetGameMode()); + WriteByte ((Byte)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused, used to be world height + WriteByte (8); // Client list width or something + Flush(); + + SendCompass(a_World); +} + + + + + +void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: + WriteByte (0); // Index 0, byte (flags) + WriteByte (0); // Flags, empty + WriteByte (0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SOUND_EFFECT); + WriteString (a_SoundName); + WriteInt (a_SrcX); + WriteInt (a_SrcY); + WriteInt (a_SrcZ); + WriteFloat (a_Volume); + WriteByte ((char)(a_Pitch * 63.0f)); + Flush(); +} + + + + + +void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX); + WriteByte(a_SrcY); + WriteInt (a_SrcZ); + WriteInt (a_Data); + Flush(); +} + + + + + +void cProtocol132::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); + WriteShort ((short)(a_Mob.GetSpeedX() * 400)); + WriteShort ((short)(a_Mob.GetSpeedY() * 400)); + WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); + + WriteCommonMetadata(a_Mob); + WriteMobMetadata(a_Mob); + WriteByte(0x7f); + + Flush(); +} + + + + + +void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results) +{ + if (a_Results.empty()) + { + // No results to send + return; + } + + AString Serialized(a_Results[0]); + for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr) + { + Serialized.push_back(0); + Serialized.append(*itr); + } // for itr - a_Results[] + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_TAB_COMPLETION); + WriteString(Serialized); + Flush(); +} + + + + + +void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + // Unloading the chunk is done by sending a "map chunk" packet + // with IncludeInitialize set to true and primary bitmap set to 0: + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(true); // IncludeInitialize + WriteShort(0); // Primary bitmap + WriteShort(0); // Add bitmap + WriteInt(0); + Flush(); +} + + + + + +void cProtocol132::SendWholeInventory(const cWindow & a_Window) +{ + // 1.3.2 requires player inventory slots to be sent as SetSlot packets, + // otherwise it sometimes fails to update the window + + // Send the entire window: + super::SendWholeInventory(a_Window); + + // Send the player inventory and hotbar: + const cInventory & Inventory = m_Client->GetPlayer()->GetInventory(); + int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots + char WindowID = a_Window.GetWindowID(); + for (int i = 0; i < cInventory::invInventoryCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i)); + } // for i - Inventory[] + BaseOffset += cInventory::invInventoryCount; + for (int i = 0; i < cInventory::invHotbarCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i)); + } // for i - Hotbar[] + + // Send even the item being dragged: + SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem()); +} + + + + + +AString cProtocol132::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2615 + // Server uses SHA1 to mix ServerID, Client secret and server public key together + // The mixing is done in StartEncryption, the result is in m_AuthServerID + + return m_AuthServerID; +} + + + + + +int cProtocol132::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125 + case PACKET_CLIENT_STATUSES: return ParseClientStatuses(); + case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse(); + case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance(); + case PACKET_TAB_COMPLETION: return ParseTabCompletion(); + } +} + + + + + +int cProtocol132::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + HANDLE_PACKET_READ(ReadChar, char, CursorX); + HANDLE_PACKET_READ(ReadChar, char, CursorY); + HANDLE_PACKET_READ(ReadChar, char, CursorZ); + + m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); + HANDLE_PACKET_READ(ReadBEInt, int, ServerPort); + m_Username = Username; + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD + CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs + cRoot::Get()->GetServer()->GetPublicKey().Save(sink); + SendEncryptionKeyRequest(); + + return PARSE_OK; +} + + + + + +int cProtocol132::ParseClientStatuses(void) +{ + HANDLE_PACKET_READ(ReadByte, byte, Status); + if ((Status & 1) == 0) + { + m_Client->HandleLogin(39, m_Username); + } + else + { + m_Client->HandleRespawn(); + } + return PARSE_OK; +} + + + + + +int cProtocol132::ParseEncryptionKeyResponse(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength); + AString EncKey; + if (!m_ReceivedData.ReadString(EncKey, EncKeyLength)) + { + return PARSE_INCOMPLETE; + } + HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength); + AString EncNonce; + if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength)) + { + return PARSE_INCOMPLETE; + } + if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + { + LOGD("Too long encryption"); + m_Client->Kick("Hacked client"); + return PARSE_OK; + } + + HandleEncryptionKeyResponse(EncKey, EncNonce); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLogin(void) +{ + // Login packet not used in 1.3.2 + return PARSE_ERROR; +} + + + + + +int cProtocol132::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Flags); + HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed); + HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseTabCompletion(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text); + m_Client->HandleTabCompletion(Text); + return PARSE_OK; +} + + + + + +void cProtocol132::SendData(const char * a_Data, int a_Size) +{ + m_DataToSend.append(a_Data, a_Size); +} + + + + + +void cProtocol132::Flush(void) +{ + ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly? + + if (m_DataToSend.empty()) + { + LOGD("Flushing empty"); + return; + } + const char * a_Data = m_DataToSend.data(); + int a_Size = m_DataToSend.size(); + if (m_IsEncrypted) + { + byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + super::SendData((const char *)Encrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::SendData(a_Data, a_Size); + } + m_DataToSend.clear(); +} + + + + + +void cProtocol132::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + WriteShort(-1); + return; + } + + WriteShort(ItemType); + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (a_Item.m_Enchantments.IsEmpty()) + { + WriteShort(-1); + return; + } + + // Send the enchantments: + cFastNBTWriter Writer; + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + Writer.Finish(); + AString Compressed; + CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + WriteShort(Compressed.size()); + SendData(Compressed.data(), Compressed.size()); +} + + + + + +int cProtocol132::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); + if (MetadataLength <= 0) + { + return PARSE_OK; + } + + // Read the metadata + AString Metadata; + Metadata.resize(MetadataLength); + if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength)) + { + return PARSE_INCOMPLETE; + } + + return ParseItemMetadata(a_Item, Metadata); +} + + + + + +int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +{ + // Uncompress the GZIPped data: + AString Uncompressed; + if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) + { + AString HexDump; + CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Parse into NBT: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + AString HexDump; + CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); + LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Load enchantments from the NBT: + for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) + { + if ( + (NBT.GetType(tag) == TAG_List) && + ( + (NBT.GetName(tag) == "ench") || + (NBT.GetName(tag) == "StoredEnchantments") + ) + ) + { + a_Item.m_Enchantments.ParseFromNBT(NBT, tag); + } + } + + return PARSE_OK; +} + + + + + +void cProtocol132::SendCompass(const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COMPASS); + WriteInt((int)(a_World.GetSpawnX())); + WriteInt((int)(a_World.GetSpawnY())); + WriteInt((int)(a_World.GetSpawnZ())); + Flush(); +} + + + + + +void cProtocol132::SendEncryptionKeyRequest(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfd); + WriteString(cRoot::Get()->GetServer()->GetServerID()); + WriteShort((short)m_ServerPublicKey.size()); + SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size()); + WriteShort(4); + WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + Flush(); +} + + + + + +void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) +{ + // Decrypt EncNonce using privkey + RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey()); + time_t CurTime = time(NULL); + CryptoPP::RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + byte DecryptedNonce[MAX_ENC_LEN]; + DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); + if (!res.isValidCoding || (res.messageLength != 4)) + { + LOGD("Bad nonce length"); + m_Client->Kick("Hacked client"); + return; + } + if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this) + { + LOGD("Bad nonce value"); + m_Client->Kick("Hacked client"); + return; + } + + // Decrypt the symmetric encryption key using privkey: + byte DecryptedKey[MAX_ENC_LEN]; + res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey); + if (!res.isValidCoding || (res.messageLength != 16)) + { + LOGD("Bad key length"); + m_Client->Kick("Hacked client"); + return; + } + + { + // Send encryption key response: + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfc); + WriteShort(0); + WriteShort(0); + Flush(); + } + + StartEncryption(DecryptedKey); + return; +} + + + + + +void cProtocol132::StartEncryption(const byte * a_Key) +{ + m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_IsEncrypted = true; + + // Prepare the m_AuthServerID: + CryptoPP::SHA1 Checksum; + AString ServerID = cRoot::Get()->GetServer()->GetServerID(); + Checksum.Update((const byte *)ServerID.c_str(), ServerID.length()); + Checksum.Update(a_Key, 16); + Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length()); + byte Digest[20]; + Checksum.Final(Digest); + DigestToJava(Digest, m_AuthServerID); +} + + + + diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h new file mode 100644 index 000000000..2023d7960 --- /dev/null +++ b/src/Protocol/Protocol132.h @@ -0,0 +1,102 @@ + +// Protocol132.h + +// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) + + + + + +#pragma once + +#include "Protocol125.h" +#include "lib/cryptopp/modes.h" +#include "lib/cryptopp/aes.h" + + + + + +class cProtocol132 : + public cProtocol125 +{ + typedef cProtocol125 super; +public: + + cProtocol132(cClientHandle * a_Client); + virtual ~cProtocol132(); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + // Sending commands (alphabetically sorted): + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + + virtual AString GetAuthServerID(void) override; + + /// Handling of the additional packets: + virtual int ParsePacket(unsigned char a_PacketType) override; + + // Modified packets: + virtual int ParseBlockPlace (void) override; + virtual int ParseHandshake (void) override; + virtual int ParseLogin (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseClientStatuses (void); + virtual int ParseEncryptionKeyResponse(void); + virtual int ParseLocaleViewDistance (void); + virtual int ParseTabCompletion (void); + +protected: + bool m_IsEncrypted; + CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor; + CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor; + AString m_DataToSend; + + /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() + AString m_AuthServerID; + + /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() + AString m_ServerPublicKey; + + virtual void SendData(const char * a_Data, int a_Size) override; + + // DEBUG: + virtual void Flush(void) override; + + // Items in slots are sent differently + virtual void WriteItem(const cItem & a_Item) override; + virtual int ParseItem(cItem & a_Item) override; + + /// Parses the metadata that may come with the item. + int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); + + virtual void SendCompass(const cWorld & a_World); + virtual void SendEncryptionKeyRequest(void); + + /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption + void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); + + /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID + void StartEncryption(const byte * a_Key); +} ; + + + + diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp new file mode 100644 index 000000000..ea406d7a2 --- /dev/null +++ b/src/Protocol/Protocol14x.cpp @@ -0,0 +1,256 @@ + +// Protocol14x.cpp + +/* +Implements the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + +#include "Globals.h" +#include "Protocol14x.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "lib/cryptopp/randpool.h" +#include "../Item.h" +#include "ChunkDataSerializer.h" +#include "../Entities/Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Entities/Pickup.h" +#include "../Entities/FallingBlock.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_UPDATE_TIME = 0x04, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_ENTITY_METADATA = 0x28, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol142: + +cProtocol142::cProtocol142(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +int cProtocol142::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteItem (a_Pickup.GetItem()); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX); + WriteByte(a_SrcY); + WriteInt (a_SrcZ); + WriteInt (a_Data); + WriteBool(0); + Flush(); +} + + + + + +void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + WriteInt64(a_WorldAge); + WriteInt64(a_TimeOfDay); + Flush(); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol146: + +cProtocol146::cProtocol146(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(!a_Pickup.GetItem().IsEmpty()); + + cCSLock Lock(m_CSPacket); + + // Send a SPAWN_OBJECT packet for the base entity: + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Pickup.GetUniqueID()); + WriteByte(0x02); + WriteInt ((int)(a_Pickup.GetPosX() * 32)); + WriteInt ((int)(a_Pickup.GetPosY() * 32)); + WriteInt ((int)(a_Pickup.GetPosZ() * 32)); + WriteInt (1); + WriteShort((short)(a_Pickup.GetSpeed().x * 32)); + WriteShort((short)(a_Pickup.GetSpeed().y * 32)); + WriteShort((short)(a_Pickup.GetSpeed().z * 32)); + WriteByte(0); + WriteByte(0); + + // Send a ENTITY_METADATA packet with the slot info: + WriteByte(PACKET_ENTITY_METADATA); + WriteInt(a_Pickup.GetUniqueID()); + WriteByte(0xaa); // a slot value at index 10 + WriteItem(a_Pickup.GetItem()); + WriteByte(0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // Send a spawn object / vehicle packet + cCSLock Lock(m_CSPacket); + + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_FallingBlock.GetUniqueID()); + WriteByte(70); + WriteInt ((int)(a_FallingBlock.GetPosX() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosY() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosZ() * 32)); + WriteByte (0); // Pitch + WriteByte (0); // Yaw + WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype + WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte (a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (a_VehicleSubType); + if (a_VehicleSubType != 0) + { + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + diff --git a/src/Protocol/Protocol14x.h b/src/Protocol/Protocol14x.h new file mode 100644 index 000000000..ca497bbc1 --- /dev/null +++ b/src/Protocol/Protocol14x.h @@ -0,0 +1,63 @@ + +// Protocol14x.h + +/* +Interfaces to the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + + + + + +#pragma once + +#include "Protocol132.h" + + + + + +class cProtocol142 : + public cProtocol132 +{ + typedef cProtocol132 super; + +public: + cProtocol142(cClientHandle * a_Client); + + // Sending commands (alphabetically sorted): + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + + // Specific packet parsers: + virtual int ParseLocaleViewDistance(void) override; +} ; + + + + + +class cProtocol146 : + public cProtocol142 +{ + typedef cProtocol142 super; + +public: + cProtocol146(cClientHandle * a_Client); + + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; +} ; + + + + diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp new file mode 100644 index 000000000..c337d26e7 --- /dev/null +++ b/src/Protocol/Protocol15x.cpp @@ -0,0 +1,138 @@ + +// Protocol15x.cpp + +/* +Implements the 1.5.x protocol classes: + - cProtocol150 + - release 1.5 protocol (#60) + - release 1.5.2 protocol (#61, no relevant changes found) +*/ + +#include "Globals.h" +#include "Protocol15x.h" +#include "../ClientHandle.h" +#include "../Item.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_WINDOW_OPEN = 0x64, +} ; + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol150: + +cProtocol150::cProtocol150(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol150::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_Window.GetWindowID()); + WriteByte (a_Window.GetWindowType()); + WriteString(a_Window.GetWindowTitle()); + WriteByte (a_Window.GetNumNonInventorySlots()); + WriteByte (1); // Use title + Flush(); +} + + + + + +int cProtocol150::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadByte, Byte, Button); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadByte, Byte, Mode); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // Convert Button, Mode, SlotNum and HeldItem into eClickAction: + eClickAction Action; + switch ((Mode << 8) | Button) + { + case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; + case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; + case 0x0100: Action = caShiftLeftClick; break; + case 0x0101: Action = caShiftRightClick; break; + case 0x0200: Action = caNumber1; break; + case 0x0201: Action = caNumber2; break; + case 0x0202: Action = caNumber3; break; + case 0x0203: Action = caNumber4; break; + case 0x0204: Action = caNumber5; break; + case 0x0205: Action = caNumber6; break; + case 0x0206: Action = caNumber7; break; + case 0x0207: Action = caNumber8; break; + case 0x0208: Action = caNumber9; break; + case 0x0300: Action = caMiddleClick; break; + case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; + case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; + case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; + case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; + case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; + case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; + case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; + case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; + case 0x0600: Action = caDblClick; break; + } + + if (Action == caUnknown) + { + LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.", + Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str() + ); + ASSERT(!"Unknown click action"); + return PARSE_OK; + } + + m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); + return PARSE_OK; +} + + + + + diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h new file mode 100644 index 000000000..e554fe130 --- /dev/null +++ b/src/Protocol/Protocol15x.h @@ -0,0 +1,38 @@ + +// Protocol15x.h + +/* +Declares the 1.5.x protocol classes: + - cProtocol150 + - release 1.5 and 1.5.1 protocol (#60) + - release 1.5.2 protocol (#61; no relevant changes found) +*/ + + + + + +#pragma once + +#include "Protocol14x.h" + + + + + +class cProtocol150 : + public cProtocol146 +{ + typedef cProtocol146 super; + +public: + cProtocol150(cClientHandle * a_Client); + + virtual void SendWindowOpen(const cWindow & a_Window) override; + + virtual int ParseWindowClick(void); +} ; + + + + diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp new file mode 100644 index 000000000..cfa27b3c4 --- /dev/null +++ b/src/Protocol/Protocol16x.cpp @@ -0,0 +1,268 @@ + +// Protocol16x.cpp + +/* +Implements the 1.6.x protocol classes: + - cProtocol161 + - 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) +*/ + +#include "Globals.h" +#include "Protocol16x.h" +#include "../ClientHandle.h" +#include "../Entities/Entity.h" +#include "../Entities/Player.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_CHAT = 0x03, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_STEER_VEHICLE = 0x1b, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_ENTITY_PROPERTIES = 0x2c, + PACKET_WINDOW_OPEN = 0x64, + PACKET_TILE_EDITOR_OPEN = 0x85, + PACKET_PLAYER_ABILITIES = 0xca, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol161: + +cProtocol161::cProtocol161(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ATTACH_ENTITY); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); + WriteBool(false); // TODO: "Should use leash?" -> no + Flush(); +} + + + + + +void cProtocol161::SendChat(const AString & a_Message) +{ + super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); +} + + + + + +void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_TILE_EDITOR_OPEN); + WriteByte(0); + WriteInt(a_BlockX); + WriteInt(a_BlockY); + WriteInt(a_BlockZ); + Flush(); +} + + + + + +void cProtocol161::SendGameMode(eGameMode a_GameMode) +{ + super::SendGameMode(a_GameMode); + SendPlayerMaxSpeed(); +} + + + + + +void cProtocol161::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteFloat((float)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol161::SendPlayerMaxSpeed(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_PROPERTIES); + WriteInt(m_Client->GetPlayer()->GetUniqueID()); + WriteInt(1); + WriteString("generic.movementSpeed"); + WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); + Flush(); +} + + + + + +void cProtocol161::SendRespawn(void) +{ + // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast + super::SendRespawn(); + SendPlayerMaxSpeed(); +} + + + + + +void cProtocol161::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_Window.GetWindowID()); + WriteByte (a_Window.GetWindowType()); + WriteString(a_Window.GetWindowTitle()); + WriteByte (a_Window.GetNumNonInventorySlots()); + WriteByte (1); // Use title + if (a_Window.GetWindowType() == cWindow::wtAnimalChest) + { + WriteInt(0); // TODO: The animal's EntityID + } + Flush(); +} + + + + + +int cProtocol161::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); + m_Client->HandleEntityAction(EntityID, ActionID); + return PARSE_OK; +} + + + + + +int cProtocol161::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, Flags); + HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol161::ParseSteerVehicle(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Sideways); + HANDLE_PACKET_READ(ReadBEFloat, float, Forward); + HANDLE_PACKET_READ(ReadBool, bool, Jump); + HANDLE_PACKET_READ(ReadBool, bool, Unmount); + if (Unmount) + { + m_Client->HandleUnmount(); + } + else + { + m_Client->HandleSteerVehicle(Forward, Sideways); + } + return PARSE_OK; +} + + + + + +int cProtocol161::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + case PACKET_STEER_VEHICLE: return ParseSteerVehicle(); + default: return super::ParsePacket(a_PacketType); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol162: + +cProtocol162::cProtocol162(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol162::SendPlayerMaxSpeed(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_PROPERTIES); + WriteInt(m_Client->GetPlayer()->GetUniqueID()); + WriteInt(1); + WriteString("generic.movementSpeed"); + WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); + WriteShort(0); + Flush(); +} + + + + diff --git a/src/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h new file mode 100644 index 000000000..325e41c5a --- /dev/null +++ b/src/Protocol/Protocol16x.h @@ -0,0 +1,76 @@ + +// Protocol16x.h + +/* +Declares the 1.6.x protocol classes: + - cProtocol161 + - 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) +*/ + + + + + +#pragma once + +#include "Protocol15x.h" + + + + + +class cProtocol161 : + public cProtocol150 +{ + typedef cProtocol150 super; + +public: + cProtocol161(cClientHandle * a_Client); + +protected: + + // cProtocol150 overrides: + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendPlayerMaxSpeed(void) override; + virtual void SendRespawn (void) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + + virtual int ParseEntityAction (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseSteerVehicle(void); + + // Enable new packets' handling + virtual int ParsePacket(unsigned char a_PacketType) override; +} ; + + + + + +class cProtocol162 : + public cProtocol161 +{ + typedef cProtocol161 super; + +public: + cProtocol162(cClientHandle * a_Client); + +protected: + // cProtocol161 overrides: + virtual void SendPlayerMaxSpeed(void) override; +} ; + + + + diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp new file mode 100644 index 000000000..746e1c127 --- /dev/null +++ b/src/Protocol/Protocol17x.cpp @@ -0,0 +1,1944 @@ + +// Protocol17x.cpp + +/* +Implements the 1.7.x protocol classes: + - cProtocol172 + - release 1.7.2 protocol (#4) +(others may be added later in the future for the 1.7 release series) +*/ + +#include "Globals.h" +#include "Protocol17x.h" +#include "ChunkDataSerializer.h" +#include "../ClientHandle.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../WorldStorage/FastNBT.h" +#include "../StringCompression.h" +#include "../Entities/ExpOrb.h" +#include "../Entities/Minecart.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Pickup.h" +#include "../Entities/Player.h" +#include "../Mobs/IncludeAllMonsters.h" +#include "../UI/Window.h" + + + + + +#define HANDLE_READ(Proc, Type, Var) \ + Type Var; \ + m_ReceivedData.Proc(Var); + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return false; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) : + super(a_Client), + m_ServerAddress(a_ServerAddress), + m_ServerPort(a_ServerPort), + m_State(a_State), + m_ReceivedData(32 KiB), + m_OutPacketBuffer(64 KiB), + m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt + m_IsEncrypted(false) +{ +} + + + + + +void cProtocol172::DataReceived(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Decrypted[512]; + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + AddReceivedData((const char *)Decrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + AddReceivedData(a_Data, a_Size); + } +} + + + + + +void cProtocol172::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cPacketizer Pkt(*this, 0x1b); // Attach Entity packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteInt((a_Vehicle != NULL) ? a_Vehicle->GetUniqueID() : 0); + Pkt.WriteBool(false); +} + + + + + +void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + cPacketizer Pkt(*this, 0x24); // Block Action packet + Pkt.WriteInt(a_BlockX); + Pkt.WriteShort(a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteByte(a_Byte1); + Pkt.WriteByte(a_Byte2); + Pkt.WriteVarInt(a_BlockType); +} + + + + + +void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) +{ + cPacketizer Pkt(*this, 0x24); // Block Break Animation packet + Pkt.WriteInt(a_EntityID); + Pkt.WriteInt(a_BlockX); + Pkt.WriteInt(a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteChar(a_Stage); +} + + + + + +void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cPacketizer Pkt(*this, 0x23); // Block Change packet + Pkt.WriteInt(a_BlockX); + Pkt.WriteByte(a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteVarInt(a_BlockType); + Pkt.WriteByte(a_BlockMeta); +} + + + + + +void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + cPacketizer Pkt(*this, 0x22); // Multi Block Change packet + Pkt.WriteInt(a_ChunkX); + Pkt.WriteInt(a_ChunkZ); + Pkt.WriteShort((short)a_Changes.size()); + Pkt.WriteInt(a_Changes.size() * 4); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); + unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + Pkt.WriteInt((Coords << 16) | Blocks); + } // for itr - a_Changes[] +} + + + + + +void cProtocol172::SendChat(const AString & a_Message) +{ + cPacketizer Pkt(*this, 0x02); // Chat Message packet + Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); +} + + + + + +void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + // Serialize first, before creating the Packetizer (the packetizer locks a CS) + // This contains the flags and bitmasks, too + const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + + cPacketizer Pkt(*this, 0x21); // Chunk Data packet + Pkt.WriteInt(a_ChunkX); + Pkt.WriteInt(a_ChunkZ); + Pkt.WriteBuf(ChunkData.data(), ChunkData.size()); +} + + + + + +void cProtocol172::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cPacketizer Pkt(*this, 0x0d); // Collect Item packet + Pkt.WriteInt(a_Pickup.GetUniqueID()); + Pkt.WriteInt(a_Player.GetUniqueID()); +} + + + + + +void cProtocol172::SendDestroyEntity(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x13); // Destroy Entities packet + Pkt.WriteByte(1); + Pkt.WriteInt(a_Entity.GetUniqueID()); +} + + + + + +void cProtocol172::SendDisconnect(const AString & a_Reason) +{ + cPacketizer Pkt(*this, 0x40); + Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str())); +} + + + + + +void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPacketizer Pkt(*this, 0x36); // Sign Editor Open packet + Pkt.WriteInt(a_BlockX); + Pkt.WriteInt(a_BlockY); + Pkt.WriteInt(a_BlockZ); +} + + + + + +void cProtocol172::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cPacketizer Pkt(*this, 0x04); // Entity Equipment packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteShort(a_SlotNum); + Pkt.WriteItem(a_Item); +} + + + + + +void cProtocol172::SendEntityHeadLook(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x19); // Entity Head Look packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByteAngle(a_Entity.GetHeadYaw()); +} + + + + + +void cProtocol172::SendEntityLook(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x16); // Entity Look packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); +} + + + + + +void cProtocol172::SendEntityMetadata(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteEntityMetadata(a_Entity); + Pkt.WriteByte(0x7f); // The termination byte +} + + + + + +void cProtocol172::SendEntityProperties(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x20); // Entity Properties packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteEntityProperties(a_Entity); +} + + + + + +void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByte(a_RelX); + Pkt.WriteByte(a_RelY); + Pkt.WriteByte(a_RelZ); +} + + + + + +void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteByte(a_RelX); + Pkt.WriteByte(a_RelY); + Pkt.WriteByte(a_RelZ); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); +} + + + + + +void cProtocol172::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + cPacketizer Pkt(*this, 0x1a); // Entity Status packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteChar(a_Status); +} + + + + + +void cProtocol172::SendEntityVelocity(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x12); // Entity Velocity packet + Pkt.WriteInt(a_Entity.GetUniqueID()); + // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick + Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); +} + + + + + +void cProtocol172::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + cPacketizer Pkt(*this, 0x27); // Explosion packet + Pkt.WriteFloat((float)a_BlockX); + Pkt.WriteFloat((float)a_BlockY); + Pkt.WriteFloat((float)a_BlockZ); + Pkt.WriteFloat((float)a_Radius); + Pkt.WriteInt(a_BlocksAffected.size()); + for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr) + { + Pkt.WriteChar((char)itr->x); + Pkt.WriteChar((char)itr->y); + Pkt.WriteChar((char)itr->z); + } // for itr - a_BlockAffected[] + Pkt.WriteFloat((float)a_PlayerMotion.x); + Pkt.WriteFloat((float)a_PlayerMotion.y); + Pkt.WriteFloat((float)a_PlayerMotion.z); +} + + + + + +void cProtocol172::SendGameMode(eGameMode a_GameMode) +{ + cPacketizer Pkt(*this, 0x2b); // Change Game State packet + Pkt.WriteByte(3); // Reason: Change game mode + Pkt.WriteFloat((float)a_GameMode); +} + + + + + +void cProtocol172::SendHealth(void) +{ + cPacketizer Pkt(*this, 0x06); // Update Health packet + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetHealth()); + Pkt.WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); +} + + + + + +void cProtocol172::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + cPacketizer Pkt(*this, 0x2f); // Set Slot packet + Pkt.WriteChar(a_WindowID); + Pkt.WriteShort(a_SlotNum); + Pkt.WriteItem(a_Item); +} + + + + + +void cProtocol172::SendKeepAlive(int a_PingID) +{ + cPacketizer Pkt(*this, 0x00); // Keep Alive packet + Pkt.WriteInt(a_PingID); +} + + + + + +void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + // Send the Join Game packet: + { + cPacketizer Pkt(*this, 0x01); // Join Game packet + Pkt.WriteInt(a_Player.GetUniqueID()); + Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 + Pkt.WriteChar((char)a_World.GetDimension()); + Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) + Pkt.WriteByte(cRoot::Get()->GetServer()->GetMaxPlayers()); + Pkt.WriteString("default"); // Level type - wtf? + } + + // Send the spawn position: + { + cPacketizer Pkt(*this, 0x05); // Spawn Position packet + Pkt.WriteInt((int)a_World.GetSpawnX()); + Pkt.WriteInt((int)a_World.GetSpawnY()); + Pkt.WriteInt((int)a_World.GetSpawnZ()); + } + + // Send player abilities: + SendPlayerAbilities(); +} + + + + + +void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup) +{ + { + cPacketizer Pkt(*this, 0x0e); // Spawn Object packet + Pkt.WriteVarInt(a_Pickup.GetUniqueID()); + Pkt.WriteByte(2); // Type = Pickup + Pkt.WriteFPInt(a_Pickup.GetPosX()); + Pkt.WriteFPInt(a_Pickup.GetPosY()); + Pkt.WriteFPInt(a_Pickup.GetPosZ()); + Pkt.WriteByteAngle(a_Pickup.GetYaw()); + Pkt.WriteByteAngle(a_Pickup.GetPitch()); + Pkt.WriteInt(0); // No object data + } + { + cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet + Pkt.WriteInt(a_Pickup.GetUniqueID()); + Pkt.WriteByte((0x05 << 5) | 10); // Slot type + index 10 + Pkt.WriteItem(a_Pickup.GetItem()); + Pkt.WriteByte(0x7f); // End of metadata + } +} + + + + + +void cProtocol172::SendPlayerAbilities(void) +{ + cPacketizer Pkt(*this, 0x39); // Player Abilities packet + Byte Flags = 0; + if (m_Client->GetPlayer()->IsGameModeCreative()) + { + Flags |= 0x01; + } + // TODO: Other flags (god mode, flying, can fly + Pkt.WriteByte(Flags); + // TODO: Pkt.WriteFloat(m_Client->GetPlayer()->GetMaxFlyingSpeed()); + Pkt.WriteFloat(0.05f); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetMaxSpeed()); +} + + + + + +void cProtocol172::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + cPacketizer Pkt(*this, 0x0b); // Animation packet + Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteChar(a_Animation); +} + + + + + +void cProtocol172::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + cPacketizer Pkt(*this, 0x38); // Playerlist Item packet + Pkt.WriteString(a_Player.GetName()); + Pkt.WriteBool(a_IsOnline); + Pkt.WriteShort(a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0); +} + + + + + +void cProtocol172::SendPlayerMaxSpeed(void) +{ + cPacketizer Pkt(*this, 0x20); // Entity Properties + Pkt.WriteInt(m_Client->GetPlayer()->GetUniqueID()); + Pkt.WriteInt(1); // Count + Pkt.WriteString("generic.movementSpeed"); + Pkt.WriteDouble(0.1); + if (m_Client->GetPlayer()->IsSprinting()) + { + Pkt.WriteShort(1); // Modifier count + Pkt.WriteInt64(0x662a6b8dda3e4c1c); + Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier + Pkt.WriteDouble(0.3); + Pkt.WriteByte(2); + } + else + { + Pkt.WriteShort(0); // Modifier count + } +} + + + + + +void cProtocol172::SendPlayerMoveLook(void) +{ + cPacketizer Pkt(*this, 0x08); // Player Position And Look packet + Pkt.WriteDouble(m_Client->GetPlayer()->GetPosX()); + Pkt.WriteDouble(m_Client->GetPlayer()->GetPosY()); + Pkt.WriteDouble(m_Client->GetPlayer()->GetPosZ()); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetYaw()); + Pkt.WriteFloat((float)m_Client->GetPlayer()->GetPitch()); + Pkt.WriteBool(m_Client->GetPlayer()->IsOnGround()); +} + + + + + +void cProtocol172::SendPlayerPosition(void) +{ + // There is no dedicated packet for this, send the whole thing: + SendPlayerMoveLook(); +} + + + + + +void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player) +{ + // Called to spawn another player for the client + cPacketizer Pkt(*this, 0x0c); // Spawn Player packet + Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteString(Printf("%d", a_Player.GetUniqueID())); // TODO: Proper UUID + Pkt.WriteString(a_Player.GetName()); + Pkt.WriteFPInt(a_Player.GetPosX()); + Pkt.WriteFPInt(a_Player.GetPosY()); + Pkt.WriteFPInt(a_Player.GetPosZ()); + Pkt.WriteByteAngle(a_Player.GetYaw()); + Pkt.WriteByteAngle(a_Player.GetPitch()); + short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType; + Pkt.WriteShort(ItemType); + Pkt.WriteByte((3 << 5) | 6); // Metadata: float + index 6 + Pkt.WriteFloat((float)a_Player.GetHealth()); + Pkt.WriteByte(0x7f); // Metadata: end +} + + + + + +void cProtocol172::SendRespawn(void) +{ + cPacketizer Pkt(*this, 0x07); // Respawn packet + Pkt.WriteInt(m_Client->GetPlayer()->GetWorld()->GetDimension()); + Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) + Pkt.WriteByte((Byte)m_Client->GetPlayer()->GetEffectiveGameMode()); + Pkt.WriteString("default"); +} + + + + + +void cProtocol172::SendExperience (void) +{ + cPacketizer Pkt(*this, 0x1F); //Experience Packet + Pkt.WriteFloat(m_Client->GetPlayer()->GetXpPercentage()); + Pkt.WriteShort(m_Client->GetPlayer()->GetXpLevel()); + Pkt.WriteShort(m_Client->GetPlayer()->GetCurrentXp()); +} + + + + + +void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb) +{ + cPacketizer Pkt(*this, 0x11); + Pkt.WriteVarInt(a_ExpOrb.GetUniqueID()); + Pkt.WriteInt((int) a_ExpOrb.GetPosX()); + Pkt.WriteInt((int) a_ExpOrb.GetPosY()); + Pkt.WriteInt((int) a_ExpOrb.GetPosZ()); + Pkt.WriteShort(a_ExpOrb.GetReward()); +} + + + + + +void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8 +{ + cPacketizer Pkt(*this, 0x29); // Sound Effect packet + Pkt.WriteString(a_SoundName); + Pkt.WriteInt(a_SrcX); + Pkt.WriteInt(a_SrcY); + Pkt.WriteInt(a_SrcZ); + Pkt.WriteFloat(a_Volume); + Pkt.WriteByte((Byte)(a_Pitch * 63)); +} + + + + + +void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cPacketizer Pkt(*this, 0x28); // Effect packet + Pkt.WriteInt(a_EffectID); + Pkt.WriteInt(a_SrcX); + Pkt.WriteByte(a_SrcY); + Pkt.WriteInt(a_SrcZ); + Pkt.WriteInt(a_Data); + Pkt.WriteBool(false); +} + + + + + +void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + cPacketizer Pkt(*this, 0x0e); // Spawn Object packet + Pkt.WriteVarInt(a_FallingBlock.GetUniqueID()); + Pkt.WriteByte(70); // Falling block + Pkt.WriteFPInt(a_FallingBlock.GetPosX()); + Pkt.WriteFPInt(a_FallingBlock.GetPosY()); + Pkt.WriteFPInt(a_FallingBlock.GetPosZ()); + Pkt.WriteByteAngle(a_FallingBlock.GetYaw()); + Pkt.WriteByteAngle(a_FallingBlock.GetPitch()); + Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 12)); + Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); +} + + + + + +void cProtocol172::SendSpawnMob(const cMonster & a_Mob) +{ + cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet + Pkt.WriteVarInt(a_Mob.GetUniqueID()); + Pkt.WriteByte((Byte)a_Mob.GetMobType()); + Pkt.WriteFPInt(a_Mob.GetPosX()); + Pkt.WriteFPInt(a_Mob.GetPosY()); + Pkt.WriteFPInt(a_Mob.GetPosZ()); + Pkt.WriteByteAngle(a_Mob.GetPitch()); + Pkt.WriteByteAngle(a_Mob.GetHeadYaw()); + Pkt.WriteByteAngle(a_Mob.GetYaw()); + Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400)); + Pkt.WriteEntityMetadata(a_Mob); + Pkt.WriteByte(0x7f); // Metadata terminator +} + + + + + +void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + cPacketizer Pkt(*this, 0xe); // Spawn Object packet + Pkt.WriteVarInt(a_Entity.GetUniqueID()); + Pkt.WriteByte(a_ObjectType); + Pkt.WriteFPInt(a_Entity.GetPosX()); + Pkt.WriteFPInt(a_Entity.GetPosY()); + Pkt.WriteFPInt(a_Entity.GetPosZ()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); + Pkt.WriteInt(a_ObjectData); + if (a_ObjectData != 0) + { + Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } +} + + + + + +void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + cPacketizer Pkt(*this, 0xe); // Spawn Object packet + Pkt.WriteVarInt(a_Vehicle.GetUniqueID()); + Pkt.WriteByte(a_VehicleType); + Pkt.WriteFPInt(a_Vehicle.GetPosX()); + Pkt.WriteFPInt(a_Vehicle.GetPosY()); + Pkt.WriteFPInt(a_Vehicle.GetPosZ()); + Pkt.WriteByteAngle(a_Vehicle.GetYaw()); + Pkt.WriteByteAngle(a_Vehicle.GetPitch()); + Pkt.WriteInt(a_VehicleSubType); + if (a_VehicleSubType != 0) + { + Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + } +} + + + + + +void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results) +{ + AString Results; + Results.reserve(500); // Make a moderate reservation to avoid excessive reallocations + for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr) + { + Results.append(*itr); + Results.push_back(0); + } + + cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet + Pkt.WriteVarInt(a_Results.size()); + Pkt.WriteString(Results); +} + + + + + +void cProtocol172::SendTeleportEntity(const cEntity & a_Entity) +{ + cPacketizer Pkt(*this, 0x18); + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteFPInt(a_Entity.GetPosX()); + Pkt.WriteFPInt(a_Entity.GetPosY()); + Pkt.WriteFPInt(a_Entity.GetPosZ()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); + Pkt.WriteByteAngle(a_Entity.GetPitch()); +} + + + + + +void cProtocol172::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPacketizer Pkt(*this, 0x2c); // Spawn Global Entity packet + Pkt.WriteVarInt(0); // EntityID = 0, always + Pkt.WriteByte(1); // Type = Thunderbolt + Pkt.WriteFPInt(a_BlockX); + Pkt.WriteFPInt(a_BlockY); + Pkt.WriteFPInt(a_BlockZ); +} + + + + + +void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cPacketizer Pkt(*this, 0x03); + Pkt.WriteInt64(a_WorldAge); + Pkt.WriteInt64(a_TimeOfDay); +} + + + + + +void cProtocol172::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + cPacketizer Pkt(*this, 0x21); // Chunk Data packet + Pkt.WriteInt(a_ChunkX); + Pkt.WriteInt(a_ChunkZ); + Pkt.WriteBool(true); + Pkt.WriteShort(0); // Primary bitmap + Pkt.WriteShort(0); // Add bitmap + Pkt.WriteInt(0); // Compressed data size +} + + + + + +void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + cPacketizer Pkt(*this, 0x33); + Pkt.WriteInt(a_BlockX); + Pkt.WriteShort((short)a_BlockY); + Pkt.WriteInt(a_BlockZ); + Pkt.WriteString(a_Line1); + Pkt.WriteString(a_Line2); + Pkt.WriteString(a_Line3); + Pkt.WriteString(a_Line4); +} + + + + + +void cProtocol172::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPacketizer Pkt(*this, 0x0a); + Pkt.WriteInt(a_Entity.GetUniqueID()); + Pkt.WriteInt(a_BlockX); + Pkt.WriteByte((Byte)a_BlockY); + Pkt.WriteInt(a_BlockZ); +} + + + + + +void cProtocol172::SendWeather(eWeather a_Weather) +{ + { + cPacketizer Pkt(*this, 0x2b); // Change Game State packet + Pkt.WriteByte((a_Weather == wSunny) ? 1 : 2); // End rain / begin rain + Pkt.WriteFloat(0); // Unused for weather + } + + // TODO: Fade effect, somehow +} + + + + + +void cProtocol172::SendWholeInventory(const cWindow & a_Window) +{ + cPacketizer Pkt(*this, 0x30); // Window Items packet + Pkt.WriteChar(a_Window.GetWindowID()); + Pkt.WriteShort(a_Window.GetNumSlots()); + cItems Slots; + a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); + for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr) + { + Pkt.WriteItem(*itr); + } // for itr - Slots[] +} + + + + + +void cProtocol172::SendWindowClose(const cWindow & a_Window) +{ + cPacketizer Pkt(*this, 0x2e); + Pkt.WriteChar(a_Window.GetWindowID()); +} + + + + + +void cProtocol172::SendWindowOpen(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() < 0) + { + // Do not send this packet for player inventory windows + return; + } + + cPacketizer Pkt(*this, 0x2d); + Pkt.WriteChar(a_Window.GetWindowID()); + Pkt.WriteChar(a_Window.GetWindowType()); + Pkt.WriteString(a_Window.GetWindowTitle()); + Pkt.WriteChar(a_Window.GetNumNonInventorySlots()); + Pkt.WriteBool(true); + if (a_Window.GetWindowType() == cWindow::wtAnimalChest) + { + Pkt.WriteInt(0); // TODO: The animal's EntityID + } +} + + + + + +void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +{ + cPacketizer Pkt(*this, 0x31); // Window Property packet + Pkt.WriteChar(a_Window.GetWindowID()); + Pkt.WriteShort(a_Property); + Pkt.WriteShort(a_Value); +} + + + + + +void cProtocol172::AddReceivedData(const char * a_Data, int a_Size) +{ + if (!m_ReceivedData.Write(a_Data, a_Size)) + { + // Too much data in the incoming queue, report to caller: + m_Client->PacketBufferFull(); + return; + } + + // Handle all complete packets: + while (true) + { + UInt32 PacketLen; + if (!m_ReceivedData.ReadVarInt(PacketLen)) + { + // Not enough data + return; + } + if (!m_ReceivedData.CanReadBytes(PacketLen)) + { + // The full packet hasn't been received yet + return; + } + UInt32 PacketType; + UInt32 Mark1 = m_ReceivedData.GetReadableSpace(); + if (!m_ReceivedData.ReadVarInt(PacketType)) + { + // Not enough data + return; + } + + UInt32 NumBytesRead = Mark1 - m_ReceivedData.GetReadableSpace(); + HandlePacket(PacketType, PacketLen - NumBytesRead); + + if (Mark1 - m_ReceivedData.GetReadableSpace() > PacketLen) + { + // Read more than packet length, report as error + m_Client->PacketError(PacketType); + } + + // Go to packet end in any case: + m_ReceivedData.ResetRead(); + m_ReceivedData.ReadVarInt(PacketType); + m_ReceivedData.SkipRead(PacketLen); + m_ReceivedData.CommitRead(); + } // while (true) +} + + + + +void cProtocol172::HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes) +{ + switch (m_State) + { + case 1: + { + // Status + switch (a_PacketType) + { + case 0x00: HandlePacketStatusRequest(a_RemainingBytes); return; + case 0x01: HandlePacketStatusPing (a_RemainingBytes); return; + } + break; + } + + case 2: + { + // Login + switch (a_PacketType) + { + case 0x00: HandlePacketLoginStart(a_RemainingBytes); return; + case 0x01: HandlePacketLoginEncryptionResponse(a_RemainingBytes); return; + } + break; + } + + case 3: + { + // Game + switch (a_PacketType) + { + case 0x00: HandlePacketKeepAlive (a_RemainingBytes); return; + case 0x01: HandlePacketChatMessage (a_RemainingBytes); return; + case 0x02: HandlePacketUseEntity (a_RemainingBytes); return; + case 0x03: HandlePacketPlayer (a_RemainingBytes); return; + case 0x04: HandlePacketPlayerPos (a_RemainingBytes); return; + case 0x05: HandlePacketPlayerLook (a_RemainingBytes); return; + case 0x06: HandlePacketPlayerPosLook (a_RemainingBytes); return; + case 0x07: HandlePacketBlockDig (a_RemainingBytes); return; + case 0x08: HandlePacketBlockPlace (a_RemainingBytes); return; + case 0x09: HandlePacketSlotSelect (a_RemainingBytes); return; + case 0x0a: HandlePacketAnimation (a_RemainingBytes); return; + case 0x0b: HandlePacketEntityAction (a_RemainingBytes); return; + case 0x0c: HandlePacketSteerVehicle (a_RemainingBytes); return; + case 0x0d: HandlePacketWindowClose (a_RemainingBytes); return; + case 0x0e: HandlePacketWindowClick (a_RemainingBytes); return; + case 0x0f: // Confirm transaction - not used in MCS + case 0x10: HandlePacketCreativeInventoryAction(a_RemainingBytes); return; + case 0x12: HandlePacketUpdateSign (a_RemainingBytes); return; + case 0x13: HandlePacketPlayerAbilities (a_RemainingBytes); return; + case 0x14: HandlePacketTabComplete (a_RemainingBytes); return; + case 0x15: HandlePacketClientSettings (a_RemainingBytes); return; + case 0x16: HandlePacketClientStatus (a_RemainingBytes); return; + case 0x17: HandlePacketPluginMessage (a_RemainingBytes); return; + } + break; + } + } // switch (m_State) + + // Unknown packet type, report to the client: + m_Client->PacketUnknown(a_PacketType); + m_ReceivedData.SkipRead(a_RemainingBytes); + m_ReceivedData.CommitRead(); +} + + + + + +void cProtocol172::HandlePacketStatusPing(UInt32 a_RemainingBytes) +{ + ASSERT(a_RemainingBytes == 8); + if (a_RemainingBytes != 8) + { + m_Client->PacketError(0x01); + m_ReceivedData.SkipRead(a_RemainingBytes); + m_ReceivedData.CommitRead(); + return; + } + Int64 Timestamp; + m_ReceivedData.ReadBEInt64(Timestamp); + m_ReceivedData.CommitRead(); + + cPacketizer Pkt(*this, 0x01); // Ping packet + Pkt.WriteInt64(Timestamp); +} + + + + + +void cProtocol172::HandlePacketStatusRequest(UInt32 a_RemainingBytes) +{ + // No more bytes in this packet + ASSERT(a_RemainingBytes == 0); + m_ReceivedData.CommitRead(); + + // Send the response: + AString Response = "{\"version\":{\"name\":\"1.7.2\",\"protocol\":4},\"players\":{"; + AppendPrintf(Response, "\"max\":%u,\"online\":%u,\"sample\":[]},", + cRoot::Get()->GetServer()->GetMaxPlayers(), + cRoot::Get()->GetServer()->GetNumPlayers() + ); + AppendPrintf(Response, "\"description\":{\"text\":\"%s\"}", + cRoot::Get()->GetServer()->GetDescription().c_str() + ); + Response.append("}"); + + cPacketizer Pkt(*this, 0x00); // Response packet + Pkt.WriteString(Response); +} + + + + + +void cProtocol172::HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes) +{ + // TODO: Add protocol encryption +} + + + + + +void cProtocol172::HandlePacketLoginStart(UInt32 a_RemainingBytes) +{ + AString Username; + m_ReceivedData.ReadVarUTF8String(Username); + + // TODO: Protocol encryption should be set up here if not localhost / auth + + // Send login success: + { + cPacketizer Pkt(*this, 0x02); // Login success packet + Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID + Pkt.WriteString(Username); + } + + m_State = 3; // State = Game + m_Client->HandleLogin(4, Username); +} + + + + + +void cProtocol172::HandlePacketAnimation(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, EntityID); + HANDLE_READ(ReadByte, Byte, Animation); + m_Client->HandleAnimation(Animation); +} + + + + + +void cProtocol172::HandlePacketBlockDig(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadByte, Byte, Status); + HANDLE_READ(ReadBEInt, int, BlockX); + HANDLE_READ(ReadByte, Byte, BlockY); + HANDLE_READ(ReadBEInt, int, BlockZ); + HANDLE_READ(ReadByte, Byte, Face); + m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, Face, Status); +} + + + + + +void cProtocol172::HandlePacketBlockPlace(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, BlockX); + HANDLE_READ(ReadByte, Byte, BlockY); + HANDLE_READ(ReadBEInt, int, BlockZ); + HANDLE_READ(ReadByte, Byte, Face); + HANDLE_READ(ReadByte, Byte, CursorX); + HANDLE_READ(ReadByte, Byte, CursorY); + HANDLE_READ(ReadByte, Byte, CursorZ); + m_Client->HandleRightClick(BlockX, BlockY, BlockZ, Face, CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem()); +} + + + + + +void cProtocol172::HandlePacketChatMessage(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Message); + m_Client->HandleChat(Message); +} + + + + + +void cProtocol172::HandlePacketClientSettings(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Locale); + HANDLE_READ(ReadByte, Byte, ViewDistance); + HANDLE_READ(ReadByte, Byte, ChatFlags); + HANDLE_READ(ReadByte, Byte, Unused); + HANDLE_READ(ReadByte, Byte, Difficulty); + HANDLE_READ(ReadByte, Byte, ShowCape); + // TODO: handle in m_Client +} + + + + + +void cProtocol172::HandlePacketClientStatus(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadByte, Byte, ActionID); + switch (ActionID) + { + case 0: + { + // Respawn + m_Client->HandleRespawn(); + break; + } + case 1: + { + // Request stats + // TODO + break; + } + case 2: + { + // Open Inventory achievement + // TODO + break; + } + } +} + + + + + +void cProtocol172::HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEShort, short, SlotNum); + cItem Item; + if (!ReadItem(Item)) + { + return; + } + m_Client->HandleCreativeInventory(SlotNum, Item); +} + + + + + +void cProtocol172::HandlePacketEntityAction(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, PlayerID); + HANDLE_READ(ReadByte, Byte, Action); + HANDLE_READ(ReadBEInt, int, JumpBoost); + m_Client->HandleEntityAction(PlayerID, Action); +} + + + + + +void cProtocol172::HandlePacketKeepAlive(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, KeepAliveID); + m_Client->HandleKeepAlive(KeepAliveID); +} + + + + + +void cProtocol172::HandlePacketPlayer(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBool, bool, IsOnGround); + // TODO: m_Client->HandlePlayerOnGround(IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPlayerAbilities(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadByte, Byte, Flags); + HANDLE_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_READ(ReadBEFloat, float, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(); +} + + + + + +void cProtocol172::HandlePacketPlayerLook(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEFloat, float, Yaw); + HANDLE_READ(ReadBEFloat, float, Pitch); + HANDLE_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerLook(Yaw, Pitch, IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPlayerPos(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEDouble, double, PosX); + HANDLE_READ(ReadBEDouble, double, PosY); + HANDLE_READ(ReadBEDouble, double, Stance); + HANDLE_READ(ReadBEDouble, double, PosZ); + HANDLE_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPlayerPosLook(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEDouble, double, PosX); + HANDLE_READ(ReadBEDouble, double, PosY); + HANDLE_READ(ReadBEDouble, double, Stance); + HANDLE_READ(ReadBEDouble, double, PosZ); + HANDLE_READ(ReadBEFloat, float, Yaw); + HANDLE_READ(ReadBEFloat, float, Pitch); + HANDLE_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Yaw, Pitch, IsOnGround); +} + + + + + +void cProtocol172::HandlePacketPluginMessage(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Channel); + HANDLE_READ(ReadBEShort, short, Length); + AString Data; + m_ReceivedData.ReadString(Data, Length); + // TODO: m_Client->HandlePluginMessage(Channel, Data); +} + + + + + +void cProtocol172::HandlePacketSlotSelect(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEShort, short, SlotNum); + m_Client->HandleSlotSelected(SlotNum); +} + + + + + +void cProtocol172::HandlePacketSteerVehicle(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEFloat, float, Forward); + HANDLE_READ(ReadBEFloat, float, Sideways); + HANDLE_READ(ReadBool, bool, ShouldJump); + HANDLE_READ(ReadBool, bool, ShouldUnmount); + if (ShouldUnmount) + { + m_Client->HandleUnmount(); + } + else + { + m_Client->HandleSteerVehicle(Forward, Sideways); + } +} + + + + + +void cProtocol172::HandlePacketTabComplete(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadVarUTF8String, AString, Text); + m_Client->HandleTabCompletion(Text); +} + + + + + +void cProtocol172::HandlePacketUpdateSign(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, BlockX); + HANDLE_READ(ReadBEShort, short, BlockY); + HANDLE_READ(ReadBEInt, int, BlockZ); + HANDLE_READ(ReadVarUTF8String, AString, Line1); + HANDLE_READ(ReadVarUTF8String, AString, Line2); + HANDLE_READ(ReadVarUTF8String, AString, Line3); + HANDLE_READ(ReadVarUTF8String, AString, Line4); + m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); +} + + + + + +void cProtocol172::HandlePacketUseEntity(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadBEInt, int, EntityID); + HANDLE_READ(ReadByte, Byte, MouseButton); + m_Client->HandleUseEntity(EntityID, (MouseButton == 1)); +} + + + + + +void cProtocol172::HandlePacketWindowClick(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadChar, char, WindowID); + HANDLE_READ(ReadBEShort, short, SlotNum); + HANDLE_READ(ReadByte, Byte, Button); + HANDLE_READ(ReadBEShort, short, TransactionID); + HANDLE_READ(ReadByte, Byte, Mode); + cItem Item; + ReadItem(Item); + + // Convert Button, Mode, SlotNum and HeldItem into eClickAction: + eClickAction Action; + switch ((Mode << 8) | Button) + { + case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; + case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; + case 0x0100: Action = caShiftLeftClick; break; + case 0x0101: Action = caShiftRightClick; break; + case 0x0200: Action = caNumber1; break; + case 0x0201: Action = caNumber2; break; + case 0x0202: Action = caNumber3; break; + case 0x0203: Action = caNumber4; break; + case 0x0204: Action = caNumber5; break; + case 0x0205: Action = caNumber6; break; + case 0x0206: Action = caNumber7; break; + case 0x0207: Action = caNumber8; break; + case 0x0208: Action = caNumber9; break; + case 0x0300: Action = caMiddleClick; break; + case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; + case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; + case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; + case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; + case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; + case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; + case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; + case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; + case 0x0600: Action = caDblClick; break; + } + + m_Client->HandleWindowClick(WindowID, SlotNum, Action, Item); +} + + + + + +void cProtocol172::HandlePacketWindowClose(UInt32 a_RemainingBytes) +{ + HANDLE_READ(ReadChar, char, WindowID); + m_Client->HandleWindowClose(WindowID); +} + + + + + +void cProtocol172::WritePacket(cByteBuffer & a_Packet) +{ + cCSLock Lock(m_CSPacket); + AString Pkt; + a_Packet.ReadAll(Pkt); + WriteVarInt(Pkt.size()); + SendData(Pkt.data(), Pkt.size()); + Flush(); +} + + + + + +void cProtocol172::SendData(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + m_Client->SendData((const char *)Encrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + m_Client->SendData(a_Data, a_Size); + } +} + + + + + + +bool cProtocol172::ReadItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + if (ItemType == -1) + { + // The item is empty, no more data follows + a_Item.Empty(); + return true; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); + if (MetadataLength <= 0) + { + return true; + } + + // Read the metadata + AString Metadata; + if (!m_ReceivedData.ReadString(Metadata, MetadataLength)) + { + return false; + } + + ParseItemMetadata(a_Item, Metadata); + return true; +} + + + + + +void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +{ + // Uncompress the GZIPped data: + AString Uncompressed; + if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) + { + AString HexDump; + CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + LOGWARNING("Cannot unGZIP item metadata (%u bytes):\n%s", a_Metadata.size(), HexDump.c_str()); + return; + } + + // Parse into NBT: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + AString HexDump; + CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); + LOGWARNING("Cannot parse NBT item metadata: (%u bytes)\n%s", Uncompressed.size(), HexDump.c_str()); + return; + } + + // Load enchantments from the NBT: + for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) + { + if ( + (NBT.GetType(tag) == TAG_List) && + ( + (NBT.GetName(tag) == "ench") || + (NBT.GetName(tag) == "StoredEnchantments") + ) + ) + { + a_Item.m_Enchantments.ParseFromNBT(NBT, tag); + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol172::cPacketizer: + +cProtocol172::cPacketizer::~cPacketizer() +{ + AString DataToSend; + + // Send the packet length + UInt32 PacketLen = m_Out.GetUsedSpace(); + m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen); + m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend); + m_Protocol.SendData(DataToSend.data(), DataToSend.size()); + m_Protocol.m_OutPacketLenBuffer.CommitRead(); + + // Send the packet data: + m_Out.ReadAll(DataToSend); + m_Protocol.SendData(DataToSend.data(), DataToSend.size()); + m_Out.CommitRead(); +} + + + + + +void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + WriteShort(-1); + return; + } + + WriteShort(ItemType); + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (a_Item.m_Enchantments.IsEmpty()) + { + WriteShort(-1); + return; + } + + // Send the enchantments: + cFastNBTWriter Writer; + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + Writer.Finish(); + AString Compressed; + CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + WriteShort(Compressed.size()); + WriteBuf(Compressed.data(), Compressed.size()); +} + + + + + +void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle) +{ + WriteByte((char)(255 * a_Angle / 360)); +} + + + + + +void cProtocol172::cPacketizer::WriteFPInt(double a_Value) +{ + int Value = (int)(a_Value * 32); + WriteInt(Value); +} + + + + + +void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) +{ + // Common metadata: + Byte Flags = 0; + if (a_Entity.IsOnFire()) + { + Flags |= 0x01; + } + if (a_Entity.IsCrouched()) + { + Flags |= 0x02; + } + if (a_Entity.IsSprinting()) + { + Flags |= 0x08; + } + if (a_Entity.IsRclking()) + { + Flags |= 0x10; + } + if (a_Entity.IsInvisible()) + { + Flags |= 0x20; + } + WriteByte(0); // Byte(0) + index 0 + WriteByte(Flags); + + switch (a_Entity.GetEntityType()) + { + case cEntity::etPlayer: break; // TODO? + case cEntity::etPickup: + { + WriteByte((5 << 5) | 10); // Slot(5) + index 10 + WriteItem(((const cPickup &)a_Entity).GetItem()); + break; + } + case cEntity::etMinecart: + { + WriteByte(0x51); + + // The following expression makes Minecarts shake more with less health or higher damage taken + // It gets half the maximum health, and takes it away from the current health minus the half health: + /* Health: 5 | 3 - (5 - 3) = 1 (shake power) + Health: 3 | 3 - (3 - 3) = 3 + Health: 1 | 3 - (1 - 3) = 5 + */ + WriteInt((((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4); + WriteByte(0x52); + WriteInt(1); // Shaking direction, doesn't seem to affect anything + WriteByte(0x73); + WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer + + if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace) + { + WriteByte(0x10); + WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); + } + break; + } + case cEntity::etProjectile: + { + if (((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow) + { + WriteByte(0x10); + WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); + } + break; + } + case cEntity::etMonster: + { + WriteMobMetadata((const cMonster &)a_Entity); + break; + } + } +} + + + + + +void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) +{ + switch (a_Mob.GetMobType()) + { + case cMonster::mtCreeper: + { + WriteByte(0x10); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); + WriteByte(0x11); + WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); + break; + } + + case cMonster::mtBat: + { + WriteByte(0x10); + WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); + break; + } + + case cMonster::mtPig: + { + WriteByte(0x10); + WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); + break; + } + + case cMonster::mtVillager: + { + WriteByte(0x50); + WriteInt(((const cVillager &)a_Mob).GetVilType()); + break; + } + + case cMonster::mtZombie: + { + WriteByte(0x0c); + WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); + WriteByte(0x0d); + WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); + WriteByte(0x0e); + WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); + break; + } + + case cMonster::mtGhast: + { + WriteByte(0x10); + WriteByte(((const cGhast &)a_Mob).IsCharging()); + break; + } + + case cMonster::mtWolf: + { + const cWolf & Wolf = (const cWolf &)a_Mob; + Byte WolfStatus = 0; + if (Wolf.IsSitting()) + { + WolfStatus |= 0x1; + } + if (Wolf.IsAngry()) + { + WolfStatus |= 0x2; + } + if (Wolf.IsTame()) + { + WolfStatus |= 0x4; + } + WriteByte(0x10); + WriteByte(WolfStatus); + + WriteByte(0x72); + WriteFloat((float)(a_Mob.GetHealth())); + WriteByte(0x13); + WriteByte(Wolf.IsBegging() ? 1 : 0); + WriteByte(0x14); + WriteByte(Wolf.GetCollarColor()); + break; + } + + case cMonster::mtSheep: + { + WriteByte(0x10); + Byte SheepMetadata = 0; + SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); + if (((const cSheep &)a_Mob).IsSheared()) + { + SheepMetadata |= 0x10; + } + WriteByte(SheepMetadata); + break; + } + + case cMonster::mtEnderman: + { + WriteByte(0x10); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); + WriteByte(0x11); + WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); + WriteByte(0x12); + WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); + break; + } + + case cMonster::mtSkeleton: + { + WriteByte(0x0d); + WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); + break; + } + + case cMonster::mtWitch: + { + WriteByte(0x15); + WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); + break; + } + + case cMonster::mtSlime: + { + WriteByte(0x10); + WriteByte(((const cSlime &)a_Mob).GetSize()); + break; + } + + case cMonster::mtMagmaCube: + { + WriteByte(0x10); + WriteByte(((const cMagmaCube &)a_Mob).GetSize()); + break; + } + + case cMonster::mtHorse: + { + const cHorse & Horse = (const cHorse &)a_Mob; + int Flags = 0; + if (Horse.IsTame()) + { + Flags |= 0x02; + } + if (Horse.IsSaddled()) + { + Flags |= 0x04; + } + if (Horse.IsChested()) + { + Flags |= 0x08; + } + if (Horse.IsBaby()) + { + Flags |= 0x10; + } + if (Horse.IsEating()) + { + Flags |= 0x20; + } + if (Horse.IsRearing()) + { + Flags |= 0x40; + } + if (Horse.IsMthOpen()) + { + Flags |= 0x80; + } + WriteByte(0x50); // Int at index 16 + WriteInt(Flags); + WriteByte(0x13); // Byte at index 19 + WriteByte(Horse.GetHorseType()); + WriteByte(0x54); // Int at index 20 + int Appearance = 0; + Appearance = Horse.GetHorseColor(); + Appearance |= Horse.GetHorseStyle() << 8; + WriteInt(Appearance); + WriteByte(0x56); // Int at index 22 + WriteInt(Horse.GetHorseArmour()); + break; + } + } // switch (a_Mob.GetType()) +} + + + + + +void cProtocol172::cPacketizer::WriteEntityProperties(const cEntity & a_Entity) +{ + if (!a_Entity.IsMob()) + { + // No properties for anything else than mobs + WriteInt(0); + return; + } + const cMonster & Mob = (const cMonster &)a_Entity; + + // TODO: Send properties and modifiers based on the mob type + + WriteInt(0); // NumProperties +} + + + + diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h new file mode 100644 index 000000000..6b897fd57 --- /dev/null +++ b/src/Protocol/Protocol17x.h @@ -0,0 +1,260 @@ + +// Protocol17x.h + +/* +Declares the 1.7.x protocol classes: + - cProtocol172 + - release 1.7.2 protocol (#4) +(others may be added later in the future for the 1.7 release series) +*/ + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" +#include "lib/cryptopp/modes.h" +#include "lib/cryptopp/aes.h" + + + + + +class cProtocol172 : + public cProtocol // TODO +{ + typedef cProtocol super; // TODO + +public: + + cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + + virtual AString GetAuthServerID(void) override { return m_AuthServerID; } + +protected: + + /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed + class cPacketizer + { + public: + cPacketizer(cProtocol172 & a_Protocol, UInt32 a_PacketType) : + m_Protocol(a_Protocol), + m_Out(a_Protocol.m_OutPacketBuffer), + m_Lock(a_Protocol.m_CSPacket) + { + m_Out.WriteVarInt(a_PacketType); + } + + ~cPacketizer(); + + void WriteBool(bool a_Value) + { + m_Out.WriteBool(a_Value); + } + + void WriteByte(Byte a_Value) + { + m_Out.WriteByte(a_Value); + } + + void WriteChar(char a_Value) + { + m_Out.WriteChar(a_Value); + } + + void WriteShort(short a_Value) + { + m_Out.WriteBEShort(a_Value); + } + + void WriteInt(int a_Value) + { + m_Out.WriteBEInt(a_Value); + } + + void WriteInt64(Int64 a_Value) + { + m_Out.WriteBEInt64(a_Value); + } + + void WriteFloat(float a_Value) + { + m_Out.WriteBEFloat(a_Value); + } + + void WriteDouble(double a_Value) + { + m_Out.WriteBEDouble(a_Value); + } + + void WriteVarInt(UInt32 a_Value) + { + m_Out.WriteVarInt(a_Value); + } + + void WriteString(const AString & a_Value) + { + m_Out.WriteVarUTF8String(a_Value); + } + + void WriteBuf(const char * a_Data, int a_Size) + { + m_Out.Write(a_Data, a_Size); + } + + void WriteItem(const cItem & a_Item); + void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte + void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer + void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f + void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob + void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field + + protected: + cProtocol172 & m_Protocol; + cByteBuffer & m_Out; + cCSLock m_Lock; + } ; + + AString m_ServerAddress; + + UInt16 m_ServerPort; + + AString m_AuthServerID; + + /// State of the protocol. 1 = status, 2 = login, 3 = game + UInt32 m_State; + + /// Buffer for the received data + cByteBuffer m_ReceivedData; + + /// Buffer for composing the outgoing packets, through cPacketizer + cByteBuffer m_OutPacketBuffer; + + /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) + cByteBuffer m_OutPacketLenBuffer; + + bool m_IsEncrypted; + CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor; + CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor; + + + /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets + void AddReceivedData(const char * a_Data, int a_Size); + + /// Reads and handles the packet. The packet length and type have already been read. + void HandlePacket(UInt32 a_PacketType, UInt32 a_RemainingBytes); + + // Packet handlers while in the Status state (m_State == 1): + void HandlePacketStatusPing (UInt32 a_RemainingBytes); + void HandlePacketStatusRequest(UInt32 a_RemainingBytes); + + // Packet handlers while in the Login state (m_State == 2): + void HandlePacketLoginEncryptionResponse(UInt32 a_RemainingBytes); + void HandlePacketLoginStart (UInt32 a_RemainingBytes); + + // Packet handlers while in the Game state (m_State == 3): + void HandlePacketAnimation (UInt32 a_RemainingBytes); + void HandlePacketBlockDig (UInt32 a_RemainingBytes); + void HandlePacketBlockPlace (UInt32 a_RemainingBytes); + void HandlePacketChatMessage (UInt32 a_RemainingBytes); + void HandlePacketClientSettings (UInt32 a_RemainingBytes); + void HandlePacketClientStatus (UInt32 a_RemainingBytes); + void HandlePacketCreativeInventoryAction(UInt32 a_RemainingBytes); + void HandlePacketEntityAction (UInt32 a_RemainingBytes); + void HandlePacketKeepAlive (UInt32 a_RemainingBytes); + void HandlePacketPlayer (UInt32 a_RemainingBytes); + void HandlePacketPlayerAbilities (UInt32 a_RemainingBytes); + void HandlePacketPlayerLook (UInt32 a_RemainingBytes); + void HandlePacketPlayerPos (UInt32 a_RemainingBytes); + void HandlePacketPlayerPosLook (UInt32 a_RemainingBytes); + void HandlePacketPluginMessage (UInt32 a_RemainingBytes); + void HandlePacketSlotSelect (UInt32 a_RemainingBytes); + void HandlePacketSteerVehicle (UInt32 a_RemainingBytes); + void HandlePacketTabComplete (UInt32 a_RemainingBytes); + void HandlePacketUpdateSign (UInt32 a_RemainingBytes); + void HandlePacketUseEntity (UInt32 a_RemainingBytes); + void HandlePacketWindowClick (UInt32 a_RemainingBytes); + void HandlePacketWindowClose (UInt32 a_RemainingBytes); + + + /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. + void WritePacket(cByteBuffer & a_Packet); + + /// Sends the data to the client, encrypting them if needed. + virtual void SendData(const char * a_Data, int a_Size) override; + + void SendCompass(const cWorld & a_World); + + /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data + bool ReadItem(cItem & a_Item); + + /// Parses item metadata as read by ReadItem(), into the item enchantments. + void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); +} ; + + + + diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp new file mode 100644 index 000000000..489149d74 --- /dev/null +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -0,0 +1,925 @@ + +// ProtocolRecognizer.cpp + +// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + +#include "Globals.h" + +#include "ProtocolRecognizer.h" +#include "Protocol125.h" +#include "Protocol132.h" +#include "Protocol14x.h" +#include "Protocol15x.h" +#include "Protocol16x.h" +#include "Protocol17x.h" +#include "../ClientHandle.h" +#include "../Root.h" +#include "../Server.h" +#include "../World.h" +#include "../ChatColor.h" + + + + + +cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : + super(a_Client), + m_Protocol(NULL), + m_Buffer(512) +{ +} + + + + + +cProtocolRecognizer::~cProtocolRecognizer() +{ + delete m_Protocol; +} + + + + + +AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) +{ + switch (a_ProtocolVersion) + { + case PROTO_VERSION_1_2_5: return "1.2.5"; + case PROTO_VERSION_1_3_2: return "1.3.2"; + case PROTO_VERSION_1_4_2: return "1.4.2"; + case PROTO_VERSION_1_4_4: return "1.4.4"; + case PROTO_VERSION_1_4_6: return "1.4.6"; + case PROTO_VERSION_1_5_0: return "1.5"; + 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"; + case PROTO_VERSION_1_7_2: return "1.7.2"; + } + ASSERT(!"Unknown protocol version"); + return Printf("Unknown protocol (%d)", a_ProtocolVersion); +} + + + + + +void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) +{ + if (m_Protocol == NULL) + { + if (!m_Buffer.Write(a_Data, a_Size)) + { + m_Client->Kick("Unsupported protocol version"); + return; + } + + if (!TryRecognizeProtocol()) + { + return; + } + + // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing: + AString Dump; + m_Buffer.ResetRead(); + m_Buffer.ReadAll(Dump); + m_Protocol->DataReceived(Dump.data(), Dump.size()); + } + else + { + m_Protocol->DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage); +} + + + + + +void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cProtocolRecognizer::SendChat(const AString & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChat(a_Message); +} + + + + + +void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); +} + + + + + +void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) +{ + if (m_Protocol != NULL) + { + m_Protocol->SendDisconnect(a_Reason); + } + else + { + // This is used when the client sends a server-ping, respond with the default packet: + WriteByte ((char)0xff); // PACKET_DISCONNECT + WriteString(a_Reason); + } +} + + + + +void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityHeadLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityMetadata(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityProperties(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityVelocity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion); +} + + + + + +void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cProtocolRecognizer::SendHealth(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendHealth(); +} + + + + + +void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); +} + + + + + +void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendKeepAlive(int a_PingID) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendKeepAlive(a_PingID); +} + + + + + +void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendLogin(a_Player, a_World); +} + + + + + +void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cProtocolRecognizer::SendPlayerAbilities(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerAbilities(); +} + + + + + +void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cProtocolRecognizer::SendPlayerMaxSpeed(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMaxSpeed(); +} + + + + + +void cProtocolRecognizer::SendPlayerMoveLook(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cProtocolRecognizer::SendPlayerPosition(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerPosition(); +} + + + + + +void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cProtocolRecognizer::SendRespawn(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendRespawn(); +} + + + + + +void cProtocolRecognizer::SendExperience(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendExperience(); +} + + + + + +void cProtocolRecognizer::SendExperienceOrb(const cExpOrb & a_ExpOrb) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendExperienceOrb(a_ExpOrb); +} + + + + + +void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); +} + + + + + +void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnFallingBlock(a_FallingBlock); +} + + + + + +void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType); +} + + + + + +void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTabCompletionResults(a_Results); +} + + + + + +void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); +} + + + + + +void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendWeather(eWeather a_Weather) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowClose(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowOpen(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowOpen(a_Window); +} + + + + + +AString cProtocolRecognizer::GetAuthServerID(void) +{ + ASSERT(m_Protocol != NULL); + return m_Protocol->GetAuthServerID(); +} + + + + + +void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) +{ + // This is used only when handling the server ping + m_Client->SendData(a_Data, a_Size); +} + + + + + +bool cProtocolRecognizer::TryRecognizeProtocol(void) +{ + // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and + // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro + + // The first packet should be a Handshake, 0x02: + unsigned char PacketType; + if (!m_Buffer.ReadByte(PacketType)) + { + return false; + } + switch (PacketType) + { + case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing + case 0xfe: + { + // This may be either a packet length or the length-less Ping packet + Byte NextByte; + if (!m_Buffer.ReadByte(NextByte)) + { + // Not enough data for either protocol + // This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway + return false; + } + if (NextByte != 0x01) + { + // This is definitely NOT a length-less Ping packet, handle as lengthed protocol: + break; + } + if (!m_Buffer.ReadByte(NextByte)) + { + // There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely + // and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message) + SendLengthlessServerPing(); + return false; + } + if (NextByte == 0xfa) + { + // Definitely a length-less Ping followed by a Plugin message + SendLengthlessServerPing(); + return false; + } + // Definitely a lengthed Initial handshake, handle below: + break; + } + } // switch (PacketType) + + // This must be a lengthed protocol, try if it has the entire initial handshake packet: + m_Buffer.ResetRead(); + UInt32 PacketLen; + UInt32 ReadSoFar = m_Buffer.GetReadableSpace(); + if (!m_Buffer.ReadVarInt(PacketLen)) + { + // Not enough bytes for the packet length, keep waiting + return false; + } + ReadSoFar -= m_Buffer.GetReadableSpace(); + if (!m_Buffer.CanReadBytes(PacketLen)) + { + // Not enough bytes for the packet, keep waiting + return false; + } + return TryRecognizeLengthedProtocol(PacketLen - ReadSoFar); +} + + + + + +bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void) +{ + // The comm started with 0x02, which is a Handshake packet in the length-less protocol family + // 1.3.2 starts with 0x02 0x39 <name-length-short> + // 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :) + char ch; + if (!m_Buffer.ReadChar(ch)) + { + return false; + } + switch (ch) + { + case PROTO_VERSION_1_3_2: + { + m_Protocol = new cProtocol132(m_Client); + return true; + } + case PROTO_VERSION_1_4_2: + case PROTO_VERSION_1_4_4: + { + m_Protocol = new cProtocol142(m_Client); + return true; + } + case PROTO_VERSION_1_4_6: + { + m_Protocol = new cProtocol146(m_Client); + return true; + } + case PROTO_VERSION_1_5_0: + case PROTO_VERSION_1_5_2: + { + m_Protocol = new cProtocol150(m_Client); + return true; + } + case PROTO_VERSION_1_6_1: + { + m_Protocol = new cProtocol161(m_Client); + 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; + } + } + m_Protocol = new cProtocol125(m_Client); + return true; +} + + + + + +bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining) +{ + UInt32 PacketType; + UInt32 NumBytesRead = m_Buffer.GetReadableSpace(); + if (!m_Buffer.ReadVarInt(PacketType)) + { + return false; + } + if (PacketType != 0x00) + { + // Not an initial handshake packet, we don't know how to talk to them + LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, initial packet %u)", + m_Client->GetIPString().c_str(), PacketType + ); + m_Client->Kick("Unsupported protocol version"); + return false; + } + UInt32 ProtocolVersion; + if (!m_Buffer.ReadVarInt(ProtocolVersion)) + { + return false; + } + NumBytesRead -= m_Buffer.GetReadableSpace(); + switch (ProtocolVersion) + { + case PROTO_VERSION_1_7_2: + { + AString ServerAddress; + short ServerPort; + UInt32 NextState; + m_Buffer.ReadVarUTF8String(ServerAddress); + m_Buffer.ReadBEShort(ServerPort); + m_Buffer.ReadVarInt(NextState); + m_Buffer.CommitRead(); + m_Protocol = new cProtocol172(m_Client, ServerAddress, ServerPort, NextState); + return true; + } + } + LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)", + m_Client->GetIPString().c_str(), ProtocolVersion + ); + m_Client->Kick("Unsupported protocol version"); + return false; +} + + + + + +void cProtocolRecognizer::SendLengthlessServerPing(void) +{ + AString Reply; + switch (cRoot::Get()->GetPrimaryServerVersion()) + { + case PROTO_VERSION_1_2_5: + case PROTO_VERSION_1_3_2: + { + // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29 + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetServer()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetServer()->GetMaxPlayers() + ); + break; + } + + case PROTO_VERSION_1_4_2: + case PROTO_VERSION_1_4_4: + case PROTO_VERSION_1_4_6: + case PROTO_VERSION_1_5_0: + 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 + // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit. + // Who cares? We're disconnecting anyway. + m_Buffer.ResetRead(); + if (m_Buffer.CanReadBytes(2)) + { + byte val; + m_Buffer.ReadByte(val); // Packet type - Serverlist ping + m_Buffer.ReadByte(val); // 0x01 magic value + ASSERT(val == 0x01); + } + + // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100 + AString NumPlayers; + Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers()); + AString MaxPlayers; + Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers()); + + AString ProtocolVersionNum; + Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion()); + AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion())); + + // Cannot use Printf() because of in-string NUL bytes. + Reply = cChatColor::Delimiter; + Reply.append("1"); + Reply.push_back(0); + Reply.append(ProtocolVersionNum); + Reply.push_back(0); + Reply.append(ProtocolVersionTxt); + Reply.push_back(0); + Reply.append(cRoot::Get()->GetServer()->GetDescription()); + Reply.push_back(0); + Reply.append(NumPlayers); + Reply.push_back(0); + Reply.append(MaxPlayers); + break; + } + } // switch (m_PrimaryServerVersion) + m_Client->Kick(Reply); +} + + + + diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h new file mode 100644 index 000000000..9ca0c1c88 --- /dev/null +++ b/src/Protocol/ProtocolRecognizer.h @@ -0,0 +1,154 @@ + +// ProtocolRecognizer.h + +// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +// 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, 1.6.3, 1.6.4, 1.7.2" +#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4" + + + + + +class cProtocolRecognizer : + public cProtocol +{ + typedef cProtocol super; + +public: + enum + { + PROTO_VERSION_1_2_5 = 29, + PROTO_VERSION_1_3_2 = 39, + PROTO_VERSION_1_4_2 = 47, + PROTO_VERSION_1_4_4 = 49, + PROTO_VERSION_1_4_6 = 51, + PROTO_VERSION_1_5_0 = 60, + 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 + + // These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols + PROTO_VERSION_1_7_2 = 4, + } ; + + cProtocolRecognizer(cClientHandle * a_Client); + virtual ~cProtocolRecognizer(); + + /// Translates protocol version number into protocol version text: 49 -> "1.4.4" + static AString GetVersionTextFromInt(int a_ProtocolVersion); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAbilities (void) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendExperience (void) override; + virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; + virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override; + virtual void SendTabCompletionResults(const AStringVector & a_Results) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (const cWindow & a_Window) override; + virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + + virtual AString GetAuthServerID(void) override; + + virtual void SendData(const char * a_Data, int a_Size) override; + +protected: + cProtocol * m_Protocol; //< The recognized protocol + cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol + + /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized + bool TryRecognizeProtocol(void); + + /** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized. + Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet + Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only + when the 0x02 Handshake packet has been received + */ + bool TryRecognizeLengthlessProtocol(void); + + /** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized. + The packet length and type have already been read, type is 0 + The number of bytes remaining in the packet is passed as a_PacketLengthRemaining + **/ + bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining); + + /** Called when the recognizer gets a length-less protocol's server ping packet + Responds with server stats and destroys the client. + */ + void SendLengthlessServerPing(void); +} ; + + + + + diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp new file mode 100644 index 000000000..72f2d9ba9 --- /dev/null +++ b/src/RCONServer.cpp @@ -0,0 +1,333 @@ + +// RCONServer.cpp + +// Implements the cRCONServer class representing the RCON server + +#include "Globals.h" +#include "inifile/iniFile.h" +#include "RCONServer.h" +#include "Server.h" +#include "Root.h" +#include "CommandOutput.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +enum +{ + // Client -> Server: + RCON_PACKET_COMMAND = 2, + RCON_PACKET_LOGIN = 3, + + // Server -> Client: + RCON_PACKET_RESPONSE = 2, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONCommandOutput: + +class cRCONCommandOutput : + public cCommandOutputCallback +{ +public: + cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : + m_Connection(a_Connection), + m_RequestID(a_RequestID) + { + } + + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + m_Buffer.append(a_Text); + } + + virtual void Finished(void) override + { + m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str()); + delete this; + } + +protected: + cRCONServer::cConnection & m_Connection; + int m_RequestID; + AString m_Buffer; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONServer: + +cRCONServer::cRCONServer(cServer & a_Server) : + m_Server(a_Server), + m_ListenThread4(*this, cSocket::IPv4, "RCON IPv4"), + m_ListenThread6(*this, cSocket::IPv6, "RCON IPv6") +{ +} + + + + + +cRCONServer::~cRCONServer() +{ + m_ListenThread4.Stop(); + m_ListenThread6.Stop(); +} + + + + + +void cRCONServer::Initialize(cIniFile & a_IniFile) +{ + if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) + { + return; + } + + // Read the password, don't allow an empty one: + m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); + if (m_Password.empty()) + { + LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); + return; + } + + // Read and initialize both IPv4 and IPv6 ports for RCON + bool HasAnyPorts = false; + AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); + if (m_ListenThread4.Initialize(Ports4)) + { + HasAnyPorts = true; + m_ListenThread4.Start(); + } + AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); + if (m_ListenThread6.Initialize(Ports6)) + { + HasAnyPorts = true; + m_ListenThread6.Start(); + } + if (!HasAnyPorts) + { + LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled."); + return; + } +} + + + + + +void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) +{ + if (!a_Socket.IsValid()) + { + return; + } + + LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); + + // Create a new cConnection object, it will be deleted when the connection is closed + m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONServer::cConnection: + +cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : + m_IsAuthenticated(false), + m_RCONServer(a_RCONServer), + m_Socket(a_Socket), + m_IPAddress(a_Socket.GetIPString()) +{ +} + + + + + +void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size) +{ + // Append data to the buffer: + m_Buffer.append(a_Data, a_Size); + + // Process the packets in the buffer: + while (m_Buffer.size() >= 14) + { + int Length = IntFromBuffer(m_Buffer.data()); + if (Length > 1500) + { + // Too long, drop the connection + LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", + Length, m_IPAddress.c_str() + ); + m_RCONServer.m_SocketThreads.RemoveClient(this); + m_Socket.CloseSocket(); + delete this; + return; + } + if (Length > (int)(m_Buffer.size() + 4)) + { + // Incomplete packet yet, wait for more data to come + return; + } + + int RequestID = IntFromBuffer(m_Buffer.data() + 4); + int PacketType = IntFromBuffer(m_Buffer.data() + 8); + if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) + { + m_RCONServer.m_SocketThreads.RemoveClient(this); + m_Socket.CloseSocket(); + delete this; + return; + } + m_Buffer.erase(0, Length + 4); + } // while (m_Buffer.size() >= 14) +} + + + + + +void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) +{ + a_Data.assign(m_Outgoing); + m_Outgoing.clear(); +} + + + + + +void cRCONServer::cConnection::SocketClosed(void) +{ + m_RCONServer.m_SocketThreads.RemoveClient(this); + delete this; +} + + + + + +bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +{ + switch (a_PacketType) + { + case RCON_PACKET_LOGIN: + { + if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0) + { + LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str()); + SendResponse(-1, RCON_PACKET_RESPONSE, 0, NULL); + return false; + } + m_IsAuthenticated = true; + + LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str()); + + // Send OK response: + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); + return true; + } + + case RCON_PACKET_COMMAND: + { + if (!m_IsAuthenticated) + { + char AuthNeeded[] = "You need to authenticate first!"; + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded); + return false; + } + + AString cmd(a_Payload, a_PayloadLength); + LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); + cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID))); + + // Send an empty response: + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); + return true; + } + } + + // Unknown packet type, drop the connection: + LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.", + m_IPAddress.c_str(), a_PacketType + ); + return false; +} + + + + + +/// Reads 4 bytes from a_Buffer and returns the int they represent +int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer) +{ + return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0]; +} + + + + + +/// Puts 4 bytes representing the int into the buffer +void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) +{ + a_Buffer[0] = a_Value & 0xff; + a_Buffer[1] = (a_Value >> 8) & 0xff; + a_Buffer[2] = (a_Value >> 16) & 0xff; + a_Buffer[3] = (a_Value >> 24) & 0xff; +} + + + + + +/// Sends a RCON packet back to the client +void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +{ + ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr + + char Buffer[4]; + int Length = a_PayloadLength + 10; + IntToBuffer(Length, Buffer); + m_Outgoing.append(Buffer, 4); + IntToBuffer(a_RequestID, Buffer); + m_Outgoing.append(Buffer, 4); + IntToBuffer(a_PacketType, Buffer); + m_Outgoing.append(Buffer, 4); + if (a_PayloadLength > 0) + { + m_Outgoing.append(a_Payload, a_PayloadLength); + } + m_Outgoing.push_back(0); + m_Outgoing.push_back(0); + m_RCONServer.m_SocketThreads.NotifyWrite(this); +} + + + + diff --git a/src/RCONServer.h b/src/RCONServer.h new file mode 100644 index 000000000..0e89800a2 --- /dev/null +++ b/src/RCONServer.h @@ -0,0 +1,109 @@ + +// RCONServer.h + +// Declares the cRCONServer class representing the RCON server + + + + + +#pragma once + +#include "OSSupport/SocketThreads.h" +#include "OSSupport/ListenThread.h" + + + + + +// fwd: +class cServer; +class cIniFile; + + + + + +class cRCONServer : + public cListenThread::cCallback +{ +public: + cRCONServer(cServer & a_Server); + ~cRCONServer(); + + void Initialize(cIniFile & a_IniFile); + +protected: + friend class cRCONCommandOutput; + + class cConnection : + public cSocketThreads::cCallback + { + public: + cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); + + protected: + friend class cRCONCommandOutput; + + /// Set to true if the client has successfully authenticated + bool m_IsAuthenticated; + + /// Buffer for the incoming data + AString m_Buffer; + + /// Buffer for the outgoing data + AString m_Outgoing; + + /// Server that owns this connection and processes requests + cRCONServer & m_RCONServer; + + /// The socket belonging to the client + cSocket & m_Socket; + + /// Address of the client + AString m_IPAddress; + + + // cSocketThreads::cCallback overrides: + virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void GetOutgoingData(AString & a_Data) override; + virtual void SocketClosed(void) override; + + /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped + bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + + /// Reads 4 bytes from a_Buffer and returns the int they represent + int IntFromBuffer(const char * a_Buffer); + + /// Puts 4 bytes representing the int into the buffer + void IntToBuffer(int a_Value, char * a_Buffer); + + /// Sends a RCON packet back to the client + void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + } ; + + + /// The server object that will process the commands received + cServer & m_Server; + + /// The thread(s) that take care of all the traffic on the RCON ports + cSocketThreads m_SocketThreads; + + /// The thread for accepting IPv4 RCON connections + cListenThread m_ListenThread4; + + /// The thread for accepting IPv6 RCON connections + cListenThread m_ListenThread6; + + /// Password for authentication + AString m_Password; + + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; +} ; + + + + + diff --git a/src/ReferenceManager.cpp b/src/ReferenceManager.cpp new file mode 100644 index 000000000..6a9ed0e43 --- /dev/null +++ b/src/ReferenceManager.cpp @@ -0,0 +1,43 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ReferenceManager.h" +#include "Entities/Entity.h" + + + + + +cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ) + : m_Type( a_Type ) +{ +} + +cReferenceManager::~cReferenceManager() +{ + if( m_Type == RFMNGR_REFERENCERS ) + { + for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) + { + *(*itr) = 0; // Set referenced pointer to 0 + } + } + else + { + for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) + { + cEntity* Ptr = (*(*itr)); + if( Ptr ) Ptr->Dereference( *(*itr) ); + } + } +} + +void cReferenceManager::AddReference( cEntity*& a_EntityPtr ) +{ + m_References.push_back( &a_EntityPtr ); +} + +void cReferenceManager::Dereference( cEntity*& a_EntityPtr ) +{ + m_References.remove( &a_EntityPtr ); +}
\ No newline at end of file diff --git a/src/ReferenceManager.h b/src/ReferenceManager.h new file mode 100644 index 000000000..bcd451f72 --- /dev/null +++ b/src/ReferenceManager.h @@ -0,0 +1,34 @@ + +#pragma once + + + + + +class cEntity; + + + + + +class cReferenceManager +{ +public: + enum ENUM_REFERENCE_MANAGER_TYPE + { + RFMNGR_REFERENCERS, + RFMNGR_REFERENCES, + }; + cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ); + ~cReferenceManager(); + + void AddReference( cEntity*& a_EntityPtr ); + void Dereference( cEntity*& a_EntityPtr ); +private: + ENUM_REFERENCE_MANAGER_TYPE m_Type; + std::list< cEntity** > m_References; +}; + + + + diff --git a/src/Root.cpp b/src/Root.cpp new file mode 100644 index 000000000..282227525 --- /dev/null +++ b/src/Root.cpp @@ -0,0 +1,754 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Root.h" +#include "Server.h" +#include "World.h" +#include "WebAdmin.h" +#include "FurnaceRecipe.h" +#include "GroupManager.h" +#include "CraftingRecipes.h" +#include "PluginManager.h" +#include "MonsterConfig.h" +#include "Entities/Player.h" +#include "Blocks/BlockHandler.h" +#include "Items/ItemHandler.h" +#include "Chunk.h" +#include "Protocol/ProtocolRecognizer.h" // for protocol version constants +#include "CommandOutput.h" +#include "DeadlockDetect.h" +#include "OSSupport/Timer.h" + +#include "lib/iniFile/iniFile.h" + +#ifdef _WIN32 + #include <psapi.h> +#elif defined(__linux__) + #include <fstream> +#elif defined(__APPLE__) + #include <mach/mach.h> +#endif + + + + + +cRoot* cRoot::s_Root = NULL; + + + + + +cRoot::cRoot() + : m_Server( NULL ) + , m_MonsterConfig( NULL ) + , m_GroupManager( NULL ) + , m_CraftingRecipes(NULL) + , m_FurnaceRecipe( NULL ) + , m_WebAdmin( NULL ) + , m_PluginManager( NULL ) + , m_Log( NULL ) + , m_bStop( false ) + , m_bRestart( false ) + , m_InputThread( NULL ) + , m_pDefaultWorld( NULL ) +{ + s_Root = this; +} + + + + + +cRoot::~cRoot() +{ + s_Root = 0; +} + + + + + +void cRoot::InputThread(void * a_Params) +{ + cRoot & self = *(cRoot*)a_Params; + + cLogCommandOutputCallback Output; + + while (!(self.m_bStop || self.m_bRestart) && std::cin.good()) + { + std::string Command; + std::getline(std::cin, Command); + if (!Command.empty()) + { + self.ExecuteConsoleCommand(Command, Output); + } + } + + if (!(self.m_bStop || self.m_bRestart)) + { + // We have come here because the std::cin has received an EOF and the server is still running; stop the server: + self.m_bStop = true; + } +} + + + + + +void cRoot::Start(void) +{ + cDeadlockDetect dd; + delete m_Log; + m_Log = new cMCLogger(); + + m_bStop = false; + while (!m_bStop) + { + cTimer Time; + long long mseconds = Time.GetNowTime(); + + m_bRestart = false; + + LoadGlobalSettings(); + + LOG("Creating new server instance..."); + m_Server = new cServer(); + + LOG("Reading server config..."); + cIniFile IniFile; + if (!IniFile.ReadFile("settings.ini")) + { + LOGWARN("Regenerating settings.ini, all settings will be reset"); + IniFile.AddHeaderComment(" This is the main server configuration"); + IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); + IniFile.AddHeaderComment(" See: http://www.mc-server.org/wiki/doku.php?id=configure:settings.ini for further configuration help"); + } + + m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); + if (m_PrimaryServerVersion == 0) + { + m_PrimaryServerVersion = cProtocolRecognizer::PROTO_VERSION_LATEST; + } + else + { + // Make a note in the log that the primary server version is explicitly set in the ini file + LOGINFO("Primary server version set explicitly to %d.", m_PrimaryServerVersion); + } + + LOG("Starting server..."); + if (!m_Server->InitServer(IniFile)) + { + LOGERROR("Failure starting server, aborting..."); + return; + } + + m_WebAdmin = new cWebAdmin(); + m_WebAdmin->Init(); + + LOGD("Loading settings..."); + m_GroupManager = new cGroupManager(); + m_CraftingRecipes = new cCraftingRecipes; + m_FurnaceRecipe = new cFurnaceRecipe(); + + LOGD("Loading worlds..."); + LoadWorlds(IniFile); + + LOGD("Loading plugin manager..."); + m_PluginManager = new cPluginManager(); + m_PluginManager->ReloadPluginsNow(IniFile); + + LOGD("Loading MonsterConfig..."); + m_MonsterConfig = new cMonsterConfig; + + // This sets stuff in motion + LOGD("Starting Authenticator..."); + m_Authenticator.Start(IniFile); + + IniFile.WriteFile("settings.ini"); + + LOGD("Starting worlds..."); + StartWorlds(); + + LOGD("Starting deadlock detector..."); + dd.Start(); + + LOGD("Finalising startup..."); + m_Server->Start(); + + m_WebAdmin->Start(); + + #if !defined(ANDROID_NDK) + LOGD("Starting InputThread..."); + m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); + m_InputThread->Start( false ); // We should NOT wait? Otherwise we can´t stop the server from other threads than the input thread + #endif + + 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); + } + + #if !defined(ANDROID_NDK) + delete m_InputThread; m_InputThread = NULL; + #endif + + // Deallocate stuffs + LOG("Shutting down server..."); + m_Server->Shutdown(); + + LOGD("Shutting down deadlock detector..."); + dd.Stop(); + + LOGD("Stopping world threads..."); + StopWorlds(); + + LOGD("Stopping authenticator..."); + m_Authenticator.Stop(); + + LOGD("Freeing MonsterConfig..."); + delete m_MonsterConfig; m_MonsterConfig = NULL; + delete m_WebAdmin; m_WebAdmin = NULL; + LOGD("Unloading recipes..."); + delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; + delete m_CraftingRecipes; m_CraftingRecipes = NULL; + LOGD("Forgetting groups..."); + delete m_GroupManager; m_GroupManager = 0; + LOGD("Unloading worlds..."); + UnloadWorlds(); + + LOGD("Stopping plugin manager..."); + delete m_PluginManager; m_PluginManager = NULL; + + cItemHandler::Deinit(); + cBlockHandler::Deinit(); + + LOG("Cleaning up..."); + //delete HeartBeat; HeartBeat = 0; + delete m_Server; m_Server = 0; + LOG("Shutdown successful!"); + } + + delete m_Log; m_Log = 0; +} + + + + + +void cRoot::LoadGlobalSettings() +{ + // Nothing needed yet +} + + + + + +void cRoot::LoadWorlds(cIniFile & IniFile) +{ + // First get the default world + AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); + m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() ); + m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; + + // Then load the other worlds + unsigned int KeyNum = IniFile.FindKey("Worlds"); + unsigned int NumWorlds = IniFile.GetNumValues( KeyNum ); + if (NumWorlds <= 0) + { + return; + } + + bool FoundAdditionalWorlds = false; + for (unsigned int i = 0; i < NumWorlds; i++) + { + AString ValueName = IniFile.GetValueName(KeyNum, i ); + if (ValueName.compare("World") != 0) + { + continue; + } + AString WorldName = IniFile.GetValue(KeyNum, i ); + if (WorldName.empty()) + { + continue; + } + FoundAdditionalWorlds = true; + cWorld* NewWorld = new cWorld( WorldName.c_str() ); + m_WorldsByName[ WorldName ] = NewWorld; + } // for i - Worlds + + if (!FoundAdditionalWorlds) + { + if (IniFile.GetKeyComment("Worlds", 0) != " World=secondworld") + { + IniFile.AddKeyComment("Worlds", " World=secondworld"); + } + } +} + + + + + +void cRoot::StartWorlds(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->Start(); + itr->second->InitializeSpawn(); + } +} + + + + + +void cRoot::StopWorlds(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->Stop(); + } +} + + + + + +void cRoot::UnloadWorlds(void) +{ + m_pDefaultWorld = NULL; + for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + delete itr->second; + } + m_WorldsByName.clear(); +} + + + + + +cWorld* cRoot::GetDefaultWorld() +{ + return m_pDefaultWorld; +} + + + + + +cWorld* cRoot::GetWorld( const AString & a_WorldName ) +{ + WorldMap::iterator itr = m_WorldsByName.find( a_WorldName ); + if( itr != m_WorldsByName.end() ) + return itr->second; + return 0; +} + + + + + +bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(itr->second)) + { + return false; + } + } + return true; +} + + + + + +void cRoot::TickCommands(void) +{ + // Execute any pending commands: + cCommandQueue PendingCommands; + { + cCSLock Lock(m_CSPendingCommands); + std::swap(PendingCommands, m_PendingCommands); + } + for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr) + { + ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output)); + } +} + + + + + +void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) +{ + // Some commands are built-in: + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } + + // Put the command into a queue (Alleviates FS #363): + cCSLock Lock(m_CSPendingCommands); + m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output)); +} + + + + + +void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd) +{ + // Some commands are built-in: + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } + + // Put the command into a queue (Alleviates FS #363): + cCSLock Lock(m_CSPendingCommands); + m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback)); +} + + + + + +void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) +{ + // Some commands are built-in: + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } + + LOG("Executing console command: \"%s\"", a_Cmd.c_str()); + m_Server->ExecuteConsoleCommand(a_Cmd, a_Output); +} + + + + + +void cRoot::KickUser(int a_ClientID, const AString & a_Reason) +{ + m_Server->KickUser(a_ClientID, a_Reason); +} + + + + + +void cRoot::AuthenticateUser(int a_ClientID) +{ + m_Server->AuthenticateUser(a_ClientID); +} + + + + + +int cRoot::GetTotalChunkCount(void) +{ + int res = 0; + for ( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + res += itr->second->GetNumChunks(); + } + return res; +} + + + + + +void cRoot::SaveAllChunks(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->QueueSaveAllChunks(); + } +} + + + + + +void cRoot::BroadcastChat(const AString & a_Message) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + itr->second->BroadcastChat(a_Message); + } // for itr - m_WorldsByName[] +} + + + + + +bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) + { + ++itr2; + if (!itr->second->ForEachPlayer(a_Callback)) + { + return false; + } + } + return true; +} + + + + + +bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + class cCallback : public cPlayerListCallback + { + unsigned int BestRating; + unsigned int NameLength; + const AString PlayerName; + + cPlayerListCallback & m_Callback; + virtual bool Item (cPlayer * a_pPlayer) + { + unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName()); + if (Rating > 0 && Rating >= BestRating) + { + BestMatch = a_pPlayer; + if( Rating > BestRating ) NumMatches = 0; + BestRating = Rating; + ++NumMatches; + } + if (Rating == NameLength) // Perfect match + { + return true; + } + return false; + } + + public: + cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) + : m_Callback( a_Callback ) + , BestMatch( NULL ) + , BestRating( 0 ) + , NumMatches( 0 ) + , NameLength( a_PlayerName.length() ) + , PlayerName( a_PlayerName ) + {} + + cPlayer * BestMatch; + unsigned int NumMatches; + } Callback (a_PlayerName, a_Callback); + ForEachPlayer( Callback ); + + if (Callback.NumMatches == 1) + { + return a_Callback.Item (Callback.BestMatch); + } + return false; +} + + + + + +AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) +{ + return cProtocolRecognizer::GetVersionTextFromInt(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; + int SumNumDirty = 0; + int SumNumInLighting = 0; + int SumNumInGenerator = 0; + int SumMem = 0; + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + cWorld * World = itr->second; + int NumInGenerator = World->GetGeneratorQueueLength(); + int NumInSaveQueue = World->GetStorageSaveQueueLength(); + int NumInLoadQueue = World->GetStorageLoadQueueLength(); + int NumValid = 0; + int NumDirty = 0; + int NumInLighting = 0; + World->GetChunkStats(NumValid, NumDirty, NumInLighting); + a_Output.Out("World %s:", World->GetName().c_str()); + a_Output.Out(" Num loaded chunks: %d", NumValid); + a_Output.Out(" Num dirty chunks: %d", NumDirty); + a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting); + a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator); + a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue); + a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue); + int Mem = NumValid * sizeof(cChunk); + a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); + a_Output.Out(" Per-chunk memory size breakdown:"); + a_Output.Out(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); + a_Output.Out(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); + a_Output.Out(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); + a_Output.Out(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024); + a_Output.Out(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024); + int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap); + a_Output.Out(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024); + SumNumValid += NumValid; + SumNumDirty += NumDirty; + SumNumInLighting += NumInLighting; + SumNumInGenerator += NumInGenerator; + SumMem += Mem; + } + a_Output.Out("Totals:"); + a_Output.Out(" Num loaded chunks: %d", SumNumValid); + a_Output.Out(" Num dirty chunks: %d", SumNumDirty); + a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting); + a_Output.Out(" Num chunks in generator queue: %d", SumNumInGenerator); + a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)); +} + + + + + +int cRoot::GetFurnaceFuelBurnTime(const cItem & a_Fuel) +{ + cFurnaceRecipe * FR = Get()->GetFurnaceRecipe(); + return FR->GetBurnTime(a_Fuel); +} + + + + diff --git a/src/Root.h b/src/Root.h new file mode 100644 index 000000000..4e38dd17f --- /dev/null +++ b/src/Root.h @@ -0,0 +1,190 @@ + +#pragma once + +#include "Authenticator.h" +#include "HTTPServer/HTTPServer.h" + + + + + +// fwd: +class cThread; +class cMonsterConfig; +class cGroupManager; +class cCraftingRecipes; +class cFurnaceRecipe; +class cWebAdmin; +class cPluginManager; +class cServer; +class cWorld; +class cPlayer; +class cCommandOutputCallback ; + +typedef cItemCallback<cPlayer> cPlayerListCallback; +typedef cItemCallback<cWorld> cWorldListCallback; + + + + + +/// The root of the object hierarchy +class cRoot // tolua_export +{ // tolua_export +public: + static cRoot * Get() { return s_Root; } // tolua_export + + cRoot(void); + ~cRoot(); + + void Start(void); + + cServer * GetServer(void) { return m_Server; } // tolua_export + cWorld * GetDefaultWorld(void); // tolua_export + cWorld * GetWorld(const AString & a_WorldName); // tolua_export + + /// Calls the callback for each world; returns true if the callback didn't abort (return true) + bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << + + /// Writes chunkstats, for each world and totals, to the output callback + void LogChunkStats(cCommandOutputCallback & a_Output); + + int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export + void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export + + cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } + + cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export + cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export + cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature + + /// Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel + static int GetFurnaceFuelBurnTime(const cItem & a_Fuel); // tolua_export + + cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export + cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export + cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } + + /** Queues a console command for execution through the cServer class. + The command will be executed in the tick thread + The command's output will be written to the a_Output callback + "stop" and "restart" commands have special handling. + */ + void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + + /** Queues a console command for execution through the cServer class. + The command will be executed in the tick thread + The command's output will be sent to console + "stop" and "restart" commands have special handling. + */ + void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export + + /// Executes a console command through the cServer class; does special handling for "stop" and "restart". + void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + + /// Kicks the user, no matter in what world they are. Used from cAuthenticator + void KickUser(int a_ClientID, const AString & a_Reason); + + /// Called by cAuthenticator to auth the specified user + void AuthenticateUser(int a_ClientID); + + /// Executes commands queued in the command queue + void TickCommands(void); + + /// Returns the number of chunks loaded + int GetTotalChunkCount(void); // tolua_export + + /// Saves all chunks in all worlds + void SaveAllChunks(void); // tolua_export + + /// Sends a chat message to all connected clients (in all worlds) + void BroadcastChat(const AString & a_Message); // tolua_export + + /// Calls the callback for each player in all worlds + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// 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); + + /// 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 + { + public: + cCommand(const AString & a_Command, cCommandOutputCallback * a_Output) : + m_Command(a_Command), + m_Output(a_Output) + { + } + + AString m_Command; + cCommandOutputCallback * m_Output; + } ; + + typedef std::map<AString, cWorld *> WorldMap; + typedef std::vector<cCommand> cCommandQueue; + + /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported. + int m_PrimaryServerVersion; + + cWorld * m_pDefaultWorld; + WorldMap m_WorldsByName; + + cCriticalSection m_CSPendingCommands; + cCommandQueue m_PendingCommands; + + cThread * m_InputThread; + + cServer * m_Server; + cMonsterConfig * m_MonsterConfig; + + cGroupManager * m_GroupManager; + cCraftingRecipes * m_CraftingRecipes; + cFurnaceRecipe * m_FurnaceRecipe; + cWebAdmin * m_WebAdmin; + cPluginManager * m_PluginManager; + cAuthenticator m_Authenticator; + cHTTPServer m_HTTPServer; + + cMCLogger * m_Log; + + bool m_bStop; + bool m_bRestart; + + void LoadGlobalSettings(); + + /// Loads the worlds from settings.ini, creates the worldmap + void LoadWorlds(cIniFile & IniFile); + + /// Starts each world's life + void StartWorlds(void); + + /// Stops each world's threads, so that it's safe to unload them + void StopWorlds(void); + + /// Unloads all worlds from memory + void UnloadWorlds(void); + + /// Does the actual work of executing a command + void DoExecuteConsoleCommand(const AString & a_Cmd); + + static void InputThread(void* a_Params); + + static cRoot* s_Root; +}; // tolua_export + + + + diff --git a/src/Server.cpp b/src/Server.cpp new file mode 100644 index 000000000..5f0e007d7 --- /dev/null +++ b/src/Server.cpp @@ -0,0 +1,707 @@ +// ReDucTor is an awesome guy who helped me a lot + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Server.h" +#include "ClientHandle.h" +#include "OSSupport/Timer.h" +#include "Mobs/Monster.h" +#include "OSSupport/Socket.h" +#include "Root.h" +#include "World.h" +#include "ChunkDef.h" +#include "PluginManager.h" +#include "GroupManager.h" +#include "ChatColor.h" +#include "Entities/Player.h" +#include "Inventory.h" +#include "Item.h" +#include "FurnaceRecipe.h" +#include "WebAdmin.h" +#include "Protocol/ProtocolRecognizer.h" +#include "CommandOutput.h" + +#include "MersenneTwister.h" + +#include "lib/iniFile/iniFile.h" +#include "Vector3f.h" + +#include <fstream> +#include <sstream> +#include <iostream> + +extern "C" { + #include "lib/zlib/zlib.h" +} + + + + +// For the "dumpmem" server command: +/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory +#define ENABLE_LEAK_FINDER + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + #pragma warning(push) + #pragma warning(disable:4100) + #include "LeakFinder.h" + #pragma warning(pop) +#endif + + + + + +typedef std::list< cClientHandle* > ClientList; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cServer::cTickThread: + +cServer::cTickThread::cTickThread(cServer & a_Server) : + super("ServerTickThread"), + m_Server(a_Server) +{ +} + + + + + +void cServer::cTickThread::Execute(void) +{ + cTimer Timer; + + long long msPerTick = 50; + long long LastTime = Timer.GetNowTime(); + + while (!m_ShouldTerminate) + { + long long NowTime = Timer.GetNowTime(); + float DeltaTime = (float)(NowTime-LastTime); + m_ShouldTerminate = !m_Server.Tick(DeltaTime); + long long TickTime = Timer.GetNowTime() - NowTime; + + if (TickTime < msPerTick) + { + // Stretch tick time until it's at least msPerTick + cSleep::MilliSleep((unsigned int)(msPerTick - TickTime)); + } + + LastTime = NowTime; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cServer: + +cServer::cServer(void) : + m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"), + m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"), + m_bIsConnected(false), + m_bRestarting(false), + m_RCONServer(*this), + m_TickThread(*this) +{ +} + + + + + +void cServer::ClientDestroying(const cClientHandle * a_Client) +{ + m_SocketThreads.StopReading(a_Client); +} + + + + + +void cServer::NotifyClientWrite(const cClientHandle * a_Client) +{ + m_NotifyWriteThread.NotifyClientWrite(a_Client); +} + + + + + +void cServer::WriteToClient(const cClientHandle * a_Client, const AString & a_Data) +{ + m_SocketThreads.Write(a_Client, a_Data); +} + + + + + +void cServer::QueueClientClose(const cClientHandle * a_Client) +{ + m_SocketThreads.QueueClose(a_Client); +} + + + + + +void cServer::RemoveClient(const cClientHandle * a_Client) +{ + m_SocketThreads.RemoveClient(a_Client); +} + + + + + +void cServer::ClientMovedToWorld(const cClientHandle * a_Client) +{ + cCSLock Lock(m_CSClients); + m_ClientsToRemove.push_back(const_cast<cClientHandle *>(a_Client)); +} + + + + + +void cServer::PlayerCreated(const cPlayer * a_Player) +{ + // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread + cCSLock Lock(m_CSPlayerCountDiff); + m_PlayerCountDiff += 1; +} + + + + + +void cServer::PlayerDestroying(const cPlayer * a_Player) +{ + // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread + cCSLock Lock(m_CSPlayerCountDiff); + m_PlayerCountDiff -= 1; +} + + + + + +bool cServer::InitServer(cIniFile & a_SettingsIni) +{ + m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!").c_str(); + m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100); + m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false); + m_PlayerCount = 0; + m_PlayerCountDiff = 0; + + if (m_bIsConnected) + { + LOGERROR("ERROR: Trying to initialize server while server is already running!"); + return false; + } + + LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS); + LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS); + + if (cSocket::WSAStartup() != 0) // Only does anything on Windows, but whatever + { + LOGERROR("WSAStartup() != 0"); + return false; + } + + bool HasAnyPorts = false; + AString Ports = a_SettingsIni.GetValueSet("Server", "Port", "25565"); + m_ListenThreadIPv4.SetReuseAddr(true); + if (m_ListenThreadIPv4.Initialize(Ports)) + { + HasAnyPorts = true; + } + + Ports = a_SettingsIni.GetValueSet("Server", "PortsIPv6", "25565"); + m_ListenThreadIPv6.SetReuseAddr(true); + if (m_ListenThreadIPv6.Initialize(Ports)) + { + HasAnyPorts = true; + } + + if (!HasAnyPorts) + { + LOGERROR("Couldn't open any ports. Aborting the server"); + return false; + } + + m_RCONServer.Initialize(a_SettingsIni); + + m_bIsConnected = true; + + m_ServerID = "-"; + if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true)) + { + MTRand mtrand1; + unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000; + unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000; + std::ostringstream sid; + sid << std::hex << r1; + sid << std::hex << r2; + m_ServerID = sid.str(); + m_ServerID.resize(16, '0'); + } + + m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); + if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) + { + m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE; + LOGINFO("Setting default viewdistance to the minimum of %d", m_ClientViewDistance); + } + if (m_ClientViewDistance > cClientHandle::MAX_VIEW_DISTANCE) + { + m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE; + LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance); + } + + m_NotifyWriteThread.Start(this); + + PrepareKeys(); + + return true; +} + + + + + +int cServer::GetNumPlayers(void) +{ + cCSLock Lock(m_CSPlayerCount); + return m_PlayerCount; +} + + + + + +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? + + LOGD("Generating protocol encryption keypair..."); + + time_t CurTime = time(NULL); + CryptoPP::RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); + CryptoPP::RSA::PublicKey pk(m_PrivateKey); + m_PublicKey = pk; +} + + + + + +void cServer::OnConnectionAccepted(cSocket & a_Socket) +{ + if (!a_Socket.IsValid()) + { + return; + } + + const AString & ClientIP = a_Socket.GetIPString(); + if (ClientIP.empty()) + { + LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting."); + a_Socket.CloseSocket(); + return; + } + + LOGD("Client \"%s\" connected!", ClientIP.c_str()); + + cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance); + if (!m_SocketThreads.AddClient(a_Socket, NewHandle)) + { + // For some reason SocketThreads have rejected the handle, clean it up + LOGERROR("Client \"%s\" cannot be handled, server probably unstable", ClientIP.c_str()); + a_Socket.CloseSocket(); + delete NewHandle; + return; + } + + cCSLock Lock(m_CSClients); + m_Clients.push_back(NewHandle); +} + + + + + +bool cServer::Tick(float a_Dt) +{ + // Apply the queued playercount adjustments (postponed to avoid deadlocks) + int PlayerCountDiff = 0; + { + cCSLock Lock(m_CSPlayerCountDiff); + std::swap(PlayerCountDiff, m_PlayerCountDiff); + } + { + cCSLock Lock(m_CSPlayerCount); + m_PlayerCount += PlayerCountDiff; + } + + // Send the tick to the plugins, as well as let the plugin manager reload, if asked to (issue #102): + cPluginManager::Get()->Tick(a_Dt); + + // Let the Root process all the queued commands: + cRoot::Get()->TickCommands(); + + // Tick all clients not yet assigned to a world: + TickClients(a_Dt); + + if (!m_bRestarting) + { + return true; + } + else + { + m_bRestarting = false; + m_RestartEvent.Set(); + return false; + } +} + + + + + +void cServer::TickClients(float a_Dt) +{ + cClientHandleList RemoveClients; + { + cCSLock Lock(m_CSClients); + + // Remove clients that have moved to a world (the world will be ticking them from now on) + for (cClientHandleList::const_iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) + { + m_Clients.remove(*itr); + } // for itr - m_ClientsToRemove[] + m_ClientsToRemove.clear(); + + // Tick the remaining clients, take out those that have been destroyed into RemoveClients + for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) + { + if ((*itr)->IsDestroyed()) + { + // Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 ) + RemoveClients.push_back(*itr); + itr = m_Clients.erase(itr); + continue; + } + (*itr)->Tick(a_Dt); + ++itr; + } // for itr - m_Clients[] + } + + // Delete the clients that have been destroyed + for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) + { + delete *itr; + } // for itr - RemoveClients[] +} + + + + + +bool cServer::Start(void) +{ + if (!m_ListenThreadIPv4.Start()) + { + return false; + } + if (!m_ListenThreadIPv6.Start()) + { + return false; + } + if (!m_TickThread.Start()) + { + return false; + } + return true; +} + + + + + +bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd) +{ + return cRoot::Get()->GetPluginManager()->CallHookChat(a_Client.GetPlayer(), a_Cmd); +} + + + + + +void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) +{ + AStringVector split = StringSplit(a_Cmd, " "); + if (split.empty()) + { + return; + } + + // Special handling: "stop" and "restart" are built in + if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0)) + { + return; + } + + // "help" and "reload" are to be handled by MCS, so that they work no matter what + if (split[0] == "help") + { + PrintHelp(split, a_Output); + return; + } + if (split[0] == "reload") + { + cPluginManager::Get()->ReloadPlugins(); + return; + } + + // There is currently no way a plugin can do these (and probably won't ever be): + if (split[0].compare("chunkstats") == 0) + { + cRoot::Get()->LogChunkStats(a_Output); + a_Output.Finished(); + return; + } + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + if (split[0].compare("dumpmem") == 0) + { + LeakFinderXmlOutput Output("memdump.xml"); + DumpUsedMemory(&Output); + return; + } + + if (split[0].compare("killmem") == 0) + { + while (true) + { + new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS + } + } + #endif + + if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) + { + a_Output.Finished(); + return; + } + + a_Output.Out("Unknown command, type 'help' for all commands."); + a_Output.Finished(); +} + + + + + +void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +{ + typedef std::pair<AString, AString> AStringPair; + typedef std::vector<AStringPair> AStringPairs; + + class cCallback : + public cPluginManager::cCommandEnumCallback + { + public: + cCallback(void) : m_MaxLen(0) {} + + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override + { + if (!a_HelpString.empty()) + { + m_Commands.push_back(AStringPair(a_Command, a_HelpString)); + if (m_MaxLen < a_Command.length()) + { + m_MaxLen = a_Command.length(); + } + } + return false; + } + + AStringPairs m_Commands; + size_t m_MaxLen; + } Callback; + cPluginManager::Get()->ForEachConsoleCommand(Callback); + std::sort(Callback.m_Commands.begin(), Callback.m_Commands.end()); + for (AStringPairs::const_iterator itr = Callback.m_Commands.begin(), end = Callback.m_Commands.end(); itr != end; ++itr) + { + const AStringPair & cmd = *itr; + a_Output.Out(Printf("%-*s%s\n", Callback.m_MaxLen, cmd.first.c_str(), cmd.second.c_str())); + } // for itr - Callback.m_Commands[] + a_Output.Finished(); +} + + + + + +void cServer::BindBuiltInConsoleCommands(void) +{ + cPluginManager * PlgMgr = cPluginManager::Get(); + PlgMgr->BindConsoleCommand("help", NULL, " - Shows the available commands"); + PlgMgr->BindConsoleCommand("reload", NULL, " - Reloads all plugins"); + PlgMgr->BindConsoleCommand("restart", NULL, " - Restarts the server cleanly"); + PlgMgr->BindConsoleCommand("stop", NULL, " - Stops the server cleanly"); + PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics"); + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); + #endif +} + + + + + +void cServer::Shutdown(void) +{ + m_ListenThreadIPv4.Stop(); + m_ListenThreadIPv6.Stop(); + + m_bRestarting = true; + m_RestartEvent.Wait(); + + cRoot::Get()->SaveAllChunks(); + + cCSLock Lock(m_CSClients); + for( ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr ) + { + (*itr)->Destroy(); + delete *itr; + } + m_Clients.clear(); +} + + + + + +void cServer::KickUser(int a_ClientID, const AString & a_Reason) +{ + cCSLock Lock(m_CSClients); + for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + if ((*itr)->GetUniqueID() == a_ClientID) + { + (*itr)->Kick(a_Reason); + } + } // for itr - m_Clients[] +} + + + + + +void cServer::AuthenticateUser(int a_ClientID) +{ + cCSLock Lock(m_CSClients); + for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + if ((*itr)->GetUniqueID() == a_ClientID) + { + (*itr)->Authenticate(); + return; + } + } // for itr - m_Clients[] +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cServer::cNotifyWriteThread: + +cServer::cNotifyWriteThread::cNotifyWriteThread(void) : + super("ClientPacketThread"), + m_Server(NULL) +{ +} + + + + + +cServer::cNotifyWriteThread::~cNotifyWriteThread() +{ + m_ShouldTerminate = true; + m_Event.Set(); + Wait(); +} + + + + + +bool cServer::cNotifyWriteThread::Start(cServer * a_Server) +{ + m_Server = a_Server; + return super::Start(); +} + + + + + +void cServer::cNotifyWriteThread::Execute(void) +{ + cClientHandleList Clients; + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_Clients.size() == 0) + { + cCSUnlock Unlock(Lock); + m_Event.Wait(); + if (m_ShouldTerminate) + { + return; + } + } + + // Copy the clients to notify and unlock the CS: + Clients.splice(Clients.begin(), m_Clients); + Lock.Unlock(); + + for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr) + { + m_Server->m_SocketThreads.NotifyWrite(*itr); + } // for itr - Clients[] + Clients.clear(); + } // while (!mShouldTerminate) +} + + + + + +void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client) +{ + { + cCSLock Lock(m_CS); + m_Clients.remove(const_cast<cClientHandle *>(a_Client)); // Put it there only once + m_Clients.push_back(const_cast<cClientHandle *>(a_Client)); + } + m_Event.Set(); +} + + + + diff --git a/src/Server.h b/src/Server.h new file mode 100644 index 000000000..0d93469a5 --- /dev/null +++ b/src/Server.h @@ -0,0 +1,195 @@ + +// cServer.h + +// Interfaces to the cServer object representing the network server + + + + + +#pragma once + +#include "OSSupport/SocketThreads.h" +#include "OSSupport/ListenThread.h" +#include "cryptopp/rsa.h" +#include "cryptopp/randpool.h" +#include "RCONServer.h" + + + + + +// fwd: +class cPlayer; +class cClientHandle; +class cIniFile; +class cCommandOutputCallback ; + +typedef std::list<cClientHandle *> cClientHandleList; + + + + + +class cServer // tolua_export + : public cListenThread::cCallback +{ // tolua_export +public: // tolua_export + bool InitServer(cIniFile & a_SettingsIni); + + // tolua_begin + + const AString & GetDescription(void) const {return m_Description; } + + // Player counts: + int GetMaxPlayers(void) const {return m_MaxPlayers; } + int GetNumPlayers(void); + void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; } + + // Hardcore mode or not: + bool IsHardcore(void) const {return m_bIsHardcore; } + + // tolua_end + + bool Start(void); + + bool Command(cClientHandle & a_Client, AString & a_Cmd); + + /// Executes the console command, sends output through the specified callback + void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + + /// Lists all available console commands and their helpstrings + void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output); + + /// Binds the built-in console commands with the plugin manager + static void BindBuiltInConsoleCommands(void); + + void Shutdown(void); + + void KickUser(int a_ClientID, const AString & a_Reason); + void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user + + const AString & GetServerID(void) const { return m_ServerID; } // tolua_export + + void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client + + void NotifyClientWrite(const cClientHandle * a_Client); // Notifies m_SocketThreads that client has something to be written + + void WriteToClient(const cClientHandle * a_Client, const AString & a_Data); // Queues outgoing data for the client through m_SocketThreads + + void QueueClientClose(const cClientHandle * a_Client); // Queues the clienthandle to close when all its outgoing data is sent + + void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads + + /// Don't tick a_Client anymore, it will be ticked from its cPlayer instead + void ClientMovedToWorld(const cClientHandle * a_Client); + + /// Notifies the server that a player was created; the server uses this to adjust the number of players + void PlayerCreated(const cPlayer * a_Player); + + /// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players + void PlayerDestroying(const cPlayer * a_Player); + + CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } + +private: + + friend class cRoot; // so cRoot can create and destroy cServer + + /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) + class cNotifyWriteThread : + public cIsThread + { + typedef cIsThread super; + + cEvent m_Event; // Set when m_Clients gets appended + cServer * m_Server; + + cCriticalSection m_CS; + cClientHandleList m_Clients; + + virtual void Execute(void); + + public: + + cNotifyWriteThread(void); + ~cNotifyWriteThread(); + + bool Start(cServer * a_Server); + + void NotifyClientWrite(const cClientHandle * a_Client); + } ; + + /// The server tick thread takes care of the players who aren't yet spawned in a world + class cTickThread : + public cIsThread + { + typedef cIsThread super; + + public: + cTickThread(cServer & a_Server); + + protected: + cServer & m_Server; + + // cIsThread overrides: + virtual void Execute(void) override; + } ; + + + cNotifyWriteThread m_NotifyWriteThread; + + cListenThread m_ListenThreadIPv4; + cListenThread m_ListenThreadIPv6; + + cCriticalSection m_CSClients; ///< Locks client lists + cClientHandleList m_Clients; ///< Clients that are connected to the server and not yet assigned to a cWorld + cClientHandleList m_ClientsToRemove; ///< Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick() + + cCriticalSection m_CSPlayerCount; ///< Locks the m_PlayerCount + int m_PlayerCount; ///< Number of players currently playing in the server + cCriticalSection m_CSPlayerCountDiff; ///< Locks the m_PlayerCountDiff + int m_PlayerCountDiff; ///< Adjustment to m_PlayerCount to be applied in the Tick thread + + cSocketThreads m_SocketThreads; + + int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini + + bool m_bIsConnected; // true - connected false - not connected + + bool m_bRestarting; + + CryptoPP::RSA::PrivateKey m_PrivateKey; + CryptoPP::RSA::PublicKey m_PublicKey; + + cRCONServer m_RCONServer; + + AString m_Description; + int m_MaxPlayers; + bool m_bIsHardcore; + + cTickThread m_TickThread; + cEvent m_RestartEvent; + + /// The server ID used for client authentication + AString m_ServerID; + + + cServer(void); + + /// Loads, or generates, if missing, RSA keys for protocol encryption + void PrepareKeys(void); + + bool Tick(float a_Dt); + + /// Ticks the clients in m_Clients, manages the list in respect to removing clients + void TickClients(float a_Dt); + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; +}; // tolua_export + + + + diff --git a/src/Simulator/DelayedFluidSimulator.cpp b/src/Simulator/DelayedFluidSimulator.cpp new file mode 100644 index 000000000..a4645ca09 --- /dev/null +++ b/src/Simulator/DelayedFluidSimulator.cpp @@ -0,0 +1,158 @@ + +// DelayedFluidSimulator.cpp + +// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay +// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot. + +#include "Globals.h" + +#include "DelayedFluidSimulator.h" +#include "../World.h" +#include "../Chunk.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cDelayedFluidSimulatorChunkData::cSlot + +bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ) +{ + ASSERT(a_RelZ >= 0); + ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks)); + + cCoordWithIntVector & Blocks = m_Blocks[a_RelZ]; + int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + for (cCoordWithIntVector::const_iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr) + { + if (itr->Data == Index) + { + // Already present + return false; + } + } // for itr - Blocks[] + Blocks.push_back(cCoordWithInt(a_RelX, a_RelY, a_RelZ, Index)); + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cDelayedFluidSimulatorChunkData: + +cDelayedFluidSimulatorChunkData::cDelayedFluidSimulatorChunkData(int a_TickDelay) : + m_Slots(new cSlot[a_TickDelay]) +{ +} + + + + + +cDelayedFluidSimulatorChunkData::~cDelayedFluidSimulatorChunkData() +{ + delete[] m_Slots; + m_Slots = NULL; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cDelayedFluidSimulator: + +cDelayedFluidSimulator::cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay) : + super(a_World, a_Fluid, a_StationaryFluid), + m_TickDelay(a_TickDelay), + m_AddSlotNum(a_TickDelay - 1), + m_SimSlotNum(0), + m_TotalBlocks(0) +{ +} + + + + + +void cDelayedFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1) + return; + } + + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ); + if (BlockType != m_FluidBlock) + { + return; + } + + void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData(); + cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw; + cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_AddSlotNum]; + + // Add, if not already present: + if (!Slot.Add(RelX, a_BlockY, RelZ)) + { + return; + } + + ++m_TotalBlocks; +} + + + + + +void cDelayedFluidSimulator::Simulate(float a_Dt) +{ + m_AddSlotNum = m_SimSlotNum; + m_SimSlotNum += 1; + if (m_SimSlotNum >= m_TickDelay) + { + m_SimSlotNum = 0; + } +} + + + + + +void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData(); + cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw; + cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_SimSlotNum]; + + // Simulate all the blocks in the scheduled slot: + for (int i = 0; i < ARRAYCOUNT(Slot.m_Blocks); i++) + { + cCoordWithIntVector & Blocks = Slot.m_Blocks[i]; + if (Blocks.empty()) + { + continue; + } + for (cCoordWithIntVector::iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr) + { + SimulateBlock(a_Chunk, itr->x, itr->y, itr->z); + } + m_TotalBlocks -= Blocks.size(); + Blocks.clear(); + } +} + + + + diff --git a/src/Simulator/DelayedFluidSimulator.h b/src/Simulator/DelayedFluidSimulator.h new file mode 100644 index 000000000..c81500741 --- /dev/null +++ b/src/Simulator/DelayedFluidSimulator.h @@ -0,0 +1,82 @@ + +// DelayedFluidSimulator.h + +// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay +// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot. + + + + +#pragma once + +#include "FluidSimulator.h" + + + + + +class cDelayedFluidSimulatorChunkData : + public cFluidSimulatorData +{ +public: + class cSlot + { + public: + /// Returns true if the specified block is stored + bool HasBlock(int a_RelX, int a_RelY, int a_RelZ); + + /// Adds the specified block unless already present; returns true if added, false if the block was already present + bool Add(int a_RelX, int a_RelY, int a_RelZ); + + /** Array of block containers, each item stores blocks for one Z coord + Int param is the block index (for faster duplicate comparison in Add()) + */ + cCoordWithIntVector m_Blocks[16]; + } ; + + cDelayedFluidSimulatorChunkData(int a_TickDelay); + virtual ~cDelayedFluidSimulatorChunkData(); + + /// Slots, one for each delay tick, each containing the blocks to simulate + cSlot * m_Slots; +} ; + + + + + +class cDelayedFluidSimulator : + public cFluidSimulator +{ + typedef cFluidSimulator super; + +public: + cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay); + + // cSimulator overrides: + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + virtual void Simulate(float a_Dt) override; + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); } + +protected: + + int m_TickDelay; // Count of the m_Slots array in each ChunkData + int m_AddSlotNum; // Index into m_Slots[] where to add new blocks in each ChunkData + int m_SimSlotNum; // Index into m_Slots[] where to simulate blocks in each ChunkData + + int m_TotalBlocks; // Statistics only: the total number of blocks currently queued + + /* + Slots: + | 0 | 1 | ... | m_AddSlotNum | m_SimSlotNum | ... | m_TickDelay - 1 | + adding blocks here ^ | ^ simulating here + */ + + /// Called from SimulateChunk() to simulate each block in one slot of blocks. Descendants override this method to provide custom simulation. + virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) = 0; +} ; + + + + diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp new file mode 100644 index 000000000..ac3fb9695 --- /dev/null +++ b/src/Simulator/FireSimulator.cpp @@ -0,0 +1,374 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FireSimulator.h" +#include "../World.h" +#include "../BlockID.h" +#include "../Defines.h" +#include "../Chunk.h" + + + + + +// Easy switch for turning on debugging logging: +#if 0 + #define FLOG LOGD +#else + #define FLOG(...) +#endif + + + + + +#define MAX_CHANCE_REPLACE_FUEL 100000 +#define MAX_CHANCE_FLAMMABILITY 100000 + + + + + +static const struct +{ + int x, y, z; +} gCrossCoords[] = +{ + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, +} ; + + + + + +static const struct +{ + int x, y, z; +} gNeighborCoords[] = +{ + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 1, 0}, + { 0, -1, 0}, + { 0, 0, 1}, + { 0, 0, -1}, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFireSimulator: + +cFireSimulator::cFireSimulator(cWorld & a_World, cIniFile & a_IniFile) : + cSimulator(a_World) +{ + // Read params from the ini file: + m_BurnStepTimeFuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeFuel", 500); + m_BurnStepTimeNonfuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeNonfuel", 100); + m_Flammability = a_IniFile.GetValueSetI("FireSimulator", "Flammability", 50); + m_ReplaceFuelChance = a_IniFile.GetValueSetI("FireSimulator", "ReplaceFuelChance", 50000); +} + + + + + +cFireSimulator::~cFireSimulator() +{ +} + + + + + +void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData(); + + int NumMSecs = (int)a_Dt; + for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) + { + int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z); + BLOCKTYPE BlockType = a_Chunk->GetBlock(idx); + + if (!IsAllowedBlock(BlockType)) + { + // The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire) + FLOG("FS: Removing block {%d, %d, %d}", + itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width + ); + itr = Data.erase(itr); + continue; + } + + // Try to spread the fire: + TrySpreadFire(a_Chunk, itr->x, itr->y, itr->z); + + itr->Data -= NumMSecs; + if (itr->Data >= 0) + { + // Not yet, wait for it longer + ++itr; + continue; + } + + // Burn out the fire one step by increasing the meta: + /* + FLOG("FS: Fire at {%d, %d, %d} is stepping", + itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width + ); + */ + NIBBLETYPE BlockMeta = a_Chunk->GetMeta(idx); + if (BlockMeta == 0x0f) + { + // The fire burnt out completely + FLOG("FS: Fire at {%d, %d, %d} burnt out, removing the fire block", + itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width + ); + a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0); + RemoveFuelNeighbors(a_Chunk, itr->x, itr->y, itr->z); + itr = Data.erase(itr); + continue; + } + a_Chunk->SetMeta(idx, BlockMeta + 1); + itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z); // TODO: Add some randomness into this + } // for itr - Data[] +} + + + + + +bool cFireSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType) +{ + return (a_BlockType == E_BLOCK_FIRE); +} + + + + + +bool cFireSimulator::IsFuel(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_PLANKS: + case E_BLOCK_LEAVES: + case E_BLOCK_LOG: + case E_BLOCK_WOOL: + case E_BLOCK_BOOKCASE: + case E_BLOCK_FENCE: + case E_BLOCK_TNT: + case E_BLOCK_VINES: + { + return true; + } + } + return false; +} + + + + + +bool cFireSimulator::IsForever(BLOCKTYPE a_BlockType) +{ + return (a_BlockType == E_BLOCK_NETHERRACK); +} + + + + + +void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ); + if (!IsAllowedBlock(BlockType)) + { + return; + } + + // Check for duplicates: + cFireSimulatorChunkData & ChunkData = a_Chunk->GetFireSimulatorData(); + for (cCoordWithIntList::iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr) + { + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) + { + // Already present, skip adding + return; + } + } // for itr - ChunkData[] + + FLOG("FS: Adding block {%d, %d, %d}", a_BlockX, a_BlockY, a_BlockZ); + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ, 100)); +} + + + + + +int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + bool IsBlockBelowSolid = false; + if (a_RelY > 0) + { + BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + if (IsForever(BlockBelow)) + { + // Is burning atop of netherrack, burn forever (re-check in 10 sec) + return 10000; + } + if (IsFuel(BlockBelow)) + { + return m_BurnStepTimeFuel; + } + IsBlockBelowSolid = g_BlockIsSolid[BlockBelow]; + } + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (a_Chunk->UnboundedRelGetBlock(a_RelX + gCrossCoords[i].x, a_RelY, a_RelZ + gCrossCoords[i].z, BlockType, BlockMeta)) + { + if (IsFuel(BlockType)) + { + return m_BurnStepTimeFuel; + } + } + } // for i - gCrossCoords[] + + if (!IsBlockBelowSolid && (a_RelY >= 0)) + { + // Checked through everything, nothing was flammable + // If block below isn't solid, we can't have fire, it would be a non-fueled fire + // SetBlock just to make sure fire doesn't spawn + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return 0; + } + return m_BurnStepTimeNonfuel; +} + + + + + +void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + /* + if (m_World.GetTickRandomNumber(10000) > 100) + { + // Make the chance to spread 100x smaller + return; + } + */ + + for (int x = a_RelX - 1; x <= a_RelX + 1; x++) + { + for (int z = a_RelZ - 1; z <= a_RelZ + 1; z++) + { + for (int y = a_RelY - 1; y <= a_RelY + 2; y++) // flames spread up one more block than around + { + // No need to check the coords for equality with the parent block, + // it cannot catch fire anyway (because it's not an air block) + + if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability) + { + continue; + } + + // Start the fire in the neighbor {x, y, z} + /* + FLOG("FS: Trying to start fire at {%d, %d, %d}.", + x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width + ); + */ + if (CanStartFireInBlock(a_Chunk, x, y, z)) + { + FLOG("FS: Starting new fire at {%d, %d, %d}.", + x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width + ); + a_Chunk->UnboundedRelSetBlock(x, y, z, E_BLOCK_FIRE, 0); + } + } // for y + } // for z + } // for x +} + + + + + +void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta)) + { + // Neighbor not accessible, ignore it + continue; + } + if (!IsFuel(BlockType)) + { + continue; + } + bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance); + a_Chunk->UnboundedRelSetBlock( + a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, + ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0 + ); + } // for i - Coords[] +} + + + + + +bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta)) + { + // The chunk is not accessible + return false; + } + + if (BlockType != E_BLOCK_AIR) + { + // Only an air block can be replaced by a fire block + return false; + } + + for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++) + { + if (!a_NearChunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta)) + { + // Neighbor inaccessible, skip it while evaluating + continue; + } + if (IsFuel(BlockType)) + { + return true; + } + } // for i - Coords[] + return false; +} + + + + diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h new file mode 100644 index 000000000..0d8a548ef --- /dev/null +++ b/src/Simulator/FireSimulator.h @@ -0,0 +1,75 @@ + +#pragma once + +#include "Simulator.h" +#include "../BlockEntities/BlockEntity.h" + + + + + +/** The fire simulator takes care of the fire blocks. +It periodically increases their meta ("steps") until they "burn out"; it also supports the forever burning netherrack. +Each individual fire block gets stored in per-chunk data; that list is then used for fast retrieval. +The data value associated with each coord is used as the number of msec that the fire takes until +it progresses to the next step (blockmeta++). This value is updated if a neighbor is changed. +The simulator reads its parameters from the ini file given to the constructor. +*/ +class cFireSimulator : + public cSimulator +{ +public: + cFireSimulator(cWorld & a_World, cIniFile & a_IniFile); + ~cFireSimulator(); + + virtual void Simulate(float a_Dt) override {} // not used + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; + + bool IsFuel (BLOCKTYPE a_BlockType); + bool IsForever(BLOCKTYPE a_BlockType); + +protected: + /// Time (in msec) that a fire block takes to burn with a fuel block into the next step + unsigned m_BurnStepTimeFuel; + + /// Time (in msec) that a fire block takes to burn without a fuel block into the next step + unsigned m_BurnStepTimeNonfuel; + + /// Chance [0..100000] of an adjacent fuel to catch fire on each tick + int m_Flammability; + + /// Chance [0..100000] of a fuel burning out being replaced by a new fire block instead of an air block + int m_ReplaceFuelChance; + + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + + /// Returns the time [msec] after which the specified fire block is stepped again; based on surrounding fuels + int GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + + /// Tries to spread fire to a neighborhood of the specified block + void TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + + /// Removes all burnable blocks neighboring the specified block + void RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + + /** Returns true if a fire can be started in the specified block, + that is, it is an air block and has fuel next to it. + Note that a_NearChunk may be a chunk neighbor to the block specified! + The coords are relative to a_NearChunk but not necessarily in it. + */ + bool CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ); +} ; + + + + + +/// Stores individual fire blocks in the chunk; the int data is used as the time [msec] the fire takes to step to another stage (blockmeta++) +typedef cCoordWithIntList cFireSimulatorChunkData; + + + + diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp new file mode 100644 index 000000000..d204a1f8b --- /dev/null +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -0,0 +1,330 @@ + +// FloodyFluidSimulator.cpp + +// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :) +// http://forum.mc-server.org/showthread.php?tid=565 + +#include "Globals.h" + +#include "FloodyFluidSimulator.h" +#include "../World.h" +#include "../Chunk.h" +#include "../BlockArea.h" +#include "../Blocks/BlockHandler.h" + + + + + +// Enable or disable detailed logging +#if 0 + #define FLOG LOGD +#else + #define FLOG(...) +#endif + + + + + +cFloodyFluidSimulator::cFloodyFluidSimulator( + cWorld & a_World, + BLOCKTYPE a_Fluid, + BLOCKTYPE a_StationaryFluid, + NIBBLETYPE a_Falloff, + int a_TickDelay, + int a_NumNeighborsForSource +) : + super(a_World, a_Fluid, a_StationaryFluid, a_TickDelay), + m_Falloff(a_Falloff), + m_NumNeighborsForSource(a_NumNeighborsForSource) +{ +} + + + + + +void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + FLOG("Simulating block {%d, %d, %d}: block %d, meta %d", + a_Chunk->GetPosX() * cChunkDef::Width + a_RelX, a_RelY, a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ, + a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ), + a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ) + ); + + NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); + if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ))) + { + // Can happen - if a block is scheduled for simulating and gets replaced in the meantime. + FLOG(" BadBlockType exit"); + return; + } + + if (MyMeta != 0) + { + // Source blocks aren't checked for tributaries, others are. + if (CheckTributaries(a_Chunk, a_RelX, a_RelY, a_RelZ, MyMeta)) + { + // Has no tributary, has been decreased (in CheckTributaries()), + // no more processing needed (neighbors have been scheduled by the decrease) + FLOG(" CheckTributaries exit"); + return; + } + } + + // New meta for the spreading to neighbors: + // If this is a source block or was falling, the new meta is just the falloff + // Otherwise it is the current meta plus falloff (may be larger than max height, will be checked later) + NIBBLETYPE NewMeta = ((MyMeta == 0) || ((MyMeta & 0x08) != 0)) ? m_Falloff : (MyMeta + m_Falloff); + bool SpreadFurther = true; + if (a_RelY > 0) + { + BLOCKTYPE Below = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ); + if (IsPassableForFluid(Below) || IsBlockLava(Below) || IsBlockWater(Below)) + { + // Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian: + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8); + SpreadFurther = false; + } + // If source creation is on, check for it here: + else if ( + (m_NumNeighborsForSource > 0) && // Source creation is on + (MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out) + !IsPassableForFluid(Below) && // Only exactly 1 block deep + CheckNeighborsForSource(a_Chunk, a_RelX, a_RelY, a_RelZ) // Did we create a source? + ) + { + // We created a source, no more spreading is to be done now + // Also has been re-scheduled for ticking in the next wave, so no marking is needed + return; + } + } + + if (SpreadFurther && (NewMeta < 8)) + { + // Spread to the neighbors: + SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta); + SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta); + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta); + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta); + } + + // Mark as processed: + a_Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, MyMeta); +} + + + + + +bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta) +{ + // If we have a section above, check if there's fluid above this block that would feed it: + if (a_RelY < cChunkDef::Height - 1) + { + if (IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ))) + { + // This block is fed from above, no more processing needed + FLOG(" Fed from above"); + return false; + } + } + + // Not fed from above, check if there's a feed from the side (but not if it's a downward-flowing block): + if (a_MyMeta != 8) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + static const Vector3i Coords[] = + { + Vector3i( 1, 0, 0), + Vector3i(-1, 0, 0), + Vector3i( 0, 0, 1), + Vector3i( 0, 0, -1), + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + { + continue; + } + if (IsAllowedBlock(BlockType) && IsHigherMeta(BlockMeta, a_MyMeta)) + { + // This block is fed, no more processing needed + FLOG(" Fed from {%d, %d, %d}, type %d, meta %d", + a_Chunk->GetPosX() * cChunkDef::Width + a_RelX + Coords[i].x, + a_RelY, + a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ + Coords[i].z, + BlockType, BlockMeta + ); + return false; + } + } // for i - Coords[] + } // if not fed from above + + // Block is not fed, decrease by m_Falloff levels: + if (a_MyMeta >= 8) + { + FLOG(" Not fed and downwards, turning into non-downwards meta %d", m_Falloff); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, m_Falloff); + } + else + { + a_MyMeta += m_Falloff; + if (a_MyMeta < 8) + { + FLOG(" Not fed, decreasing from %d to %d", a_MyMeta - m_Falloff, a_MyMeta); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, a_MyMeta); + } + else + { + FLOG(" Not fed, meta %d, erasing altogether", a_MyMeta); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + } + } + return true; +} + + + + + +void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) +{ + ASSERT(a_NewMeta <= 8); // Invalid meta values + ASSERT(a_NewMeta > 0); // Source blocks aren't spread + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta)) + { + // Chunk not available + return; + } + + if (IsAllowedBlock(BlockType)) + { + if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta)) + { + // Don't spread there, there's already a higher or same level there + return; + } + } + + // Check water - lava interaction: + if (m_FluidBlock == E_BLOCK_LAVA) + { + if (IsBlockWater(BlockType)) + { + // Lava flowing into water, change to stone / cobblestone based on direction: + BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE; + FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone", + a_RelX, a_RelY, a_RelZ, + ItemTypeToString(NewBlock).c_str() + ); + a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); + m_World.BroadcastSoundEffect("random.fizz", a_RelX * 8, a_RelY * 8, a_RelZ * 8, 0.5f, 1.5f); + return; + } + } + else if (m_FluidBlock == E_BLOCK_WATER) + { + if (IsBlockLava(BlockType)) + { + // Water flowing into lava, change to cobblestone / obsidian based on dest block: + BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE; + FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s", + a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() + ); + a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); + m_World.BroadcastSoundEffect("random.fizz", a_RelX * 8, a_RelY * 8, a_RelZ * 8, 0.5f, 1.5f); + return; + } + } + else + { + ASSERT(!"Unknown fluid!"); + } + + if (!IsPassableForFluid(BlockType)) + { + // Can't spread there + return; + } + + // Wash away the block there, if possible: + if (CanWashAway(BlockType)) + { + cBlockHandler * Handler = BlockHandler(BlockType); + if (Handler->DoesDropOnUnsuitable()) + { + Handler->DropBlock( + &m_World, NULL, + a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX, + a_RelY, + a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ + ); + } + } // if (CanWashAway) + + // Spread: + FLOG(" Spreading to {%d, %d, %d} with meta %d", + a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX, + a_RelY, + a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ, + a_NewMeta + ); + a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); +} + + + + + +bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + FLOG(" Checking neighbors for source creation"); + + static const Vector3i NeighborCoords[] = + { + Vector3i(-1, 0, 0), + Vector3i( 1, 0, 0), + Vector3i( 0, 0, -1), + Vector3i( 0, 0, 1), + } ; + + int NumNeeded = m_NumNeighborsForSource; + for (int i = 0; i < ARRAYCOUNT(NeighborCoords); i++) + { + int x = a_RelX + NeighborCoords[i].x; + int y = a_RelY + NeighborCoords[i].y; + int z = a_RelZ + NeighborCoords[i].z; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk->UnboundedRelGetBlock(x, y, z, BlockType, BlockMeta)) + { + // Neighbor not available, skip it + continue; + } + // FLOG(" Neighbor at {%d, %d, %d}: %s", x, y, z, ItemToFullString(cItem(BlockType, 1, BlockMeta)).c_str()); + if ((BlockMeta == 0) && IsAnyFluidBlock(BlockType)) + { + NumNeeded--; + // FLOG(" Found a neighbor source at {%d, %d, %d}, NumNeeded := %d", x, y, z, NumNeeded); + if (NumNeeded == 0) + { + // Found enough, turn into a source and bail out + // FLOG(" Found enough neighbor sources, turning into a source"); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, 0); + return true; + } + } + } + // FLOG(" Not enough neighbors for turning into a source, NumNeeded = %d", NumNeeded); + return false; +} + + + + diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h new file mode 100644 index 000000000..c4af2e246 --- /dev/null +++ b/src/Simulator/FloodyFluidSimulator.h @@ -0,0 +1,53 @@ + +// FloodyFluidSimulator.h + +// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :) +// http://forum.mc-server.org/showthread.php?tid=565 + + + + + +#pragma once + +#include "DelayedFluidSimulator.h" + + + + + +// fwd: +class cBlockArea; + + + + + +class cFloodyFluidSimulator : + public cDelayedFluidSimulator +{ + typedef cDelayedFluidSimulator super; + +public: + cFloodyFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource); + +protected: + NIBBLETYPE m_Falloff; + int m_NumNeighborsForSource; + + // cDelayedFluidSimulator overrides: + virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override; + + /// Checks tributaries, if not fed, decreases the block's level and returns true + bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta); + + /// Spreads into the specified block, if the blocktype there allows. a_Area is for checking. + void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta); + + /// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so + bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); +} ; + + + + diff --git a/src/Simulator/FluidSimulator.cpp b/src/Simulator/FluidSimulator.cpp new file mode 100644 index 000000000..dac666484 --- /dev/null +++ b/src/Simulator/FluidSimulator.cpp @@ -0,0 +1,212 @@ + +#include "Globals.h" + +#include "FluidSimulator.h" +#include "../World.h" + + + + + +cFluidSimulator::cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) : + super(a_World), + m_FluidBlock(a_Fluid), + m_StationaryFluidBlock(a_StationaryFluid) +{ +} + + + + + +bool cFluidSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType) +{ + return ((a_BlockType == m_FluidBlock) || (a_BlockType == m_StationaryFluidBlock)); +} + + + + + +bool cFluidSimulator::CanWashAway(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_BROWN_MUSHROOM: + case E_BLOCK_CACTUS: + case E_BLOCK_COBWEB: + case E_BLOCK_CROPS: + case E_BLOCK_DEAD_BUSH: + case E_BLOCK_RAIL: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_RED_MUSHROOM: + case E_BLOCK_RED_ROSE: + case E_BLOCK_SNOW: + case E_BLOCK_SUGARCANE: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_TORCH: + case E_BLOCK_YELLOW_FLOWER: + { + return true; + } + default: + { + return false; + } + } +} + + + + + +bool cFluidSimulator::IsSolidBlock(BLOCKTYPE a_BlockType) +{ + return !IsPassableForFluid(a_BlockType); +} + + + + + +bool cFluidSimulator::IsPassableForFluid(BLOCKTYPE a_BlockType) +{ + return ( + (a_BlockType == E_BLOCK_AIR) || + (a_BlockType == E_BLOCK_FIRE) || + IsAllowedBlock(a_BlockType) || + CanWashAway(a_BlockType) + ); +} + + + + + +bool cFluidSimulator::IsHigherMeta(NIBBLETYPE a_Meta1, NIBBLETYPE a_Meta2) +{ + if (a_Meta1 == 0) + { + // Source block is higher than anything, even itself. + return true; + } + if ((a_Meta1 & 0x08) != 0) + { + // Falling fluid is higher than anything, including self + return true; + } + + if (a_Meta2 == 0) + { + // Second block is a source and first block isn't + return false; + } + if ((a_Meta2 & 0x08) != 0) + { + // Second block is falling and the first one is neither a source nor falling + return false; + } + + // All special cases have been handled, now it's just a raw comparison: + return (a_Meta1 < a_Meta2); +} + + + + + +// TODO Not working very well yet :s +Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over) +{ + if ((a_Y < 0) || (a_Y >= cChunkDef::Height)) + { + return NONE; + } + BLOCKTYPE BlockID = m_World.GetBlock(a_X, a_Y, a_Z); + if (!IsAllowedBlock(BlockID)) // No Fluid -> No Flowing direction :D + { + return NONE; + } + + /* + Disabled because of causing problems and being useless atm + char BlockBelow = m_World.GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or fluid below it -> dominating flow is down :D + if (BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow)) + return Y_MINUS; + */ + + NIBBLETYPE LowestPoint = m_World.GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted + int X = 0, Y = 0, Z = 0; //Lowest Pos will be stored here + + if (IsAllowedBlock(m_World.GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction + { + return GetFlowingDirection(a_X, a_Y + 1, a_Z, false); + } + + std::vector< Vector3i * > Points; + + Points.reserve(4); //Already allocate 4 places :D + + //add blocks around the checking pos + Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z)); + Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z)); + Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1)); + Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1)); + + for (std::vector<Vector3i *>::iterator it = Points.begin(); it < Points.end(); it++) + { + Vector3i *Pos = (*it); + char BlockID = m_World.GetBlock(Pos->x, Pos->y, Pos->z); + if(IsAllowedBlock(BlockID)) + { + char Meta = m_World.GetBlockMeta(Pos->x, Pos->y, Pos->z); + + if(Meta > LowestPoint) + { + LowestPoint = Meta; + X = Pos->x; + Y = Pos->y; + Z = Pos->z; + } + }else if(BlockID == E_BLOCK_AIR) + { + LowestPoint = 9; //This always dominates + X = Pos->x; + Y = Pos->y; + Z = Pos->z; + + } + delete Pos; + } + + if (LowestPoint == m_World.GetBlockMeta(a_X, a_Y, a_Z)) + return NONE; + + if (a_X - X > 0) + { + return X_MINUS; + } + + if (a_X - X < 0) + { + return X_PLUS; + } + + if (a_Z - Z > 0) + { + return Z_MINUS; + } + + if (a_Z - Z < 0) + { + return Z_PLUS; + } + + return NONE; +} + + + + diff --git a/src/Simulator/FluidSimulator.h b/src/Simulator/FluidSimulator.h new file mode 100644 index 000000000..672b740a2 --- /dev/null +++ b/src/Simulator/FluidSimulator.h @@ -0,0 +1,75 @@ + +#pragma once + +#include "Simulator.h" + + + + + +enum Direction +{ + X_PLUS, + X_MINUS, + Y_PLUS, + Y_MINUS, + Z_PLUS, + Z_MINUS, + NONE +}; + + + + + +/** This is a base class for all fluid simulator data classes. +Needed so that cChunk can properly delete instances of fluid simulator data, no matter what simulator it's using +*/ +class cFluidSimulatorData +{ +public: + virtual ~cFluidSimulatorData() {} +} ; + + + + + +class cFluidSimulator : + public cSimulator +{ + typedef cSimulator super; + +public: + cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid); + + // cSimulator overrides: + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; + + /// Gets the flowing direction. If a_Over is true also the block over the current block affects the direction (standard) + virtual Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true); + + /// Creates a ChunkData object for the simulator to use. The simulator returns the correct object type. + virtual cFluidSimulatorData * CreateChunkData(void) { return NULL; } + + bool IsFluidBlock (BLOCKTYPE a_BlockType) const { return (a_BlockType == m_FluidBlock); } + bool IsStationaryFluidBlock(BLOCKTYPE a_BlockType) const { return (a_BlockType == m_StationaryFluidBlock); } + bool IsAnyFluidBlock (BLOCKTYPE a_BlockType) const { return ((a_BlockType == m_FluidBlock) || (a_BlockType == m_StationaryFluidBlock)); } + + static bool CanWashAway(BLOCKTYPE a_BlockType); + + bool IsSolidBlock (BLOCKTYPE a_BlockType); + bool IsPassableForFluid(BLOCKTYPE a_BlockType); + + /// Returns true if a_Meta1 is a higher fluid than a_Meta2. Takes source blocks into account. + bool IsHigherMeta(NIBBLETYPE a_Meta1, NIBBLETYPE a_Meta2); + +protected: + BLOCKTYPE m_FluidBlock; // The fluid block type that needs simulating + BLOCKTYPE m_StationaryFluidBlock; // The fluid block type that indicates no simulation is needed +} ; + + + + + diff --git a/src/Simulator/NoopFluidSimulator.h b/src/Simulator/NoopFluidSimulator.h new file mode 100644 index 000000000..8f894433f --- /dev/null +++ b/src/Simulator/NoopFluidSimulator.h @@ -0,0 +1,36 @@ + +// NoopFluidSimulator.h + +// Declares the cNoopFluidSimulator class representing a fluid simulator that performs nothing, it ignores all blocks + + + + + +#pragma once + +#include "FluidSimulator.h" + + + + + +class cNoopFluidSimulator : + public cFluidSimulator +{ + typedef cFluidSimulator super; + +public: + cNoopFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) : + super(a_World, a_Fluid, a_StationaryFluid) + { + } + + // cSimulator overrides: + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {} + virtual void Simulate(float a_Dt) override {} +} ; + + + + diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp new file mode 100644 index 000000000..906961490 --- /dev/null +++ b/src/Simulator/RedstoneSimulator.cpp @@ -0,0 +1,1073 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "RedstoneSimulator.h" +#include "../BlockEntities/DropSpenserEntity.h" +#include "../Entities/TNTEntity.h" +#include "../Blocks/BlockTorch.h" +#include "../Blocks/BlockDoor.h" +#include "../Piston.h" + + + + + +cRedstoneSimulator::cRedstoneSimulator(cWorld & a_World) + : super(a_World) +{ +} + + + + + +cRedstoneSimulator::~cRedstoneSimulator() +{ +} + + + + + +void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height)) + { + return; + } + + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + + if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ))) + { + return; + } + + // Check for duplicates: + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) + { + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) + { + return; + } + } + + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ)); +} + + + + + +void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + if (ChunkData.empty()) + { + return; + } + + int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; + int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; + + for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;) + { + BLOCKTYPE BlockType = a_Chunk->GetBlock(dataitr->x, dataitr->y, dataitr->z); + if (!IsAllowedBlock(BlockType)) + { + dataitr = ChunkData.erase(dataitr); + continue; + } + + // Check to see if PoweredBlocks have invalid items (source is air or an unpowered source) + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end();) + { + sPoweredBlocks & Change = *itr; + BLOCKTYPE SourceBlockType = m_World.GetBlock(Change.a_SourcePos); + + if (SourceBlockType != Change.a_SourceBlock) + { + itr = m_PoweredBlocks.erase(itr); + } + else if ( + // Changeable sources + ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (m_World.GetBlockMeta(Change.a_SourcePos) == 0)) || + ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(m_World.GetBlockMeta(Change.a_SourcePos))) || + ((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (m_World.GetBlockMeta(Change.a_SourcePos) & 0x08) == 0x08) || + (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(m_World.GetBlockMeta(Change.a_SourcePos)))) + ) + { + itr = m_PoweredBlocks.erase(itr); + } + else + { + itr++; + } + } + + // Check to see if LinkedPoweredBlocks have invalid items: source, block powered through, or power destination block has changed + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end();) + { + sLinkedPoweredBlocks & Change = *itr; + BLOCKTYPE SourceBlockType = m_World.GetBlock(Change.a_SourcePos); + BLOCKTYPE MiddleBlockType = m_World.GetBlock(Change.a_MiddlePos); + + if (SourceBlockType != Change.a_SourceBlock) + { + itr = m_LinkedPoweredBlocks.erase(itr); + } + else if (MiddleBlockType != Change.a_MiddleBlock) + { + itr = m_LinkedPoweredBlocks.erase(itr); + } + else if ( + // Things that can send power through a block but which depends on meta + ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (m_World.GetBlockMeta(Change.a_SourcePos) == 0)) || + ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(m_World.GetBlockMeta(Change.a_SourcePos))) || + (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(m_World.GetBlockMeta(Change.a_SourcePos)))) + ) + { + itr = m_LinkedPoweredBlocks.erase(itr); + } + else + { + itr++; + } + } + + // PoweredBlock list was fine, now to the actual handling + int a_X = BaseX + dataitr->x; + int a_Z = BaseZ + dataitr->z; + switch (BlockType) + { + case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break; + case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break; + case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break; + case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break; + + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + { + HandleRedstoneTorch(a_X, dataitr->y, a_Z, BlockType); + break; + } + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_WOODEN_BUTTON: + { + HandleRedstoneButton(a_X, dataitr->y, a_Z, BlockType); + break; + } + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + { + HandleRedstoneRepeater(a_X, dataitr->y, a_Z, BlockType); + break; + } + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + { + HandlePiston(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + { + HandleRedstoneLamp(a_X, dataitr->y, a_Z, BlockType); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + { + HandleDropSpenser(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + { + HandleDoor(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + HandleRail(a_X, dataitr->y, a_Z, BlockType); + break; + } + } + + ++dataitr; + } +} + + + + + +void cRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + static const struct // Define which directions the torch can power + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + } ; + + if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON) + { + // Check if the block the torch is on is powered + int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on + + if (AreCoordsPowered(X, Y, Z)) + { + // There was a match, torch goes off + // FastSetBlock so the server doesn't fail an assert -_- + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); + return; + } + + // Torch still on, make all 4(X, Z) + 1(Y) sides powered + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + BLOCKTYPE Type = m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z); + if (i < ARRAYCOUNT(gCrossCoords) - 1) // Sides of torch, not top (top is last) + { + if ( + ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) && // Is it a mechanism or wire? Not block/other torch etc. + (!Vector3i(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on + ) + { + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + } + } + else + { + // Top side, power whatever is there, including blocks + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + } + } + + if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0x5) // Is torch standing on ground? If not (i.e. on wall), power block beneath + { + BLOCKTYPE Type = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + + if ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) // Still can't make a normal block powered though! + { + SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + } + } + } + else + { + // Check if the block the torch is on is powered + int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on + + // See if off state torch can be turned on again + if (AreCoordsPowered(X, Y, Z)) + { + return; // Something matches, torch still powered + } + + // Block torch on not powered, can be turned on again! + // FastSetBlock so the server doesn't fail an assert -_- + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); + } + return; +} + + + + + +void cRedstoneSimulator::HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + static const struct // Define which directions the redstone block can power + { + int x, y, z; + } gCrossCoords[] = + { + { 0, 0, 0}, // Oh, anomalous redstone. Only block that powers itself + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE); + } + return; +} + + + + + +void cRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (IsLeverOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) + { + static const struct // Define which directions the redstone lever can power (all sides) + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER); + } + + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_LEVER); + } + return; +} + + + + + +void cRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +{ + if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) + { + static const struct // Define which directions the redstone button can power (all sides) + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_BlockType); + } + + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, a_BlockType); + } +} + + + + + +void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + static const struct // Define which directions the wire can receive power from + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 1, 1, 0}, // From here to end, check for wire placed on sides of blocks + {-1, 1, 0}, + { 0, 1, 1}, + { 0, 1, -1}, + { 1,-1, 0}, + {-1,-1, 0}, + { 0,-1, 1}, + { 0,-1, -1}, + } ; + + // Check to see if directly beside a power source + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power + } + else + { + NIBBLETYPE MyMeta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + int TimesMetaSmaller = 0, TimesFoundAWire = 0; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power + { + BLOCKTYPE SurroundType; + NIBBLETYPE SurroundMeta; + m_World.GetBlockTypeMeta(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, SurroundType, SurroundMeta); + + if (SurroundType == E_BLOCK_REDSTONE_WIRE) + { + TimesFoundAWire++; + + if (SurroundMeta > 1) // Wires of power 1 or 0 cannot transfer power TO ME, don't bother checking + { + if (SurroundMeta > MyMeta) // Does surrounding wire have a higher power level than self? + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, SurroundMeta - 1); + } + } + + if (SurroundMeta < MyMeta) // Go through all surroundings to see if self power is larger than everyone else's + { + TimesMetaSmaller++; + } + } + } + + if (TimesMetaSmaller == TimesFoundAWire) + { + // All surrounding metas were smaller - self must have been a wire that was + // transferring power to other wires around. + // However, self not directly powered anymore, so source must have been removed, + // therefore, self must be set to meta zero + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0); + } + } + + if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0) // A powered wire + { + //SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); // No matter what, block underneath gets powered + + switch (GetWireDirection(a_BlockX, a_BlockY, a_BlockZ)) + { + case REDSTONE_NONE: + { + static const struct // Define which directions the redstone wire can power + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, // Power block in front + { 2, 0, 0}, // Power block in front of that (strongly power) + {-1, 0, 0}, + {-2, 0, 0}, + { 0, 0, 1}, + { 0, 0, 2}, + { 0, 0, -1}, + { 0, 0, -2}, + { 0, 1, 0}, + { 0, 2, 0}, + { 0,-1, 0}, + { 0,-2, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power if block is solid, CURRENTLY all mechanisms are solid + if (g_BlockIsSolid[m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z)]) + { + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + } + break; + } + case REDSTONE_X_POS: + { + if (m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE); + break; + } + case REDSTONE_X_NEG: + { + if (m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE); + break; + } + case REDSTONE_Z_POS: + { + if (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE); + break; + } + case REDSTONE_Z_NEG: + { + if (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_WIRE); + break; + } + } + } + return; +} + + + + + +void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + NIBBLETYPE a_Meta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (a_MyState == E_BLOCK_REDSTONE_REPEATER_OFF) + { + if (IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3)) + { + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta); + switch (a_Meta & 0x3) // We only want the direction (bottom) bits + { + case 0x0: + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + case 0x1: + { + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + case 0x2: + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + case 0x3: + { + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + } + } + } + else + { + if (!IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3)) + { + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta); + } + } + return; +} + + + + + +void cRedstoneSimulator::HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + cPiston Piston(&m_World); + Piston.ExtendPiston(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + cPiston Piston(&m_World); + Piston.RetractPiston(a_BlockX, a_BlockY, a_BlockZ); + } + return; +} + + + + + +void cRedstoneSimulator::HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + class cSetPowerToDropSpenser : + public cDropSpenserCallback + { + bool m_IsPowered; + public: + cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cDropSpenserEntity * a_DropSpenser) override + { + a_DropSpenser->SetRedstonePower(m_IsPowered); + return false; + } + } DrSpSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)); + + m_World.DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, DrSpSP); + return; +} + + + + + +void cRedstoneSimulator::HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF) + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0); + } + } + else + { + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0); + } + } + return; +} + + + + + +void cRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + } + return; +} + + + + + +void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) + { + // Block position is located at top half of door + // Is Y - 1 both within world boundaries, a door block, and the bottom half of a door? + // The bottom half stores the open/closed information + if ( + (a_BlockY - 1 >= 0) && + ((m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) || (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_IRON_DOOR)) && + (m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ & 0x08) == 0) + ) + { + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ) & 0x04) == 0) // Closed door? + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open + { + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + } + } + else // Opened door + { + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so + { + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + } + } + } + } + else + { + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x04) == 0) // Closed door? + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open + { + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + } + } + else // Opened door + { + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so + { + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + } + } + } + return; +} + + + + + +void cRedstoneSimulator::HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) +{ + switch (a_MyType) + { + case E_BLOCK_DETECTOR_RAIL: + { + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) + { + static const struct // Define which directions the rail can power (all sides) + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_MyType); + } + } + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x08); + } + else + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x07); + } + break; + } + } +} + + + + + +bool cRedstoneSimulator::AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list + { + sPoweredBlocks & Change = *itr; + + if (Change.a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + } + + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list + { + sLinkedPoweredBlocks & Change = *itr; + + if (Change.a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + } + return false; +} + + + + + +bool cRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta) +{ + // Check through powered blocks list + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) + { + sPoweredBlocks & Change = *itr; + + switch (a_Meta) + { + case 0x0: + { + // Flip the coords to check the back of the repeater + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } + break; + } + case 0x1: + { + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + case 0x2: + { + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } + break; + } + case 0x3: + { + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + } + } + + // Check linked powered list, 'middle' blocks + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + { + sLinkedPoweredBlocks & Change = *itr; + + switch (a_Meta) + { + case 0x0: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } + break; + } + case 0x1: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + case 0x2: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } + break; + } + case 0x3: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + } + } + return false; // Couldn't find power source behind repeater +} + + + + + +void cRedstoneSimulator::SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType) +{ + switch (a_Direction) + { + case BLOCK_FACE_XM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX - 2, a_BlockY, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_XP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX + 2, a_BlockY, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_YM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY - 2, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_YP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY + 2, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_ZM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ - 2, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_ZP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ + 2, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + default: + { + ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); + break; + } + } + return; +} + + + + + +void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { return; } // Check for duplicates + + sPoweredBlocks RC; + RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); + RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); + RC.a_SourceBlock = a_SourceBlock; + m_PoweredBlocks.push_back(RC); + return; +} + + + + + +void cRedstoneSimulator::SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, + int a_MiddleX, int a_MiddleY, int a_MiddleZ, + int a_SourceX, int a_SourceY, int a_SourceZ, + BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddleBlock + ) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { return; } // Check for duplicates + + sLinkedPoweredBlocks RC; + RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); + RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ); + RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); + RC.a_SourceBlock = a_SourceBlock; + RC.a_MiddleBlock = a_MiddleBlock; + m_LinkedPoweredBlocks.push_back(RC); + return; +} + + + + + +cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int Dir = REDSTONE_NONE; + + BLOCKTYPE NegX = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); + if (IsPotentialSource(NegX)) + { + Dir |= (REDSTONE_X_POS); + } + + BLOCKTYPE PosX = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); + if (IsPotentialSource(PosX)) + { + Dir |= (REDSTONE_X_NEG); + } + + BLOCKTYPE NegZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); + if (IsPotentialSource(NegZ)) + { + if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_POS; + } + + BLOCKTYPE PosZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); + if (IsPotentialSource(PosZ)) + { + if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_NEG; + } + return (eRedstoneDirection)Dir; +} + + + + + +bool cRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) +{ + // Extract the ON bit from metadata and return if true if it is set: + return ((a_BlockMeta & 0x8) == 0x8); +} + + + + + +bool cRedstoneSimulator::IsButtonOn(NIBBLETYPE a_BlockMeta) +{ + return IsLeverOn(a_BlockMeta); +} + + + + diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h new file mode 100644 index 000000000..d68c6daeb --- /dev/null +++ b/src/Simulator/RedstoneSimulator.h @@ -0,0 +1,199 @@ + +#pragma once + +#include "Simulator.h" + +/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used +typedef cCoordWithIntList cRedstoneSimulatorChunkData; + + + + + +class cRedstoneSimulator : + public cSimulator +{ + typedef cSimulator super; +public: + + cRedstoneSimulator(cWorld & a_World); + ~cRedstoneSimulator(); + + virtual void Simulate(float a_Dt) override {}; // Not used in this simulator + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return IsRedstone(a_BlockType); } + + enum eRedstoneDirection + { + REDSTONE_NONE = 0, + REDSTONE_X_POS = 0x1, + REDSTONE_X_NEG = 0x2, + REDSTONE_Z_POS = 0x4, + REDSTONE_Z_NEG = 0x8, + }; + eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ); + eRedstoneDirection GetWireDirection(const Vector3i & a_Pos) { return GetWireDirection(a_Pos.x, a_Pos.y, a_Pos.z); } + +private: + + struct sPoweredBlocks // Define structure of the directly powered blocks list + { + Vector3i a_BlockPos; // Position of powered block + Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos + BLOCKTYPE a_SourceBlock; // The source block type (for pistons pushing away sources and replacing with non source etc.) + }; + + struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side) + { + Vector3i a_BlockPos; + Vector3i a_MiddlePos; + Vector3i a_SourcePos; + BLOCKTYPE a_SourceBlock; + BLOCKTYPE a_MiddleBlock; + }; + + typedef std::vector <sPoweredBlocks> PoweredBlocksList; + typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList; + + PoweredBlocksList m_PoweredBlocks; + LinkedBlocksList m_LinkedPoweredBlocks; + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + + // We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly + // In addition to being non-performant, it would stop the player from actually breaking said device + + /* ====== SOURCES ====== */ + ///<summary>Handles the redstone torch</summary> + void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + ///<summary>Handles the redstone block</summary> + void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles levers</summary> + void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles buttons</summary> + void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + /* ==================== */ + + /* ====== CARRIERS ====== */ + ///<summary>Handles redstone wire</summary> + void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles repeaters</summary> + void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + /* ====================== */ + + /* ====== DEVICES ====== */ + ///<summary>Handles pistons</summary> + void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles dispensers and droppers</summary> + void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles TNT (exploding)</summary> + void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles redstone lamps</summary> + void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + ///<summary>Handles doords</summary> + void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles activator, detector, and powered rails</summary> + void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); + /* ===================== */ + + /* ====== Helper functions ====== */ + void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock); + void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock); + void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType); + + bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ); + bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); + + bool IsLeverOn(NIBBLETYPE a_BlockMeta); + bool IsButtonOn(NIBBLETYPE a_BlockMeta); + /* ============================== */ + + inline static bool IsMechanism(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_TNT: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_POWERED_RAIL: + { + return true; + } + default: return false; + } + } + + inline static bool IsPotentialSource(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_LEVER: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_INACTIVE_COMPARATOR: + { + return true; + } + default: return false; + } + } + + inline static bool IsRedstone(BLOCKTYPE Block) + { + switch (Block) + { + // All redstone devices, please alpha sort + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_DISPENSER: + case E_BLOCK_DAYLIGHT_SENSOR: + case E_BLOCK_DROPPER: + case E_BLOCK_FENCE_GATE: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_HOPPER: + case E_BLOCK_INACTIVE_COMPARATOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_LEVER: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_TNT: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_TRIPWIRE_HOOK: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_PISTON: + { + return true; + } + default: return false; + } + } +};
\ No newline at end of file diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp new file mode 100644 index 000000000..87fb83357 --- /dev/null +++ b/src/Simulator/SandSimulator.cpp @@ -0,0 +1,309 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SandSimulator.h" +#include "../World.h" +#include "../BlockID.h" +#include "../Defines.h" +#include "../Entities/FallingBlock.h" +#include "../Chunk.h" + + + + + +cSandSimulator::cSandSimulator(cWorld & a_World, cIniFile & a_IniFile) : + cSimulator(a_World), + m_TotalBlocks(0) +{ + m_IsInstantFall = a_IniFile.GetValueSetB("Physics", "SandInstantFall", false); +} + + + + + +void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); + if (ChunkData.empty()) + { + return; + } + + int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; + int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; + for (cSandSimulatorChunkData::const_iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr) + { + BLOCKTYPE BlockType = a_Chunk->GetBlock(itr->x, itr->y, itr->z); + if (!IsAllowedBlock(BlockType) || (itr->y <= 0)) + { + continue; + } + + BLOCKTYPE BlockBelow = (itr->y > 0) ? a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z) : E_BLOCK_AIR; + if (CanStartFallingThrough(BlockBelow)) + { + if (m_IsInstantFall) + { + DoInstantFall(a_Chunk, itr->x, itr->y, itr->z); + continue; + } + Vector3i Pos; + Pos.x = itr->x + BaseX; + Pos.y = itr->y; + Pos.z = itr->z + BaseZ; + /* + LOGD( + "Creating a falling block at {%d, %d, %d} of type %s, block below: %s", + Pos.x, Pos.y, Pos.z, ItemTypeToString(BlockType).c_str(), ItemTypeToString(BlockBelow).c_str() + ); + */ + cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z)); + FallingBlock->Initialize(&m_World); + a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0); + } + } + m_TotalBlocks -= ChunkData.size(); + ChunkData.clear(); +} + + + + + +bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_ANVIL: + case E_BLOCK_DRAGON_EGG: + { + return true; + } + } + return false; +} + + + + + +void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ))) + { + return; + } + + // Check for duplicates: + cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); + for (cSandSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) + { + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) + { + return; + } + } + + m_TotalBlocks += 1; + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ)); +} + + + + + +bool cSandSimulator::CanStartFallingThrough(BLOCKTYPE a_BlockType) +{ + // Please keep the list alpha-sorted + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_FIRE: + case E_BLOCK_LAVA: + case E_BLOCK_SNOW: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + return true; + } + } + return false; +} + + + + + +bool cSandSimulator::CanContinueFallThrough(BLOCKTYPE a_BlockType) +{ + // Please keep the list alpha-sorted + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_BROWN_MUSHROOM: + case E_BLOCK_COBWEB: + case E_BLOCK_CROPS: + case E_BLOCK_DEAD_BUSH: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_FIRE: + case E_BLOCK_FLOWER_POT: + case E_BLOCK_LAVA: + case E_BLOCK_LEVER: + case E_BLOCK_MINECART_TRACKS: + case E_BLOCK_MELON_STEM: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_PUMPKIN_STEM: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_RED_MUSHROOM: + case E_BLOCK_RED_ROSE: + case E_BLOCK_SIGN_POST: + case E_BLOCK_SNOW: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_TORCH: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_TRIPWIRE: + case E_BLOCK_TRIPWIRE_HOOK: + case E_BLOCK_WALLSIGN: + case E_BLOCK_WATER: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_YELLOW_FLOWER: + { + return true; + } + } + return false; +} + + + + + +bool cSandSimulator::IsReplacedOnRematerialization(BLOCKTYPE a_BlockType) +{ + // Please keep the list alpha-sorted + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_DEAD_BUSH: + case E_BLOCK_FIRE: + case E_BLOCK_LAVA: + case E_BLOCK_SNOW: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_WATER: + { + return true; + } + } + return false; +} + + + + + +bool cSandSimulator::DoesBreakFallingThrough(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + switch (a_BlockType) + { + case E_BLOCK_STONE_SLAB: + case E_BLOCK_WOODEN_SLAB: + { + return ((a_BlockMeta & 0x08) == 0); // Only a bottom-slab breaks the block + } + } + return false; +} + + + + + +void cSandSimulator::FinishFalling( + cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, + BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta +) +{ + ASSERT(a_BlockY < cChunkDef::Height); + + BLOCKTYPE CurrentBlockType = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if ((a_FallingBlockType == E_BLOCK_ANVIL) || IsReplacedOnRematerialization(CurrentBlockType)) + { + // Rematerialize the material here: + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FallingBlockType, a_FallingBlockMeta); + return; + } + + // Create a pickup instead: + cItems Pickups; + Pickups.Add((ENUM_ITEM_ID)a_FallingBlockType, 1, a_FallingBlockMeta); + a_World->SpawnItemPickups(Pickups, (double)a_BlockX + 0.5, (double)a_BlockY + 0.5, (double)a_BlockZ + 0.5); +} + + + + + +void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + // Remove the original block: + BLOCKTYPE FallingBlockType; + NIBBLETYPE FallingBlockMeta; + a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, FallingBlockType, FallingBlockMeta); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + + // Search for a place to put it: + for (int y = a_RelY - 1; y >= 0; y--) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + a_Chunk->GetBlockTypeMeta(a_RelX, y, a_RelZ, BlockType, BlockMeta); + int BlockY; + if (DoesBreakFallingThrough(BlockType, BlockMeta)) + { + BlockY = y; + } + else if (!CanContinueFallThrough(BlockType)) + { + BlockY = y + 1; + } + else + { + // Can fall further down + continue; + } + + // Finish the fall at the found bottom: + int BlockX = a_RelX + a_Chunk->GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk->GetPosZ() * cChunkDef::Width; + FinishFalling(&m_World, BlockX, BlockY, BlockZ, FallingBlockType, FallingBlockMeta); + return; + } + + // The block just "fell off the world" without leaving a trace +} + + + + diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h new file mode 100644 index 000000000..6e9ea15ac --- /dev/null +++ b/src/Simulator/SandSimulator.h @@ -0,0 +1,63 @@ + +#pragma once + +#include "Simulator.h" + + + + + +/// Despite the class name, this simulator takes care of all blocks that fall when suspended in the air. +class cSandSimulator : + public cSimulator +{ +public: + cSandSimulator(cWorld & a_World, cIniFile & a_IniFile); + + // cSimulator overrides: + virtual void Simulate(float a_Dt) override {} // Unused in this simulator + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; + + /// Returns true if a falling-able block can start falling through the specified block type + static bool CanStartFallingThrough(BLOCKTYPE a_BlockType); + + /// Returns true if an already-falling block can pass through the specified block type (e. g. torch) + static bool CanContinueFallThrough(BLOCKTYPE a_BlockType); + + /// Returns true if the falling block rematerializing will replace the specified block type (e. g. tall grass) + static bool IsReplacedOnRematerialization(BLOCKTYPE a_BlockType); + + /// Returns true if the specified block breaks falling blocks while they fall through it (e. g. halfslabs) + static bool DoesBreakFallingThrough(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Called when a block finishes falling at the specified coords, either by insta-fall, + or through cFallingBlock entity. + It either rematerializes the block (a_FallingBlockType) at the specified coords, or creates a pickup, + based on the block currently present in the world at the dest specified coords + */ + static void FinishFalling( + cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, + BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta + ); + +protected: + bool m_IsInstantFall; // If set to true, blocks don't fall using cFallingBlock entity, but instantly instead + + int m_TotalBlocks; // Total number of blocks currently in the queue for simulating + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + + /// Performs the instant fall of the block - removes it from top, Finishes it at the bottom + void DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); +}; + + + + +/// Per-chunk data for the simulator, specified individual chunks to simulate; Data is not used +typedef cCoordWithIntList cSandSimulatorChunkData; + + + + diff --git a/src/Simulator/Simulator.cpp b/src/Simulator/Simulator.cpp new file mode 100644 index 000000000..06fd0f858 --- /dev/null +++ b/src/Simulator/Simulator.cpp @@ -0,0 +1,51 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Simulator.h" +#include "../World.h" +#include "../Vector3i.h" +#include "../BlockID.h" +#include "../Defines.h" +#include "../Chunk.h" + + + + + +cSimulator::cSimulator(cWorld & a_World) + : m_World(a_World) +{ +} + + + + + +cSimulator::~cSimulator() +{ +} + + + + + +void cSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); + AddBlock(a_BlockX - 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 1, a_BlockZ)); + AddBlock(a_BlockX + 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 1, a_BlockZ)); + AddBlock(a_BlockX, a_BlockY, a_BlockZ - 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 1)); + AddBlock(a_BlockX, a_BlockY, a_BlockZ + 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 1)); + if (a_BlockY > 0) + { + AddBlock(a_BlockX, a_BlockY - 1, a_BlockZ, a_Chunk); + } + if (a_BlockY < cChunkDef::Height - 1) + { + AddBlock(a_BlockX, a_BlockY + 1, a_BlockZ, a_Chunk); + } +} + + + + diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h new file mode 100644 index 000000000..4f555693d --- /dev/null +++ b/src/Simulator/Simulator.h @@ -0,0 +1,46 @@ + +#pragma once + +#include "../Vector3i.h" +#include "lib/inifile/iniFile.h" + + + + + +class cWorld; +class cChunk; + + + + + +class cSimulator +{ +public: + cSimulator(cWorld & a_World); + virtual ~cSimulator(); + + /// Called in each tick, a_Dt is the time passed since the last tick, in msec + virtual void Simulate(float a_Dt) = 0; + + /// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) {}; + + /// Called when a block changes + virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); + + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) = 0; + +protected: + friend class cChunk; // Calls AddBlock() in its WakeUpSimulators() function, to speed things up + + /// Called to simulate a new block + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) = 0; + + cWorld & m_World; +} ; + + + + diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp new file mode 100644 index 000000000..2bc483cbd --- /dev/null +++ b/src/Simulator/SimulatorManager.cpp @@ -0,0 +1,80 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SimulatorManager.h" +#include "../World.h" + + + + + +cSimulatorManager::cSimulatorManager(cWorld & a_World) : + m_World(a_World), + m_Ticks(0) +{ +} + + + + + +cSimulatorManager::~cSimulatorManager() +{ +} + + + + + +void cSimulatorManager::Simulate(float a_Dt) +{ + m_Ticks++; + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + if ((m_Ticks % itr->second) == 0) + { + itr->first->Simulate(a_Dt); + } + } +} + + + + + +void cSimulatorManager::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + // m_Ticks has already been increased in Simulate() + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + if ((m_Ticks % itr->second) == 0) + { + itr->first->SimulateChunk(a_Dt, a_ChunkX, a_ChunkZ, a_Chunk); + } + } +} + + + + + +void cSimulatorManager::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + itr->first->WakeUp(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); + } +} + + + + + +void cSimulatorManager::RegisterSimulator(cSimulator * a_Simulator, int a_Rate) +{ + m_Simulators.push_back(std::make_pair(a_Simulator, a_Rate)); +} + + + + diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h new file mode 100644 index 000000000..31a709316 --- /dev/null +++ b/src/Simulator/SimulatorManager.h @@ -0,0 +1,52 @@ + +// cSimulatorManager.h + + + + +#pragma once + + + + +#include "Simulator.h" + + + + + +// fwd: Chunk.h +class cChunk; + +// fwd: World.h +class cWorld; + + + + + +class cSimulatorManager +{ +public: + cSimulatorManager(cWorld & a_World); + ~cSimulatorManager(); + + void Simulate(float a_Dt); + + void SimulateChunk(float a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk); + + void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); + + void RegisterSimulator(cSimulator * a_Simulator, int a_Rate); // Takes ownership of the simulator object! + +protected: + typedef std::vector <std::pair<cSimulator *, int> > cSimulators; + + cWorld & m_World; + cSimulators m_Simulators; + long long m_Ticks; +}; + + + + diff --git a/src/Simulator/VaporizeFluidSimulator.cpp b/src/Simulator/VaporizeFluidSimulator.cpp new file mode 100644 index 000000000..4206c64d1 --- /dev/null +++ b/src/Simulator/VaporizeFluidSimulator.cpp @@ -0,0 +1,53 @@ + +// VaporizeFluidSimulator.cpp + +// Implements the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air + +#include "Globals.h" +#include "VaporizeFluidSimulator.h" +#include "../Chunk.h" + + + + + +cVaporizeFluidSimulator::cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) : + super(a_World, a_Fluid, a_StationaryFluid) +{ +} + + + + + +void cVaporizeFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if (a_Chunk == NULL) + { + return; + } + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ); + if ( + (BlockType == m_FluidBlock) || + (BlockType == m_StationaryFluidBlock) + ) + { + a_Chunk->SetBlock(RelX, a_BlockY, RelZ, E_BLOCK_AIR, 0); + a_Chunk->BroadcastSoundEffect("random.fizz", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.6f); + } +} + + + + + +void cVaporizeFluidSimulator::Simulate(float a_Dt) +{ + // Nothing needed +} + + + + diff --git a/src/Simulator/VaporizeFluidSimulator.h b/src/Simulator/VaporizeFluidSimulator.h new file mode 100644 index 000000000..c8eb7802b --- /dev/null +++ b/src/Simulator/VaporizeFluidSimulator.h @@ -0,0 +1,34 @@ + +// VaporizeFluidSimulator.h + +// Declares the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air +// Useful for water simulation in the Nether + + + + + +#pragma once + +#include "FluidSimulator.h" + + + + + +class cVaporizeFluidSimulator : + public cFluidSimulator +{ + typedef cFluidSimulator super; + +public: + cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid); + + // cSimulator overrides: + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + virtual void Simulate(float a_Dt) override; +} ; + + + + diff --git a/src/StackWalker.cpp b/src/StackWalker.cpp new file mode 100644 index 000000000..bf18b9fc9 --- /dev/null +++ b/src/StackWalker.cpp @@ -0,0 +1,1345 @@ +/********************************************************************** + * + * StackWalker.cpp + * + * + * History: + * 2005-07-27 v1 - First public release on http://www.codeproject.com/ + * http://www.codeproject.com/threads/StackWalker.asp + * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack + * (to simplify the usage) + * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL + * (should also be enough) + * - Changed to compile correctly with the PSDK of VC7.0 + * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: + * it uses LPSTR instead of LPCSTR as first paremeter) + * - Added declarations to support VC5/6 without using 'dbghelp.h' + * - Added a 'pUserData' member to the ShowCallstack function and the + * PReadProcessMemoryRoutine declaration (to pass some user-defined data, + * which can be used in the readMemoryFunction-callback) + * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default + * - Added example for doing an exception-callstack-walking in main.cpp + * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268) + * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse! + * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx + * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx + * Fixed Bug: Compiling with "/Wall" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx + * Fixed Bug: Now checking SymUseSymSrv + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx + * Fixed Bug: Support for recursive function calls + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx + * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx + * Fixed Bug: SymDia is number 7, not 9! + * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8! + * Thanks to Teajay which reported the bug... + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx + * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory + * Thanks to Luiz Salamon which reported this "bug"... + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx + * 2009-04-10 v9 License slihtly corrected (<ORGANIZATION> replaced) + * 2010-04-15 v10 Added support for VS2010 RTM + * 2010-05-2ß v11 Now using secure MyStrcCpy. Thanks to luke.simon: + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ +#include <windows.h> +#include <tchar.h> +#include <stdio.h> +#include <stdlib.h> +#pragma comment(lib, "version.lib") // for "VerQueryValue" +#pragma warning(disable:4826) + +#include "StackWalker.h" + + +// If VC7 and later, then use the shipped 'dbghelp.h'-file +#pragma pack(push,8) +#if _MSC_VER >= 1300 +#include <dbghelp.h> +#else +// inline the important dbghelp.h-declarations... +typedef enum { + SymNone = 0, + SymCoff, + SymCv, + SymPdb, + SymExport, + SymDeferred, + SymSym, + SymDia, + SymVirtual, + NumSymTypes +} SYM_TYPE; +typedef struct _IMAGEHLP_LINE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) + PVOID Key; // internal + DWORD LineNumber; // line number in file + PCHAR FileName; // full filename + DWORD64 Address; // first instruction of line +} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; +typedef struct _IMAGEHLP_MODULE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; +typedef struct _IMAGEHLP_SYMBOL64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) + DWORD64 Address; // virtual address including dll base address + DWORD Size; // estimated size of symbol, can be zero + DWORD Flags; // info about the symbols, see the SYMF defines + DWORD MaxNameLength; // maximum size of symbol name in 'Name' + CHAR Name[1]; // symbol name (null terminated string) +} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; +typedef enum { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat +} ADDRESS_MODE; +typedef struct _tagADDRESS64 { + DWORD64 Offset; + WORD Segment; + ADDRESS_MODE Mode; +} ADDRESS64, *LPADDRESS64; +typedef struct _KDHELP64 { + DWORD64 Thread; + DWORD ThCallbackStack; + DWORD ThCallbackBStore; + DWORD NextCallback; + DWORD FramePointer; + DWORD64 KiCallUserMode; + DWORD64 KeUserCallbackDispatcher; + DWORD64 SystemRangeStart; + DWORD64 Reserved[8]; +} KDHELP64, *PKDHELP64; +typedef struct _tagSTACKFRAME64 { + ADDRESS64 AddrPC; // program counter + ADDRESS64 AddrReturn; // return address + ADDRESS64 AddrFrame; // frame pointer + ADDRESS64 AddrStack; // stack pointer + ADDRESS64 AddrBStore; // backing store pointer + PVOID FuncTableEntry; // pointer to pdata/fpo or NULL + DWORD64 Params[4]; // possible arguments to the function + BOOL Far; // WOW far call + BOOL Virtual; // is this a virtual frame? + DWORD64 Reserved[3]; + KDHELP64 KdHelp; +} STACKFRAME64, *LPSTACKFRAME64; +typedef +BOOL +(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ); +typedef +PVOID +(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( + HANDLE hProcess, + DWORD64 AddrBase + ); +typedef +DWORD64 +(__stdcall *PGET_MODULE_BASE_ROUTINE64)( + HANDLE hProcess, + DWORD64 Address + ); +typedef +DWORD64 +(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( + HANDLE hProcess, + HANDLE hThread, + LPADDRESS64 lpaddr + ); +#define SYMOPT_CASE_INSENSITIVE 0x00000001 +#define SYMOPT_UNDNAME 0x00000002 +#define SYMOPT_DEFERRED_LOADS 0x00000004 +#define SYMOPT_NO_CPP 0x00000008 +#define SYMOPT_LOAD_LINES 0x00000010 +#define SYMOPT_OMAP_FIND_NEAREST 0x00000020 +#define SYMOPT_LOAD_ANYTHING 0x00000040 +#define SYMOPT_IGNORE_CVREC 0x00000080 +#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 +#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 +#define SYMOPT_EXACT_SYMBOLS 0x00000400 +#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 +#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 +#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 +#define SYMOPT_PUBLICS_ONLY 0x00004000 +#define SYMOPT_NO_PUBLICS 0x00008000 +#define SYMOPT_AUTO_PUBLICS 0x00010000 +#define SYMOPT_NO_IMAGE_SEARCH 0x00020000 +#define SYMOPT_SECURE 0x00040000 +#define SYMOPT_DEBUG 0x80000000 +#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration +#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; +#endif // _MSC_VER < 1300 +#pragma pack(pop) + +// Some missing defines (for VC5/6): +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define strcpy_s strcpy +#define strncpy_s strncpy +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + +static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc) +{ + if (nMaxDestSize <= 0) return; + if (strlen(szSrc) < nMaxDestSize) + { + strcpy_s(szDest, nMaxDestSize, szSrc); + } + else + { + strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize); + szDest[nMaxDestSize-1] = 0; + } +} // MyStrCpy + +// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') +#define USED_CONTEXT_FLAGS CONTEXT_FULL + + +class StackWalkerInternal +{ +public: + StackWalkerInternal(StackWalker *parent, HANDLE hProcess) + { + m_parent = parent; + m_hDbhHelp = NULL; + pSC = NULL; + m_hProcess = hProcess; + m_szSymPath = NULL; + pSFTA = NULL; + pSGLFA = NULL; + pSGMB = NULL; + pSGMI = NULL; + pSGO = NULL; + pSGSFA = NULL; + pSI = NULL; + pSLM = NULL; + pSSO = NULL; + pSW = NULL; + pUDSN = NULL; + pSGSP = NULL; + } + ~StackWalkerInternal() + { + if (pSC != NULL) + pSC(m_hProcess); // SymCleanup + if (m_hDbhHelp != NULL) + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + m_parent = NULL; + if(m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + } + BOOL Init(LPCSTR szSymPath) + { + if (m_parent == NULL) + return FALSE; + // Dynamically load the Entry-Points for dbghelp.dll: + // First try to load the newsest one from + TCHAR szTemp[4096]; + // But before wqe do this, we first check if the ".local" file exists + if (GetModuleFileName(NULL, szTemp, 4096) > 0) + { + _tcscat_s(szTemp, _T(".local")); + if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) + { + // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" + // Ok, first try the new path according to the archtitecture: +#ifdef _M_IX86 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#elif _M_X64 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#elif _M_IA64 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#endif + // If still not found, try the old directories... + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#if defined _M_X64 || defined _M_IA64 + // Still not found? Then try to load the (old) 64-Bit version: + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#endif + } + } + if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one + m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") ); + if (m_hDbhHelp == NULL) + return FALSE; + pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" ); + pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" ); + + pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" ); + pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" ); + pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" ); + + pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" ); + pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" ); + pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" ); + pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + //pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" ); + pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" ); + pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" ); + pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" ); + + if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || + pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL || + pSW == NULL || pUDSN == NULL || pSLM == NULL ) + { + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + pSC = NULL; + return FALSE; + } + + // SymInitialize + if (szSymPath != NULL) + m_szSymPath = _strdup(szSymPath); + if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) + this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); + + DWORD symOptions = this->pSGO(); // SymGetOptions + symOptions |= SYMOPT_LOAD_LINES; + symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; + //symOptions |= SYMOPT_NO_PROMPTS; + // SymSetOptions + symOptions = this->pSSO(symOptions); + + char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0}; + if (this->pSGSP != NULL) + { + if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) + this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); + } + char szUserName[1024] = {0}; + DWORD dwSize = 1024; + GetUserNameA(szUserName, &dwSize); + this->m_parent->OnSymInit(buf, symOptions, szUserName); + + return TRUE; + } + + StackWalker *m_parent; + + HMODULE m_hDbhHelp; + HANDLE m_hProcess; + LPSTR m_szSymPath; + +/*typedef struct IMAGEHLP_MODULE64_V3 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + // new elements: 07-Jun-2002 + CHAR LoadedImageName[256]; // symbol file name + CHAR LoadedPdbName[256]; // pdb file name + DWORD CVSig; // Signature of the CV record in the debug directories + CHAR CVData[MAX_PATH * 3]; // Contents of the CV record + DWORD PdbSig; // Signature of PDB + GUID PdbSig70; // Signature of PDB (VC 7 and up) + DWORD PdbAge; // DBI age of pdb + BOOL PdbUnmatched; // loaded an unmatched pdb + BOOL DbgUnmatched; // loaded an unmatched dbg + BOOL LineNumbers; // we have line number information + BOOL GlobalSymbols; // we have internal symbol information + BOOL TypeInfo; // we have type information + // new elements: 17-Dec-2003 + BOOL SourceIndexed; // pdb supports source server + BOOL Publics; // contains public symbols +}; +*/ + +#pragma pack(push,8) +typedef struct IMAGEHLP_MODULE64_V2 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +}; +#pragma pack(pop) + + + // SymCleanup() + typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess ); + tSC pSC; + + // SymFunctionTableAccess64() + typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase ); + tSFTA pSFTA; + + // SymGetLineFromAddr64() + typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line ); + tSGLFA pSGLFA; + + // SymGetModuleBase64() + typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr ); + tSGMB pSGMB; + + // SymGetModuleInfo64() + typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo ); + tSGMI pSGMI; + +// // SymGetModuleInfo64() +// typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo ); +// tSGMI_V3 pSGMI_V3; + + // SymGetOptions() + typedef DWORD (__stdcall *tSGO)( VOID ); + tSGO pSGO; + + // SymGetSymFromAddr64() + typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol ); + tSGSFA pSGSFA; + + // SymInitialize() + typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess ); + tSI pSI; + + // SymLoadModule64() + typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile, + IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll ); + tSLM pSLM; + + // SymSetOptions() + typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions ); + tSSO pSSO; + + // StackWalk64() + typedef BOOL (__stdcall *tSW)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME64 StackFrame, + PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ); + tSW pSW; + + // UnDecorateSymbolName() + typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, + DWORD UndecoratedLength, DWORD Flags ); + tUDSN pUDSN; + + typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); + tSGSP pSGSP; + + +private: + // **************************************** ToolHelp32 ************************ + #define MAX_MODULE_NAME32 255 + #define TH32CS_SNAPMODULE 0x00000008 + #pragma pack( push, 8 ) + typedef struct tagMODULEENTRY32 + { + DWORD dwSize; + DWORD th32ModuleID; // This module + DWORD th32ProcessID; // owning process + DWORD GlblcntUsage; // Global usage count on the module + DWORD ProccntUsage; // Module usage count in th32ProcessID's context + BYTE * modBaseAddr; // Base address of module in th32ProcessID's context + DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr + HMODULE hModule; // The hModule of this module in th32ProcessID's context + char szModule[MAX_MODULE_NAME32 + 1]; + char szExePath[MAX_PATH]; + } MODULEENTRY32; + typedef MODULEENTRY32 * PMODULEENTRY32; + typedef MODULEENTRY32 * LPMODULEENTRY32; + #pragma pack( pop ) + + BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) + { + // CreateToolhelp32Snapshot() + typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + // Module32First() + typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + // Module32Next() + typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + // try both dlls... + const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") }; + HINSTANCE hToolhelp = NULL; + tCT32S pCT32S = NULL; + tM32F pM32F = NULL; + tM32N pM32N = NULL; + + HANDLE hSnap; + MODULEENTRY32 me; + me.dwSize = sizeof(me); + BOOL keepGoing; + size_t i; + + for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) + { + hToolhelp = LoadLibrary( dllname[i] ); + if (hToolhelp == NULL) + continue; + pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); + if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) + break; // found the functions! + FreeLibrary(hToolhelp); + hToolhelp = NULL; + } + + if (hToolhelp == NULL) + return FALSE; + + hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); + if (hSnap == (HANDLE) -1) + { + FreeLibrary(hToolhelp); + return FALSE; + } + + keepGoing = !!pM32F( hSnap, &me ); + int cnt = 0; + while (keepGoing) + { + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); + cnt++; + keepGoing = !!pM32N( hSnap, &me ); + } + CloseHandle(hSnap); + FreeLibrary(hToolhelp); + if (cnt <= 0) + return FALSE; + return TRUE; + } // GetModuleListTH32 + + // **************************************** PSAPI ************************ + typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; + } MODULEINFO, *LPMODULEINFO; + + BOOL GetModuleListPSAPI(HANDLE hProcess) + { + // EnumProcessModules() + typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ); + // GetModuleFileNameEx() + typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleBaseName() + typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleInformation() + typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize ); + + HINSTANCE hPsapi; + tEPM pEPM; + tGMFNE pGMFNE; + tGMBN pGMBN; + tGMI pGMI; + + DWORD i; + //ModuleEntry e; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE *hMods = 0; + char *tt = NULL; + char *tt2 = NULL; + const SIZE_T TTBUFLEN = 8096; + int cnt = 0; + + hPsapi = LoadLibrary( _T("psapi.dll") ); + if (hPsapi == NULL) + return FALSE; + + pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" ); + pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" ); + pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" ); + pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); + if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) + { + // we couldn´t find all functions + FreeLibrary(hPsapi); + return FALSE; + } + + hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); + tt = (char*) malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); + if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) + goto cleanup; + + if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) ) + { + //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); + goto cleanup; + } + + if ( cbNeeded > TTBUFLEN ) + { + //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); + goto cleanup; + } + + for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ ) + { + // base address, size + pGMI(hProcess, hMods[i], &mi, sizeof mi ); + // image file name + tt[0] = 0; + pGMFNE(hProcess, hMods[i], tt, TTBUFLEN ); + // module name + tt2[0] = 0; + pGMBN(hProcess, hMods[i], tt2, TTBUFLEN ); + + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); + if (dwRes != ERROR_SUCCESS) + this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); + cnt++; + } + + cleanup: + if (hPsapi != NULL) FreeLibrary(hPsapi); + if (tt2 != NULL) free(tt2); + if (tt != NULL) free(tt); + if (hMods != NULL) free(hMods); + + return cnt != 0; + } // GetModuleListPSAPI + + DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) + { + CHAR *szImg = _strdup(img); + CHAR *szMod = _strdup(mod); + DWORD result = ERROR_SUCCESS; + if ( (szImg == NULL) || (szMod == NULL) ) + result = ERROR_NOT_ENOUGH_MEMORY; + else + { + if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) + result = GetLastError(); + } + ULONGLONG fileVersion = 0; + if ( (m_parent != NULL) && (szImg != NULL) ) + { + // try to retrive the file-version: + if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) + { + VS_FIXEDFILEINFO *fInfo = NULL; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + if (dwSize > 0) + { + LPVOID vData = malloc(dwSize); + if (vData != NULL) + { + if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) + { + UINT len; + TCHAR szSubBlock[] = _T("\\"); + if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) + fInfo = NULL; + else + { + fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + } + } + free(vData); + } + } + } + + // Retrive some additional-infos about the module + IMAGEHLP_MODULE64_V2 Module; + const char *szSymType = "-unknown-"; + if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) + { + switch(Module.SymType) + { + case SymNone: + szSymType = "-nosymbols-"; + break; + case SymCoff: // 1 + szSymType = "COFF"; + break; + case SymCv: // 2 + szSymType = "CV"; + break; + case SymPdb: // 3 + szSymType = "PDB"; + break; + case SymExport: // 4 + szSymType = "-exported-"; + break; + case SymDeferred: // 5 + szSymType = "-deferred-"; + break; + case SymSym: // 6 + szSymType = "SYM"; + break; + case 7: // SymDia: + szSymType = "DIA"; + break; + case 8: //SymVirtual: + szSymType = "Virtual"; + break; + } + } + this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion); + } + if (szImg != NULL) free(szImg); + if (szMod != NULL) free(szMod); + return result; + } +public: + BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) + { + // first try toolhelp32 + if (GetModuleListTH32(hProcess, dwProcessId)) + return true; + // then try psapi + return GetModuleListPSAPI(hProcess); + } + + + BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo) + { + if(this->pSGMI == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + // First try to use the larger ModuleInfo-Structure +// memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); +// pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); +// if (this->pSGMI_V3 != NULL) +// { +// if (this->pSGMI_V3(hProcess, baseAddr, pModuleInfo) != FALSE) +// return TRUE; +// // check if the parameter was wrong (size is bad...) +// if (GetLastError() != ERROR_INVALID_PARAMETER) +// return FALSE; +// } + // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)... + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + if (pData == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + free(pData); + return TRUE; + } + free(pData); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } +}; + +// ############################################################# +StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = OptionsAll; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + this->m_szSymPath = NULL; + this->m_MaxRecursionCount = 1000; +} +StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = options; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + if (szSymPath != NULL) + { + this->m_szSymPath = _strdup(szSymPath); + this->m_options |= SymBuildPath; + } + else + this->m_szSymPath = NULL; + this->m_MaxRecursionCount = 1000; +} + +StackWalker::~StackWalker() +{ + if (m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + if (this->m_sw != NULL) + delete this->m_sw; + this->m_sw = NULL; +} + +BOOL StackWalker::LoadModules() +{ + if (this->m_sw == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + if (m_modulesLoaded != FALSE) + return TRUE; + + // Build the sym-path: + char *szSymPath = NULL; + if ( (this->m_options & SymBuildPath) != 0) + { + const size_t nSymPathLen = 4096; + szSymPath = (char*) malloc(nSymPathLen); + if (szSymPath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + szSymPath[0] = 0; + // Now first add the (optional) provided sympath: + if (this->m_szSymPath != NULL) + { + strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + strcat_s(szSymPath, nSymPathLen, ".;"); + + const size_t nTempLen = 1024; + char szTemp[nTempLen]; + // Now add the current directory: + if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + // Now add the path for the main-module: + if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) + { + // locate the rightmost path separator + if ( (*p == '\\') || (*p == '/') || (*p == ':') ) + { + *p = 0; + break; + } + } // for (search for path separator...) + if (strlen(szTemp) > 0) + { + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + } + if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + // also add the "system32"-directory: + strcat_s(szTemp, nTempLen, "\\system32"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + if ( (this->m_options & SymUseSymSrv) != 0) + { + if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, "SRV*"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, "\\websymbols"); + strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); + } + else + strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + } + } // if SymBuildPath + + // First Init the whole stuff... + BOOL bRet = this->m_sw->Init(szSymPath); + if (szSymPath != NULL) free(szSymPath); szSymPath = NULL; + if (bRet == FALSE) + { + this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); + if (bRet != FALSE) + m_modulesLoaded = TRUE; + return bRet; +} + + +// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction +// This has to be done due to a problem with the "hProcess"-parameter in x64... +// Because this class is in no case multi-threading-enabled (because of the limitations +// of dbghelp.dll) it is "safe" to use a static-variable +static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; +static LPVOID s_readMemoryFunction_UserData = NULL; + +BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +{ + CONTEXT c; + CallstackEntry csEntry; + IMAGEHLP_SYMBOL64 *pSym = NULL; + StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module; + IMAGEHLP_LINE64 Line; + int frameNum; + bool bLastEntryCalled = true; + int curRecursionCount = 0; + + if (m_modulesLoaded == FALSE) + this->LoadModules(); // ignore the result... + + if (this->m_sw->m_hDbhHelp == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + s_readMemoryFunction = readMemoryFunction; + s_readMemoryFunction_UserData = pUserData; + + if (context == NULL) + { + // If no context is provided, capture the context + if (hThread == GetCurrentThread()) + { + GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS); + } + else + { + SuspendThread(hThread); + memset(&c, 0, sizeof(CONTEXT)); + c.ContextFlags = USED_CONTEXT_FLAGS; + if (GetThreadContext(hThread, &c) == FALSE) + { + ResumeThread(hThread); + return FALSE; + } + } + } + else + c = *context; + + // init STACKFRAME for first call + STACKFRAME64 s; // in/out stackframe + memset(&s, 0, sizeof(s)); + DWORD imageType; +#ifdef _M_IX86 + // normally, call ImageNtHeader() and use machine info from PE header + imageType = IMAGE_FILE_MACHINE_I386; + s.AddrPC.Offset = c.Eip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Ebp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Esp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + imageType = IMAGE_FILE_MACHINE_AMD64; + s.AddrPC.Offset = c.Rip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Rsp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Rsp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + imageType = IMAGE_FILE_MACHINE_IA64; + s.AddrPC.Offset = c.StIIP; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.IntSp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrBStore.Offset = c.RsBSP; + s.AddrBStore.Mode = AddrModeFlat; + s.AddrStack.Offset = c.IntSp; + s.AddrStack.Mode = AddrModeFlat; +#else +#error "Platform not supported!" +#endif + + pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) goto cleanup; // not enough memory... + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + + memset(&Line, 0, sizeof(Line)); + Line.SizeOfStruct = sizeof(Line); + + memset(&Module, 0, sizeof(Module)); + Module.SizeOfStruct = sizeof(Module); + + for (frameNum = 0; ; ++frameNum ) + { + // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) + // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can + // assume that either you are done, or that the stack is so hosed that the next + // deeper frame could not be found. + // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) ) + { + // INFO: "StackWalk64" does not set "GetLastError"... + this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); + break; + } + + csEntry.offset = s.AddrPC.Offset; + csEntry.name[0] = 0; + csEntry.undName[0] = 0; + csEntry.undFullName[0] = 0; + csEntry.offsetFromSmybol = 0; + csEntry.offsetFromLine = 0; + csEntry.lineFileName[0] = 0; + csEntry.lineNumber = 0; + csEntry.loadedImageName[0] = 0; + csEntry.moduleName[0] = 0; + if (s.AddrPC.Offset == s.AddrReturn.Offset) + { + if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) ) + { + this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); + break; + } + curRecursionCount++; + } + else + curRecursionCount = 0; + if (s.AddrPC.Offset != 0) + { + // we seem to have a valid PC + // show procedure info (SymGetSymFromAddr64()) + if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + { + MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name); + // UnDecorateSymbolName() + this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); + this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); + } + else + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show line number info, NT5.0-method (SymGetLineFromAddr64()) + if (this->m_sw->pSGLFA != NULL ) + { // yes, we have SymGetLineFromAddr64() + if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) + { + csEntry.lineNumber = Line.LineNumber; + MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName); + } + else + { + this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); + } + } // yes, we have SymGetLineFromAddr64() + + // show module info (SymGetModuleInfo64()) + if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE) + { // got module info OK + switch ( Module.SymType ) + { + case SymNone: + csEntry.symTypeString = "-nosymbols-"; + break; + case SymCoff: + csEntry.symTypeString = "COFF"; + break; + case SymCv: + csEntry.symTypeString = "CV"; + break; + case SymPdb: + csEntry.symTypeString = "PDB"; + break; + case SymExport: + csEntry.symTypeString = "-exported-"; + break; + case SymDeferred: + csEntry.symTypeString = "-deferred-"; + break; + case SymSym: + csEntry.symTypeString = "SYM"; + break; +#if API_VERSION_NUMBER >= 9 + case SymDia: + csEntry.symTypeString = "DIA"; + break; +#endif + case 8: //SymVirtual: + csEntry.symTypeString = "Virtual"; + break; + default: + //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType ); + csEntry.symTypeString = NULL; + break; + } + + // TODO: Mache dies sicher...! + MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName); + csEntry.baseOfImage = Module.BaseOfImage; + MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName); + } // got module info OK + else + { + this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); + } + } // we seem to have a valid PC + + CallstackEntryType et = nextEntry; + if (frameNum == 0) + et = firstEntry; + bLastEntryCalled = false; + this->OnCallstackEntry(et, csEntry); + + if (s.AddrReturn.Offset == 0) + { + bLastEntryCalled = true; + this->OnCallstackEntry(lastEntry, csEntry); + SetLastError(ERROR_SUCCESS); + break; + } + } // for ( frameNum ) + + cleanup: + if (pSym) free( pSym ); + + if (bLastEntryCalled == false) + this->OnCallstackEntry(lastEntry, csEntry); + + if (context == NULL) + ResumeThread(hThread); + + return TRUE; +} + +BOOL __stdcall StackWalker::myReadProcMem( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ) +{ + if (s_readMemoryFunction == NULL) + { + SIZE_T st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); + return bRet; + } + else + { + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + } +} + +void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if (fileVersion == 0) + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName); + else + { + DWORD v4 = (DWORD) fileVersion & 0xFFFF; + DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF; + DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF; + DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + } + OnOutput(buffer); +} + +void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if (entry.name[0] == 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)"); + if (entry.undName[0] != 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName); + if (entry.undFullName[0] != 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName); + if (entry.lineFileName[0] == 0) + { + MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)"); + if (entry.moduleName[0] == 0) + MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)"); + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); + } + else + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); + buffer[STACKWALK_MAX_NAMELEN-1] = 0; + OnOutput(buffer); + } +} + +void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); + OnOutput(buffer); +} + +void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + OnOutput(buffer); + // Also display the OS-version +#if _MSC_VER <= 1200 + OSVERSIONINFOA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA(&ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion); + OnOutput(buffer); + } +#else + OSVERSIONINFOEXA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); + OnOutput(buffer); + } +#endif +} + +void StackWalker::OnOutput(LPCSTR buffer) +{ + OutputDebugStringA(buffer); +} diff --git a/src/StackWalker.h b/src/StackWalker.h new file mode 100644 index 000000000..bf47d3726 --- /dev/null +++ b/src/StackWalker.h @@ -0,0 +1,214 @@ +/********************************************************************** + * + * StackWalker.h + * + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * **********************************************************************/ +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#include <windows.h> + +// special defines for VC5/6 (if no actual PSDK is installed): +#if _MSC_VER < 1300 +typedef unsigned __int64 DWORD64, *PDWORD64; +#if defined(_WIN64) +typedef unsigned __int64 SIZE_T, *PSIZE_T; +#else +typedef unsigned long SIZE_T, *PSIZE_T; +#endif +#endif // _MSC_VER < 1300 + +class StackWalkerInternal; // forward +class StackWalker +{ +public: + typedef enum StackWalkOptions + { + // No addition info will be retrived + // (only the address is available) + RetrieveNone = 0, + + // Try to get the symbol-name + RetrieveSymbol = 1, + + // Try to get the line for this symbol + RetrieveLine = 2, + + // Try to retrieve the module-infos + RetrieveModuleInfo = 4, + + // Also retrieve the version for the DLL/EXE + RetrieveFileVersion = 8, + + // Contains all the abouve + RetrieveVerbose = 0xF, + + // Generate a "good" symbol-search-path + SymBuildPath = 0x10, + + // Also use the public Microsoft-Symbol-Server + SymUseSymSrv = 0x20, + + // Contains all the abouve "Sym"-options + SymAll = 0x30, + + // Contains all options (default) + OptionsAll = 0x3F + } StackWalkOptions; + + StackWalker( + int options = OptionsAll, // 'int' is by design, to combine the enum-flags + LPCSTR szSymPath = NULL, + DWORD dwProcessId = GetCurrentProcessId(), + HANDLE hProcess = GetCurrentProcess() + ); + StackWalker(DWORD dwProcessId, HANDLE hProcess); + virtual ~StackWalker(); + + typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ); + + BOOL LoadModules(); + + BOOL ShowCallstack( + HANDLE hThread = GetCurrentThread(), + const CONTEXT *context = NULL, + PReadProcessMemoryRoutine readMemoryFunction = NULL, + LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback + ); + +#if _MSC_VER >= 1300 +// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" +// in older compilers in order to use it... starting with VC7 we can declare it as "protected" +protected: +#endif + enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols + +protected: + // Entry for each Callstack-Entry + typedef struct CallstackEntry + { + DWORD64 offset; // if 0, we have no valid entry + CHAR name[STACKWALK_MAX_NAMELEN]; + CHAR undName[STACKWALK_MAX_NAMELEN]; + CHAR undFullName[STACKWALK_MAX_NAMELEN]; + DWORD64 offsetFromSmybol; + DWORD offsetFromLine; + DWORD lineNumber; + CHAR lineFileName[STACKWALK_MAX_NAMELEN]; + DWORD symType; + LPCSTR symTypeString; + CHAR moduleName[STACKWALK_MAX_NAMELEN]; + DWORD64 baseOfImage; + CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; + } CallstackEntry; + + typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + + virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); + virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); + virtual void OnOutput(LPCSTR szText); + + StackWalkerInternal *m_sw; + HANDLE m_hProcess; + DWORD m_dwProcessId; + BOOL m_modulesLoaded; + LPSTR m_szSymPath; + + int m_options; + int m_MaxRecursionCount; + + static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); + + friend StackWalkerInternal; +}; + + +// The "ugly" assembler-implementation is needed for systems before XP +// If you have a new PSDK and you only compile for XP and later, then you can use +// the "RtlCaptureContext" +// Currently there is no define which determines the PSDK-Version... +// So we just use the compiler-version (and assumes that the PSDK is +// the one which was installed by the VS-IDE) + +// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... +// But I currently use it in x64/IA64 environments... +//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) + +#if defined(_M_IX86) +#ifdef CURRENT_THREAD_VIA_EXCEPTION +// TODO: The following is not a "good" implementation, +// because the callstack is only valid in the "__except" block... +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + EXCEPTION_POINTERS *pExp = NULL; \ + __try { \ + throw 0; \ + } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ + if (pExp != NULL) \ + memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + } while(0); +#else +// The following should be enough for walking the callstack... +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + __asm call x \ + __asm x: pop eax \ + __asm mov c.Eip, eax \ + __asm mov c.Ebp, ebp \ + __asm mov c.Esp, esp \ + } while(0); +#endif + +#else + +// The following is defined for x86 (XP and higher), x64 and IA64: +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + RtlCaptureContext(&c); \ +} while(0); +#endif diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp new file mode 100644 index 000000000..36946b282 --- /dev/null +++ b/src/StringCompression.cpp @@ -0,0 +1,180 @@ + +// StringCompression.cpp + +// Implements the wrapping functions for compression and decompression using AString as their data + +#include "Globals.h" +#include "StringCompression.h" + + + + + +/// Compresses a_Data into a_Compressed; returns Z_XXX error constants same as zlib's compress2() +int CompressString(const char * a_Data, int a_Length, AString & a_Compressed) +{ + uLongf CompressedSize = compressBound(a_Length); + + // HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer! + // It saves us one allocation and one memcpy of the entire compressed data + // It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010) + a_Compressed.resize(CompressedSize); + int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, Z_DEFAULT_COMPRESSION); + if (errorcode != Z_OK) + { + return errorcode; + } + a_Compressed.resize(CompressedSize); + return Z_OK; +} + + + + + +/// Uncompresses a_Data into a_Decompressed; returns Z_XXX error constants same as zlib's uncompress() +int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize) +{ + // HACK: We're assuming that AString returns its internal buffer in its data() call and we're overwriting that buffer! + // It saves us one allocation and one memcpy of the entire compressed data + // It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010) + a_Uncompressed.resize(a_UncompressedSize); + uLongf UncompressedSize = (uLongf)a_UncompressedSize; // On some architectures the uLongf is different in size to int, that may be the cause of the -5 error + int errorcode = uncompress((Bytef*)a_Uncompressed.data(), &UncompressedSize, (const Bytef*)a_Data, a_Length); + if (errorcode != Z_OK) + { + return errorcode; + } + a_Uncompressed.resize(UncompressedSize); + return Z_OK; +} + + + + + +int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed) +{ + // Compress a_Data into a_Compressed using GZIP; return Z_XXX error constants same as zlib's compress2() + + a_Compressed.reserve(a_Length); + + char Buffer[64 KiB]; + z_stream strm; + memset(&strm, 0, sizeof(strm)); + strm.next_in = (Bytef *)a_Data; + strm.avail_in = a_Length; + strm.next_out = (Bytef *)Buffer; + strm.avail_out = sizeof(Buffer); + + int res = deflateInit2(&strm, 9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY); + if (res != Z_OK) + { + LOG("%s: compression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); + return res; + } + + while (true) + { + res = deflate(&strm, Z_FINISH); + switch (res) + { + case Z_OK: + { + // Some data has been compressed. Consume the buffer and continue compressing + a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + strm.avail_out = sizeof(Buffer); + if (strm.avail_in == 0) + { + // All data has been compressed + deflateEnd(&strm); + return Z_OK; + } + break; + } + + case Z_STREAM_END: + { + // Finished compressing. Consume the rest of the buffer and return + a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + deflateEnd(&strm); + return Z_OK; + } + + default: + { + // An error has occurred, log it and return the error value + LOG("%s: compression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); + deflateEnd(&strm); + return res; + } + } // switch (res) + } // while (true) +} + + + + + +extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_Uncompressed) +{ + // Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib + + a_Uncompressed.reserve(a_Length); + + char Buffer[64 KiB]; + z_stream strm; + memset(&strm, 0, sizeof(strm)); + strm.next_in = (Bytef *)a_Data; + strm.avail_in = a_Length; + strm.next_out = (Bytef *)Buffer; + strm.avail_out = sizeof(Buffer); + + int res = inflateInit2(&strm, 31); // Force GZIP decoding + if (res != Z_OK) + { + LOG("%s: uncompression initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); + return res; + } + + while (true) + { + res = inflate(&strm, Z_FINISH); + switch (res) + { + case Z_OK: + { + // Some data has been uncompressed. Consume the buffer and continue uncompressing + a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + strm.avail_out = sizeof(Buffer); + if (strm.avail_in == 0) + { + // All data has been uncompressed + inflateEnd(&strm); + return Z_OK; + } + break; + } + + case Z_STREAM_END: + { + // Finished uncompressing. Consume the rest of the buffer and return + a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + inflateEnd(&strm); + return Z_OK; + } + + default: + { + // An error has occurred, log it and return the error value + LOG("%s: uncompression failed: %d (\"%s\").", __FUNCTION__, res, strm.msg); + inflateEnd(&strm); + return res; + } + } // switch (res) + } // while (true) +} + + + + diff --git a/src/StringCompression.h b/src/StringCompression.h new file mode 100644 index 000000000..459e8f568 --- /dev/null +++ b/src/StringCompression.h @@ -0,0 +1,25 @@ + +// StringCompression.h + +// Interfaces to the wrapping functions for compression and decompression using AString as their data + +#include "zlib/zlib.h" // Needed for the Z_XXX return values + + + + + +/// Compresses a_Data into a_Compressed using ZLIB; returns Z_XXX error constants same as zlib's compress2() +extern int CompressString(const char * a_Data, int a_Length, AString & a_Compressed); + +/// Uncompresses a_Data into a_Uncompressed; returns Z_XXX error constants same as zlib's decompress() +extern int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize); + +/// Compresses a_Data into a_Compressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib +extern int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed); + +/// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib +extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_Uncompressed); + + + diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp new file mode 100644 index 000000000..f7aeeed26 --- /dev/null +++ b/src/StringUtils.cpp @@ -0,0 +1,766 @@ + +// StringUtils.cpp + +// Implements the various string helper functions: + +#include "Globals.h" + +#if defined(ANDROID_NDK) +#include <ctype.h> +#endif + +#ifdef _MSC_VER + // Under MSVC, link to WinSock2 (needed by RawBEToUTF8's byteswapping) + #pragma comment(lib, "ws2_32.lib") +#endif + + + + + +AString & AppendVPrintf(AString & str, const char *format, va_list args) +{ + ASSERT(format != NULL); + + char buffer[2048]; + size_t len; + #ifdef _MSC_VER + // MS CRT provides secure printf that doesn't behave like in the C99 standard + if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, args)) != -1) + #else // _MSC_VER + if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, args)) < ARRAYCOUNT(buffer)) + #endif // else _MSC_VER + { + // The result did fit into the static buffer + str.append(buffer, len); + return str; + } + + // The result did not fit into the static buffer + #ifdef _MSC_VER + // for MS CRT, we need to calculate the result length + len = _vscprintf(format, args); + if (len == -1) + { + return str; + } + #endif // _MSC_VER + + // Allocate a buffer and printf into it: + str.resize(len + 1); + // HACK: we're accessing AString's internal buffer in a way that is NOT guaranteed to always work. But it works on all STL implementations tested. + // I can't think of any other way that is safe, doesn't allocate twice as much space as needed and doesn't use C++11 features like the move constructor + #ifdef _MSC_VER + vsprintf_s((char *)str.data(), len + 1, format, args); + #else // _MSC_VER + vsnprintf((char *)str.data(), len + 1, format, args); + #endif // else _MSC_VER + str.resize(len); + return str; +} + + + + + +AString & Printf(AString & str, const char * format, ...) +{ + str.clear(); + va_list args; + va_start(args, format); + std::string &retval = AppendVPrintf(str, format, args); + va_end(args); + return retval; +} + + + + + +AString Printf(const char * format, ...) +{ + AString res; + va_list args; + va_start(args, format); + AppendVPrintf(res, format, args); + va_end(args); + return res; +} + + + + + +AString & AppendPrintf(AString &str, const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string &retval = AppendVPrintf(str, format, args); + va_end(args); + return retval; +} + + + + + +AStringVector StringSplit(const AString & str, const AString & delim) +{ + AStringVector results; + size_t cutAt = 0; + size_t Prev = 0; + while ((cutAt = str.find_first_of(delim, Prev)) != str.npos) + { + results.push_back(str.substr(Prev, cutAt - Prev)); + Prev = cutAt + 1; + } + if (Prev < str.length()) + { + results.push_back(str.substr(Prev)); + } + return results; +} + + + + + +AStringVector StringSplitAndTrim(const AString & str, const AString & delim) +{ + AStringVector results; + size_t cutAt = 0; + size_t Prev = 0; + while ((cutAt = str.find_first_of(delim, Prev)) != str.npos) + { + results.push_back(TrimString(str.substr(Prev, cutAt - Prev))); + Prev = cutAt + 1; + } + if (Prev < str.length()) + { + results.push_back(TrimString(str.substr(Prev))); + } + return results; +} + + + + +AString TrimString(const AString & str) +{ + size_t len = str.length(); + size_t start = 0; + while (start < len) + { + if (str[start] > 32) + { + break; + } + ++start; + } + if (start == len) + { + return ""; + } + + size_t end = len; + while (end >= start) + { + if (str[end] > 32) + { + break; + } + --end; + } + + return str.substr(start, end - start + 1); +} + + + + + +AString & StrToUpper(AString & s) +{ + AString::iterator i = s.begin(); + AString::iterator end = s.end(); + + while (i != end) + { + *i = (char)toupper(*i); + ++i; + } + return 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 + // MSVC has stricmp that compares case-insensitive: + return _stricmp(s1.c_str(), s2.c_str()); + #else + // Do it the hard way: + AString s1Copy(s1); + AString s2Copy(s2); + return StrToUpper(s1Copy).compare(StrToUpper(s2Copy)); + #endif // else _MSC_VER +} + + + + + +unsigned int RateCompareString(const AString & s1, const AString & s2 ) +{ + unsigned int MatchedLetters = 0; + unsigned int s1Length = s1.length(); + + if( s1Length > s2.length() ) return 0; // Definitely not a match + + for (unsigned int i = 0; i < s1Length; i++) + { + char c1 = (char)toupper( s1[i] ); + char c2 = (char)toupper( s2[i] ); + if( c1 == c2 ) + { + ++MatchedLetters; + } + else + { + break; + } + } + return MatchedLetters; +} + + + + + +void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith) +{ + size_t pos1 = iHayStack.find(iNeedle); + while (pos1 != AString::npos) + { + iHayStack.replace( pos1, iNeedle.size(), iReplaceWith); + pos1 = iHayStack.find(iNeedle, pos1); + } +} + + + + +// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8 +AString & RawBEToUTF8(short * a_RawData, int a_NumShorts, AString & a_UTF8) +{ + a_UTF8.clear(); + a_UTF8.reserve(3 * a_NumShorts / 2); // a quick guess of the resulting size + for (int i = 0; i < a_NumShorts; i++) + { + int c = ntohs(*(a_RawData + i)); + if (c < 0x80) + { + a_UTF8.push_back((char)c); + } + else if (c < 0x800) + { + a_UTF8.push_back((char)(192 + c / 64)); + a_UTF8.push_back((char)(128 + c % 64)); + } + else if (c - 0xd800u < 0x800) + { + // Error, silently drop + } + else if (c < 0x10000) + { + a_UTF8.push_back((char)(224 + c / 4096)); + a_UTF8.push_back((char)(128 + c / 64 % 64)); + a_UTF8.push_back((char)(128 + c % 64)); + } + else if (c < 0x110000) + { + a_UTF8.push_back((char)(240 + c / 262144)); + a_UTF8.push_back((char)(128 + c / 4096 % 64)); + a_UTF8.push_back((char)(128 + c / 64 % 64)); + a_UTF8.push_back((char)(128 + c % 64)); + } + else + { + // Error, silently drop + } + } + return a_UTF8; +} + + + + +// UTF-8 conversion code adapted from: +// http://stackoverflow.com/questions/2867123/convert-utf-16-to-utf-8-under-windows-and-linux-in-c + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Begin of Unicode, Inc.'s code / information +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +Notice from the original file: +* Copyright 2001-2004 Unicode, Inc. +* +* Disclaimer +* +* This source code is provided as is by Unicode, Inc. No claims are +* made as to fitness for any particular purpose. No warranties of any +* kind are expressed or implied. The recipient agrees to determine +* applicability of information provided. If this file has been +* purchased on magnetic or optical media from Unicode, Inc., the +* sole remedy for any claim will be exchange of defective media +* within 90 days of receipt. +* +* Limitations on Rights to Redistribute This Code +* +* Unicode, Inc. hereby grants the right to freely use the information +* supplied in this file in the creation of products supporting the +* Unicode Standard, and to make copies of this file in any form +* for internal or external distribution as long as this notice +* remains attached. +*/ + +#define UNI_MAX_BMP 0x0000FFFF +#define UNI_MAX_UTF16 0x0010FFFF +#define UNI_MAX_UTF32 0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 0x0010FFFF +#define UNI_SUR_HIGH_START 0xD800 +#define UNI_SUR_HIGH_END 0xDBFF +#define UNI_SUR_LOW_START 0xDC00 +#define UNI_SUR_LOW_END 0xDFFF + + + + + +static const char trailingBytesForUTF8[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 +}; + + + + + +static const unsigned int offsetsFromUTF8[6] = +{ + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + + + + + +static bool isLegalUTF8(const unsigned char * source, int length) +{ + unsigned char a; + const unsigned char * srcptr = source + length; + switch (length) + { + default: return false; + // Everything else falls through when "true"... + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: + { + if ((a = (*--srcptr)) > 0xBF) return false; + switch (*source) + { + // no fall-through in this inner switch + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + } + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + + + + + +AString & UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length, AString & a_UTF16) +{ + a_UTF16.clear(); + a_UTF16.reserve(a_UTF8Length * 3); + + const unsigned char * source = (const unsigned char*)a_UTF8; + const unsigned char * sourceEnd = source + a_UTF8Length; + const int halfShift = 10; // used for shifting by 10 bits + const unsigned int halfBase = 0x0010000UL; + const unsigned int halfMask = 0x3FFUL; + + while (source < sourceEnd) + { + unsigned int ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) + { + return a_UTF16; + } + // Do this check whether lenient or strict + if (!isLegalUTF8(source, extraBytesToRead + 1)) + { + return a_UTF16; + break; + } + + // The cases all fall through. See "Note A" below. + switch (extraBytesToRead) + { + case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (ch <= UNI_MAX_BMP) + { + // Target is a character <= 0xFFFF + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + { + // UTF-16 surrogate values are illegal in UTF-32 + ch = ' '; + } + unsigned short v = htons((unsigned short)ch); + a_UTF16.append((const char *)&v, 2); + } + else if (ch > UNI_MAX_UTF16) + { + // Invalid value, replace with a space + unsigned short v = htons(' '); + a_UTF16.append((const char *)&v, 2); + } + else + { + // target is a character in range 0xFFFF - 0x10FFFF. + ch -= halfBase; + unsigned short v1 = htons((ch >> halfShift) + UNI_SUR_HIGH_START); + unsigned short v2 = htons((ch & halfMask) + UNI_SUR_LOW_START); + a_UTF16.append((const char *)&v1, 2); + a_UTF16.append((const char *)&v2, 2); + } + } + return a_UTF16; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + + --------------------------------------------------------------------- +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// End of Unicode, Inc.'s code / information +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + + + + +#define HEX(x) ((x) > 9 ? (x) + 'A' - 10 : (x) + '0') + +/** +format binary data this way: +00001234: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 1234567890abcdef +*/ +AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_LineLength) +{ + ASSERT(a_LineLength <= 120); // Due to using a fixed size line buffer; increase line[]'s size to lift this max + char line[512]; + char * p; + char * q; + + a_Out.reserve(a_Size / a_LineLength * (18 + 6 * a_LineLength)); + for (int i = 0; i < a_Size; i += a_LineLength) + { + int k = a_Size - i; + if (k > a_LineLength) + { + k = a_LineLength; + } + #ifdef _MSC_VER + // MSVC provides a "secure" version of sprintf() + int Count = sprintf_s(line, sizeof(line), "%08x:", i); + #else + int Count = sprintf(line, "%08x:", i); + #endif + // Remove the terminating NULL / leftover garbage in line, after the sprintf-ed value + memset(line + Count, 32, sizeof(line) - Count); + p = line + 10; + q = p + 2 + a_LineLength * 3 + 1; + for (int j = 0; j < k; j++) + { + unsigned char c = ((unsigned char *)a_Data)[i + j]; + p[0] = HEX(c >> 4); + p[1] = HEX(c & 0xf); + p[2] = ' '; + if (c >= ' ') + { + q[0] = (char)c; + } + else + { + q[0] = '.'; + } + p += 3; + q ++; + } // for j + q[0] = '\n'; + q[1] = 0; + a_Out.append(line); + } // for i + return a_Out; +} + + + + + +AString EscapeString(const AString & a_Message) +{ + AString EscapedMsg; + size_t len = a_Message.size(); + size_t last = 0; + EscapedMsg.reserve(len); + for (size_t i = 0; i < len; i++) + { + char ch = a_Message[i]; + switch (ch) + { + case '\'': + case '\"': + case '\\': + { + if (i > last) + { + EscapedMsg.append(a_Message, last, i - last); + } + EscapedMsg.push_back('\\'); + EscapedMsg.push_back(ch); + last = i + 1; + break; + } + } // switch (ch) + } // for i - a_Message[] + if (len > last) + { + EscapedMsg.append(a_Message, last, len - last); + } + return EscapedMsg; +} + + + + + +AString StripColorCodes(const AString & a_Message) +{ + AString res(a_Message); + size_t idx = 0; + while (true) + { + idx = res.find("\xc2\xa7", idx); + if (idx == AString::npos) + { + return res; + } + res.erase(idx, 3); + } +} + + + + + +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/src/StringUtils.h b/src/StringUtils.h new file mode 100644 index 000000000..3917cc4ec --- /dev/null +++ b/src/StringUtils.h @@ -0,0 +1,93 @@ + +// StringUtils.h + +// Interfaces to various string helper functions + + + + +#ifndef STRINGUTILS_H_INCLUDED +#define STRINGUTILS_H_INCLUDED + + + + + +typedef std::string AString; +typedef std::vector<AString> AStringVector; +typedef std::list<AString> AStringList; + + + + + +/// Add the formated string to the existing data in the string +extern AString & AppendVPrintf(AString & str, const char * format, va_list args); + +/// Output the formatted text into the string +extern AString & Printf (AString & str, const char * format, ...); + +/// Output the formatted text into string, return string by value +extern AString Printf(const char * format, ...); + +/// Add the formatted string to the existing data in the string +extern AString & AppendPrintf (AString & str, const char * format, ...); + +/// Split the string at any of the listed delimiters, return as a stringvector +extern AStringVector StringSplit(const AString & str, const AString & delim); + +/// Split the string at any of the listed delimiters and trim each value, return as a stringvector +extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim); + +/// Trime whitespace at both ends of the string +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 + +/// Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()] +extern unsigned int RateCompareString(const AString & s1, const AString & s2 ); + +/// Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith +extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); // tolua_export + +/// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8 +extern AString & RawBEToUTF8(short * a_RawData, int a_NumShorts, AString & a_UTF8); + +/// Converts a UTF-8 string into a UTF-16 BE string, packing that back into AString; return a ref to a_UTF16 +extern AString & UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length, AString & a_UTF16); + +/// Creates a nicely formatted HEX dump of the given memory block. Max a_BytesPerLine is 120 +extern AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_BytesPerLine); + +/// Returns a copy of a_Message with all quotes and backslashes escaped by a backslash +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 + + + + +#endif // STRINGUTILS_H_INCLUDED + + + + diff --git a/src/Tracer.cpp b/src/Tracer.cpp new file mode 100644 index 000000000..ef136302f --- /dev/null +++ b/src/Tracer.cpp @@ -0,0 +1,398 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Tracer.h" +#include "World.h" + +#include "Vector3f.h" +#include "Vector3i.h" +#include "Vector3d.h" + +#include "Entities/Entity.h" + +#ifndef _WIN32 + #include <stdlib.h> // abs() +#endif + + + + + +cTracer::cTracer(cWorld * a_World) + : m_World(a_World) +{ + m_NormalTable[0].Set(-1, 0, 0); + m_NormalTable[1].Set( 0, 0,-1); + m_NormalTable[2].Set( 1, 0, 0); + m_NormalTable[3].Set( 0, 0, 1); + m_NormalTable[4].Set( 0, 1, 0); + m_NormalTable[5].Set( 0,-1, 0); +} + + + + + +cTracer::~cTracer() +{ +} + + + + + +float cTracer::SigNum( float a_Num ) +{ + if (a_Num < 0.f) return -1.f; + if (a_Num > 0.f) return 1.f; + return 0.f; +} + + + + + +void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) +{ + // calculate the direction of the ray (linear algebra) + dir = a_Direction; + + // decide which direction to start walking in + step.x = (int) SigNum(dir.x); + step.y = (int) SigNum(dir.y); + step.z = (int) SigNum(dir.z); + + // normalize the direction vector + if( dir.SqrLength() > 0.f ) dir.Normalize(); + + // how far we must move in the ray direction before + // we encounter a new voxel in x-direction + // same but y-direction + if (dir.x != 0.f) + { + tDelta.x = 1 / fabs(dir.x); + } + else + { + tDelta.x = 0; + } + if (dir.y != 0.f) + { + tDelta.y = 1 / fabs(dir.y); + } + else + { + tDelta.y = 0; + } + if (dir.z != 0.f) + { + tDelta.z = 1 / fabs(dir.z); + } + else + { + tDelta.z = 0; + } + + // start voxel coordinates + pos.x = (int)floorf(a_Start.x); + pos.y = (int)floorf(a_Start.y); + pos.z = (int)floorf(a_Start.z); + + // calculate distance to first intersection in the voxel we start from + if (dir.x < 0) + { + tMax.x = ((float)pos.x - a_Start.x) / dir.x; + } + else + { + tMax.x = (((float)pos.x + 1) - a_Start.x) / dir.x; + } + + if (dir.y < 0) + { + tMax.y = ((float)pos.y - a_Start.y) / dir.y; + } + else + { + tMax.y = (((float)pos.y + 1) - a_Start.y) / dir.y; + } + + if (dir.z < 0) + { + tMax.z = ((float)pos.z - a_Start.z) / dir.z; + } + else + { + tMax.z = (((float)pos.z + 1) - a_Start.z) / dir.z; + } +} + + + + + +bool cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight) +{ + if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height)) + { + LOGD("%s: Start Y is outside the world (%.2f), not tracing.", __FUNCTION__, a_Start.y); + return false; + } + + SetValues(a_Start, a_Direction); + + Vector3f End = a_Start + (dir * (float)a_Distance); + + if (End.y < 0) + { + float dist = -a_Start.y / dir.y; + End = a_Start + (dir * dist); + } + + // end voxel coordinates + end1.x = (int)floorf(End.x); + end1.y = (int)floorf(End.y); + end1.z = (int)floorf(End.z); + + // check if first is occupied + if (pos.Equals(end1)) + { + return false; + } + + bool reachedX = false, reachedY = false, reachedZ = false; + + int Iterations = 0; + while (Iterations < a_Distance) + { + Iterations++; + if ((tMax.x < tMax.y) && (tMax.x < tMax.z)) + { + tMax.x += tDelta.x; + pos.x += step.x; + } + else if (tMax.y < tMax.z) + { + tMax.y += tDelta.y; + pos.y += step.y; + } + else + { + tMax.z += tDelta.z; + pos.z += step.z; + } + + if (step.x > 0.0f) + { + if (pos.x >= end1.x) + { + reachedX = true; + } + } + else if (pos.x <= end1.x) + { + reachedX = true; + } + + if (step.y > 0.0f) + { + if(pos.y >= end1.y) + { + reachedY = true; + } + } + else if (pos.y <= end1.y) + { + reachedY = true; + } + + if (step.z > 0.0f) + { + if (pos.z >= end1.z) + { + reachedZ = true; + } + } + else if (pos.z <= end1.z) + { + reachedZ = true; + } + + if (reachedX && reachedY && reachedZ) + { + return false; + } + + BLOCKTYPE BlockID = m_World->GetBlock(pos.x, pos.y, pos.z); + // Block is counted as a collision if we are not doing a line of sight and it is solid, + // or if the block is not air and not water. That way mobs can still see underwater. + if ((!a_LineOfSight && g_BlockIsSolid[BlockID]) || (a_LineOfSight && (BlockID != E_BLOCK_AIR) && !IsBlockWater(BlockID))) + { + BlockHitPosition = pos; + int Normal = GetHitNormal(a_Start, End, pos ); + if(Normal > 0) + { + HitNormal = m_NormalTable[Normal-1]; + } + return true; + } + } + return false; +} + + + + + +// return 1 = hit, other is not hit +int LinesCross(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3) +{ + //float linx, liny; + + float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2); + if (abs(d)<0.001) {return 0;} + float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d; + if (AB>=0.0 && AB<=1.0) + { + float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d; + if (CD>=0.0 && CD<=1.0) + { + //linx=x0+AB*(x1-x0); + //liny=y0+AB*(y1-y0); + return 1; + } + } + return 0; +} + +// intersect3D_SegmentPlane(): intersect a segment and a plane +// Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;} +// Output: *I0 = the intersect point (when it exists) +// Return: 0 = disjoint (no intersection) +// 1 = intersection in the unique point *I0 +// 2 = the segment lies in the plane +int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal ) +{ + Vector3f u = a_End - a_Origin;//a_Ray.P1 - S.P0; + Vector3f w = a_Origin - a_PlanePos;//S.P0 - Pn.V0; + + float D = a_PlaneNormal.Dot( u );//dot(Pn.n, u); + float N = -(a_PlaneNormal.Dot( w ) );//-dot(a_Plane.n, w); + + const float EPSILON = 0.0001f; + if (fabs(D) < EPSILON) { // segment is parallel to plane + if (N == 0) // segment lies in plane + return 2; + return 0; // no intersection + } + // they are not parallel + // compute intersect param + float sI = N / D; + if (sI < 0 || sI > 1) + return 0; // no intersection + + //Vector3f I ( a_Ray->GetOrigin() + sI * u );//S.P0 + sI * u; // compute segment intersect point + RealHit = a_Origin + u * sI; + return 1; +} + +int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos) +{ + Vector3i SmallBlockPos = a_BlockPos; + char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); + + if( BlockID == E_BLOCK_AIR || IsBlockWater(BlockID)) + return 0; + + Vector3f BlockPos; + BlockPos = Vector3f(SmallBlockPos); + + Vector3f Look = (end - start); + Look.Normalize(); + + float dot = Look.Dot( Vector3f(-1, 0, 0) ); // first face normal is x -1 + if(dot < 0) + { + int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0) ); + return 1; + } + } + } + dot = Look.Dot( Vector3f(0, 0, -1) ); // second face normal is z -1 + if(dot < 0) + { + int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1) ); + return 2; + } + } + } + dot = Look.Dot( Vector3f(1, 0, 0) ); // third face normal is x 1 + if(dot < 0) + { + int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0) ); + return 3; + } + } + } + dot = Look.Dot( Vector3f(0, 0, 1) ); // fourth face normal is z 1 + if(dot < 0) + { + int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1) ); + return 4; + } + } + } + dot = Look.Dot( Vector3f(0, 1, 0) ); // fifth face normal is y 1 + if(dot < 0) + { + int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0) ); + return 5; + } + } + } + dot = Look.Dot( Vector3f(0, -1, 0) ); // sixth face normal is y -1 + if(dot < 0) + { + int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0) ); + return 6; + } + } + } + return 0; +} diff --git a/src/Tracer.h b/src/Tracer.h new file mode 100644 index 000000000..6c2ab6792 --- /dev/null +++ b/src/Tracer.h @@ -0,0 +1,82 @@ + +#pragma once + +#include "Vector3i.h" +#include "Vector3f.h" + + + + + +// fwd: +class cWorld; + + + + + +// tolua_begin + +class cTracer +{ +public: + + /// Contains the position of the block that caused the collision + Vector3f BlockHitPosition; + + /// Contains which face was hit + Vector3f HitNormal; + + /// Contains the exact position where a collision occured. (BlockHitPosition + Offset on block) + Vector3f RealHit; + + + cTracer(cWorld * a_World); + ~cTracer(); + + /// Determines if a collision occures along a line. Returns true if a collision occurs. + bool Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance) + { + return Trace(a_Start, a_Direction, a_Distance, false); + } + + /// Determines if a collision occures along a line. Returns true if a collision occurs. + /// When a_LineOfSight is true, we don't use the standard collision detection rules. Instead we use + /// the rules for monster vision. E.g. Only water and air do not block vision. + /// a_Distance is the number of iterations (blocks hits) that are tested. + bool Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight); + +private: + + /// Preps Tracer object for call of Trace function. Only used internally. + void SetValues( const Vector3f & a_Start, const Vector3f & a_Direction ); + + /// Calculates where on the block a collision occured, if it does occur + /// Returns 0 if no intersection occured + /// Returns 1 if an intersection occured at a single point + /// Returns 2 if the line segment lies in the plane being checked + int intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal ); + + /// Determines which face on the block a collision occured, if it does occur + /// Returns 0 if the block is air, water or no collision occured + /// Return 1 through 6 for the following block faces, repectively: -x, -z, x, z, y, -y + int GetHitNormal( const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos); + + float SigNum( float a_Num ); + cWorld* m_World; + + Vector3f m_NormalTable[6]; + + Vector3f dir; + Vector3f tDelta; + Vector3i pos; + Vector3i end1; + Vector3i step; + Vector3f tMax; +}; + +// tolua_end + + + + diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp new file mode 100644 index 000000000..7fd7cd996 --- /dev/null +++ b/src/UI/SlotArea.cpp @@ -0,0 +1,897 @@ + +// SlotArea.cpp + +// Implements the cSlotArea class and its descendants + +#include "Globals.h" +#include "SlotArea.h" +#include "../Entities/Player.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DropSpenserEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../Items/ItemHandler.h" +#include "Window.h" +#include "../CraftingRecipes.h" +#include "../Root.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotArea: + +cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) : + m_NumSlots(a_NumSlots), + m_ParentWindow(a_ParentWindow) +{ +} + + + + + +void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + /* + LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s", + GetNumSlots(), a_SlotNum, + ItemToFullString(a_ClickedItem).c_str(), + ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str() + ); + */ + + ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots())); + + bool bAsync = false; + if (GetSlot(a_SlotNum, a_Player) == NULL) + { + LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); + return; + } + + switch (a_ClickAction) + { + case caShiftLeftClick: + case caShiftRightClick: + { + ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + return; + } + + case caDblClick: + { + DblClicked(a_Player, a_SlotNum); + return; + } + } + + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (!Slot.IsSameType(a_ClickedItem)) + { + LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots); + LOGWARNING("My item: %s", ItemToFullString(Slot).c_str()); + LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str()); + bAsync = true; + } + cItem & DraggingItem = a_Player.GetDraggingItem(); + switch (a_ClickAction) + { + case caRightClick: + { + if (DraggingItem.m_ItemType <= 0) // Empty-handed? + { + DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); + Slot.m_ItemCount -= DraggingItem.m_ItemCount; + DraggingItem.m_ItemType = Slot.m_ItemType; + DraggingItem.m_ItemDamage = Slot.m_ItemDamage; + + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + } + else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot)) + { + // Drop one item in slot + cItemHandler * Handler = ItemHandler(Slot.m_ItemType); + if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize())) + { + Slot.m_ItemType = DraggingItem.m_ItemType; + Slot.m_ItemCount++; + Slot.m_ItemDamage = DraggingItem.m_ItemDamage; + DraggingItem.m_ItemCount--; + } + if (DraggingItem.m_ItemCount <= 0) + { + DraggingItem.Empty(); + } + } + else if (!DraggingItem.IsEqual(Slot)) + { + // Swap contents + cItem tmp(DraggingItem); + DraggingItem = Slot; + Slot = tmp; + } + break; + } + + case caLeftClick: + { + // Left-clicked + if (!DraggingItem.IsEqual(Slot)) + { + // Switch contents + cItem tmp(DraggingItem); + DraggingItem = Slot; + Slot = tmp; + } + else + { + // Same type, add items: + cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType); + int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount; + if (FreeSlots < 0) + { + ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?"); + FreeSlots = 0; + } + int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots; + Slot.m_ItemCount += (char)Filling; + DraggingItem.m_ItemCount -= (char)Filling; + if (DraggingItem.m_ItemCount <= 0) + { + DraggingItem.Empty(); + } + } + break; + } + default: + { + LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction)); + m_ParentWindow.BroadcastWholeWindow(); + return; + } + } // switch (a_ClickAction + + SetSlot(a_SlotNum, a_Player, Slot); + if (bAsync) + { + m_ParentWindow.BroadcastWholeWindow(); + } + +} + + + + + +void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) +{ + // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover: + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + m_ParentWindow.DistributeStack(Slot, a_Player, this, true); + if (Slot.IsEmpty()) + { + // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count + Slot.Empty(); + } + SetSlot(a_SlotNum, a_Player, Slot); + + // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them: + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +void cSlotArea::DblClicked(cPlayer & a_Player, int a_SlotNum) +{ + cItem & Dragging = a_Player.GetDraggingItem(); + if (Dragging.IsEmpty()) + { + // Move the item in the dblclicked slot into hand: + Dragging = *GetSlot(a_SlotNum, a_Player); + cItem EmptyItem; + SetSlot(a_SlotNum, a_Player, EmptyItem); + } + if (Dragging.IsEmpty()) + { + LOGD("%s DblClicked with an empty hand over empty slot, ignoring", a_Player.GetName().c_str()); + return; + } + + // Add as many items from the surrounding area into hand as possible: + // First skip full stacks, then if there's still space, process full stacks as well: + if (!m_ParentWindow.CollectItemsToHand(Dragging, *this, a_Player, false)) + { + m_ParentWindow.CollectItemsToHand(Dragging, *this, a_Player, true); + } + + m_ParentWindow.BroadcastWholeWindow(); // We need to broadcast, in case the window was a chest opened by multiple players +} + + + + + +void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots) +{ + for (int i = 0; i < m_NumSlots; i++) + { + const cItem * Slot = GetSlot(i, a_Player); + if (!Slot->IsStackableWith(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + { + // Different items + continue; + } + int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + if (NumFit <= 0) + { + // Full stack already + continue; + } + if (NumFit > a_ItemStack.m_ItemCount) + { + NumFit = a_ItemStack.m_ItemCount; + } + if (a_Apply) + { + cItem NewSlot(a_ItemStack); + NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; + SetSlot(i, a_Player, NewSlot); + } + a_ItemStack.m_ItemCount -= NumFit; + if (a_ItemStack.IsEmpty()) + { + return; + } + } // for i - Slots +} + + + + + +bool cSlotArea::CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool a_CollectFullStacks) +{ + int NumSlots = GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + const cItem & SlotItem = *GetSlot(i, a_Player); + if (!SlotItem.IsStackableWith(a_Dragging)) + { + continue; + } + int ToMove = a_Dragging.GetMaxStackSize() - a_Dragging.m_ItemCount; + if (ToMove > SlotItem.m_ItemCount) + { + ToMove = SlotItem.m_ItemCount; + } + a_Dragging.m_ItemCount += ToMove; + cItem NewSlot(SlotItem); + NewSlot.m_ItemCount -= ToMove; + SetSlot(i, a_Player, NewSlot); + if (!NewSlot.IsEmpty()) + { + // There are leftovers in the slot, so a_Dragging must be full + return true; + } + } // for i - Slots[] + // a_Dragging may be full if there were exactly the number of items needed to fill it + return a_Dragging.IsFullStack(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaChest: + +cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) : + cSlotArea(27, a_ParentWindow), + m_Chest(a_Chest) +{ +} + + + + + +const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: + return &(m_Chest->GetSlot(a_SlotNum)); +} + + + + + +void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_Chest->SetSlot(a_SlotNum, a_Item); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaDoubleChest: + +cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) : + cSlotArea(54, a_ParentWindow), + m_TopChest(a_TopChest), + m_BottomChest(a_BottomChest) +{ +} + + + + + +const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory: + if (a_SlotNum < 27) + { + return &(m_TopChest->GetSlot(a_SlotNum)); + } + else + { + return &(m_BottomChest->GetSlot(a_SlotNum - 27)); + } +} + + + + + +void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + if (a_SlotNum < 27) + { + m_TopChest->SetSlot(a_SlotNum, a_Item); + } + else + { + m_BottomChest->SetSlot(a_SlotNum - 27, a_Item); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaCrafting: + +cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : + cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow), + m_GridSize(a_GridSize) +{ + ASSERT((a_GridSize == 2) || (a_GridSize == 3)); +} + + + + + +void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + // Override for craft result slot + if (a_SlotNum == 0) + { + if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) + { + ShiftClickedResult(a_Player); + } + else + { + ClickedResult(a_Player); + } + return; + } + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + UpdateRecipe(a_Player); +} + + + + + +void cSlotAreaCrafting::DblClicked(cPlayer & a_Player, int a_SlotNum) +{ + if (a_SlotNum == 0) + { + // Dbl-clicking the crafting result slot shouldn't collect items to hand + return; + } + super::DblClicked(a_Player, a_SlotNum); +} + + + + + +void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player) +{ + // Toss all items on the crafting grid: + TossItems(a_Player, 1, m_NumSlots); + + // Remove the current recipe from the player -> recipe map: + for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) + { + if (itr->first == a_Player.GetUniqueID()) + { + // Remove the player from the recipe map: + m_Recipes.erase(itr); + return; + } + } // for itr - m_Recipes[] + // Player not found - that is acceptable +} + + + + + +void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) +{ + const cItem * ResultSlot = GetSlot(0, a_Player); + cItem & DraggingItem = a_Player.GetDraggingItem(); + + // Get the current recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + + // If possible, craft: + if (DraggingItem.IsEmpty()) + { + DraggingItem = Recipe.GetResult(); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + } + else if (DraggingItem.IsEqual(Recipe.GetResult())) + { + cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType); + if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize()) + { + DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount; + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + } + } + + // Get the new recipe and update the result slot: + UpdateRecipe(a_Player); + + // We're done. Send all changes to the client and bail out: + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) +{ + cItem Result(*GetSlot(0, a_Player)); + if (Result.IsEmpty()) + { + return; + } + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + do + { + // Try distributing the result. If it fails, bail out: + cItem ResultCopy(Result); + m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false); + if (!ResultCopy.IsEmpty()) + { + // Couldn't distribute all of it. Bail out + return; + } + + // Distribute the result, this time for real: + ResultCopy = Result; + m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true); + + // Remove the ingredients from the crafting grid and update the recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + UpdateRecipe(a_Player); + if (!Recipe.GetResult().IsEqual(Result)) + { + // The recipe has changed, bail out + return; + } + } while (true); +} + + + + + +void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player) +{ + cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); + SetSlot(0, a_Player, Recipe.GetResult()); + m_ParentWindow.SendSlot(a_Player, this, 0); +} + + + + + +cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +{ + for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) + { + if (itr->first == a_Player.GetUniqueID()) + { + return itr->second; + } + } // for itr - m_Recipes[] + + // Not found. Add a new one: + cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); + cCraftingRecipe Recipe(Grid); + cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); + m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe)); + return m_Recipes.back().second; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaFurnace: + +cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) : + cSlotArea(3, a_ParentWindow), + m_Furnace(a_Furnace) +{ + m_Furnace->GetContents().AddListener(*this); +} + + + + + +cSlotAreaFurnace::~cSlotAreaFurnace() +{ + m_Furnace->GetContents().RemoveListener(*this); +} + + + + + +void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + + if (m_Furnace == NULL) + { + LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); + ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); + return; + } +} + + + + + +const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace: + return &(m_Furnace->GetSlot(a_SlotNum)); +} + + + + + +void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_Furnace->SetSlot(a_SlotNum, a_Item); +} + + + + + +void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + // Something has changed in the window, broadcast the entire window to all clients + ASSERT(a_ItemGrid == &(m_Furnace->GetContents())); + + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaInventoryBase: + +cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) : + cSlotArea(a_NumSlots, a_ParentWindow), + m_SlotOffset(a_SlotOffset) +{ +} + + + + + +void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + if (a_Player.IsGameModeCreative() && (m_ParentWindow.GetWindowType() == cWindow::wtInventory)) + { + // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it + SetSlot(a_SlotNum, a_Player, a_ClickedItem); + return; + } + + // Survival inventory and all other windows' inventory has the same handling as normal slot areas + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + return; +} + + + + + +const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset + return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset); +} + + + + + +void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaArmor: + +void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +{ + if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(0, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } + else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(1, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } + else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(2, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } + else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(3, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaItemGrid: + +cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) : + super(a_ItemGrid.GetNumSlots(), a_ParentWindow), + m_ItemGrid(a_ItemGrid) +{ + m_ItemGrid.AddListener(*this); +} + + + + + +cSlotAreaItemGrid::~cSlotAreaItemGrid() +{ + m_ItemGrid.RemoveListener(*this); +} + + + + + +const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + return &m_ItemGrid.GetSlot(a_SlotNum); +} + + + + + +void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_ItemGrid.SetSlot(a_SlotNum, a_Item); +} + + + + + +void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + ASSERT(a_ItemGrid == &m_ItemGrid); + m_ParentWindow.BroadcastSlot(this, a_SlotNum); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaTemporary: + +cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) : + cSlotArea(a_NumSlots, a_ParentWindow) +{ +} + + + + + +const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum); + ASSERT(!"cSlotAreaTemporary: player not found!"); + + // Player not found, this should not happen, ever! Return NULL, but things may break by this. + return NULL; + } + + if (a_SlotNum >= (int)(itr->second.size())) + { + LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); + ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!"); + return NULL; + } + + return &(itr->second[a_SlotNum]); +} + + + + + +void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + // Player not found + LOGWARNING("cSlotAreaTemporary: player not found!"); + return; + } + + if (a_SlotNum >= (int)(itr->second.size())) + { + LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); + return; + } + + itr->second[a_SlotNum] = a_Item; +} + + + + + +void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player) +{ + ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak + m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items +} + + + + + +void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched + m_Items.erase(itr); +} + + + + + +void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str()); + return; + } + + cItems Drops; + for (int i = a_Begin; i < a_End; i++) + { + cItem & Item = itr->second[i]; + if (!Item.IsEmpty()) + { + Drops.push_back(Item); + } + Item.Empty(); + } // for i - itr->second[] + + double vX = 0, vY = 0, vZ = 0; + EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because player created +} + + + + + +cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + return NULL; + } + return &(itr->second[0]); +} + + + + diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h new file mode 100644 index 000000000..b1944d901 --- /dev/null +++ b/src/UI/SlotArea.h @@ -0,0 +1,313 @@ + +// SlotArea.h + +// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window + + + + +#pragma once + +#include "../Inventory.h" + + + +class cWindow; +class cPlayer; +class cChestEntity; +class cDropSpenserEntity; +class cFurnaceEntity; +class cCraftingRecipe; + + + + + +class cSlotArea +{ +public: + cSlotArea(int a_NumSlots, cWindow & a_ParentWindow); + virtual ~cSlotArea() {} // force a virtual destructor in all subclasses + + int GetNumSlots(void) const { return m_NumSlots; } + + /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem. + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0; + + /// Called to set an item in the specified slot for the specified player + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0; + + /// Called when a player clicks in the window. Parameters taken from the click packet. + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem); + + /// Called from Clicked when the action is a shiftclick (left or right) + virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem); + + /// Called from Clicked when the action is a caDblClick + virtual void DblClicked(cPlayer & a_Player, int a_SlotNum); + + /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked. + virtual void OnPlayerAdded(cPlayer & a_Player) {} ; + + /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked. + virtual void OnPlayerRemoved(cPlayer & a_Player) {} ; + + /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change. + The default implementation searches each slot for available space and distributes the stack there. + if a_ShouldApply is true, the changes are written into the slots; + if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) + If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled + */ + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots); + + /// Called on DblClicking to collect all stackable items into hand. + /// The items are accumulated in a_Dragging and removed from the slots immediately. + /// If a_CollectFullStacks is false, slots with full stacks are skipped while collecting. + /// Returns true if full stack has been collected in a_Dragging, false if there's space remaining to fill. + virtual bool CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool a_CollectFullStacks); + +protected: + int m_NumSlots; + cWindow & m_ParentWindow; +} ; + + + + + +/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts +class cSlotAreaInventoryBase : + public cSlotArea +{ + typedef cSlotArea super; + +public: + cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow); + + // Creative inventory's click handling is somewhat different from survival inventory's, handle that here: + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory +} ; + + + + + +/// Handles the main inventory of each player, excluding the armor and hotbar +class cSlotAreaInventory : + public cSlotAreaInventoryBase +{ + typedef cSlotAreaInventoryBase super; + +public: + cSlotAreaInventory(cWindow & a_ParentWindow) : + cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow) + { + } +} ; + + + + + +/// Handles the hotbar of each player +class cSlotAreaHotBar : + public cSlotAreaInventoryBase +{ + typedef cSlotAreaInventoryBase super; + +public: + cSlotAreaHotBar(cWindow & a_ParentWindow) : + cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow) + { + } +} ; + + + + + +/// Handles the armor area of the player's inventory +class cSlotAreaArmor : + public cSlotAreaInventoryBase +{ +public: + cSlotAreaArmor(cWindow & a_ParentWindow) : + cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow) + { + } + + // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.) + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; +} ; + + + + + +/// Handles any slot area that is representing a cItemGrid; same items for all the players +class cSlotAreaItemGrid : + public cSlotArea, + public cItemGrid::cListener +{ + typedef cSlotArea super; + +public: + cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow); + + virtual ~cSlotAreaItemGrid(); + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cItemGrid & m_ItemGrid; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; + + + + + +/** A cSlotArea with items layout that is private to each player and is temporary, such as +a crafting grid or an enchantment table. +This common ancestor stores the items in a per-player map. It also implements tossing items from the map. +*/ +class cSlotAreaTemporary : + public cSlotArea +{ + typedef cSlotArea super; + +public: + cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow); + + // cSlotArea overrides: + virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + virtual void OnPlayerAdded (cPlayer & a_Player) override; + virtual void OnPlayerRemoved(cPlayer & a_Player) override; + + /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End) + void TossItems(cPlayer & a_Player, int a_Begin, int a_End); + +protected: + typedef std::map<int, std::vector<cItem> > cItemMap; // Maps EntityID -> items + + cItemMap m_Items; + + /// Returns the pointer to the slot array for the player specified. + cItem * GetPlayerSlots(cPlayer & a_Player); +} ; + + + + + +class cSlotAreaCrafting : + public cSlotAreaTemporary +{ + typedef cSlotAreaTemporary super; + +public: + /// a_GridSize is allowed to be only 2 or 3 + cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow); + + // cSlotAreaTemporary overrides: + virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual void DblClicked (cPlayer & a_Player, int a_SlotNum); + virtual void OnPlayerRemoved(cPlayer & a_Player) override; + + // Distributing items into this area is completely disabled + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {} + +protected: + /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params + typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap; + + int m_GridSize; + cRecipeMap m_Recipes; + + /// Handles a click in the result slot. Crafts using the current recipe, if possible + void ClickedResult(cPlayer & a_Player); + + /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result. + void ShiftClickedResult(cPlayer & a_Player); + + /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player + void UpdateRecipe(cPlayer & a_Player); + + /// Retrieves the recipe for the specified player from the map, or creates one if not found + cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player); +} ; + + + + + +class cSlotAreaChest : + public cSlotArea +{ +public: + cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow); + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cChestEntity * m_Chest; +} ; + + + + + +class cSlotAreaDoubleChest : + public cSlotArea +{ +public: + cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow); + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cChestEntity * m_TopChest; + cChestEntity * m_BottomChest; +} ; + + + + + +class cSlotAreaFurnace : + public cSlotArea, + public cItemGrid::cListener +{ + typedef cSlotArea super; + +public: + cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow); + + virtual ~cSlotAreaFurnace(); + + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cFurnaceEntity * m_Furnace; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; + + + + diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp new file mode 100644 index 000000000..f5c62692f --- /dev/null +++ b/src/UI/Window.cpp @@ -0,0 +1,886 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Window.h" +#include "WindowOwner.h" +#include "SlotArea.h" +#include "../Item.h" +#include "../ClientHandle.h" +#include "../Entities/Player.h" +#include "../Entities/Pickup.h" +#include "../Inventory.h" +#include "../Items/ItemHandler.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DropSpenserEntity.h" +#include "../BlockEntities/HopperEntity.h" + + + + + +char cWindow::m_WindowIDCounter = 1; + + + + + +cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) : + m_WindowID((++m_WindowIDCounter) % 127), + m_WindowType(a_WindowType), + m_WindowTitle(a_WindowTitle), + m_Owner(NULL), + m_IsDestroyed(false), + m_ShouldDistributeToHotbarFirst(true) +{ + if (a_WindowType == wtInventory) + { + m_WindowID = 0; + } +} + + + + + +cWindow::~cWindow() +{ + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + delete *itr; + } + m_SlotAreas.clear(); +} + + + + + +int cWindow::GetNumSlots(void) const +{ + int res = 0; + for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + res += (*itr)->GetNumSlots(); + } // for itr - m_SlotAreas[] + return res; +} + + + + + +const cItem * cWindow::GetSlot(cPlayer & a_Player, int a_SlotNum) const +{ + // Return the item at the specified slot for the specified player + int LocalSlotNum = 0; + const cSlotArea * Area = GetSlotArea(a_SlotNum, LocalSlotNum); + if (Area == NULL) + { + LOGWARNING("%s: requesting item from an invalid SlotArea (SlotNum %d), returning NULL.", __FUNCTION__, a_SlotNum); + return NULL; + } + return Area->GetSlot(LocalSlotNum, a_Player); +} + + + + + +void cWindow::SetSlot(cPlayer & a_Player, int a_SlotNum, const cItem & a_Item) +{ + // Set the item to the specified slot for the specified player + int LocalSlotNum = 0; + cSlotArea * Area = GetSlotArea(a_SlotNum, LocalSlotNum); + if (Area == NULL) + { + LOGWARNING("%s: requesting write to an invalid SlotArea (SlotNum %d), ignoring.", __FUNCTION__, a_SlotNum); + return; + } + Area->SetSlot(LocalSlotNum, a_Player, a_Item); +} + + + + + +bool cWindow::IsSlotInPlayerMainInventory(int a_SlotNum) const +{ + // Returns true if the specified slot is in the Player Main Inventory slotarea + // The player main inventory is always 27 slots, 9 slots from the end of the inventory + return ((a_SlotNum >= GetNumSlots() - 36) && (a_SlotNum < GetNumSlots() - 9)); +} + + + + + +bool cWindow::IsSlotInPlayerHotbar(int a_SlotNum) const +{ + // Returns true if the specified slot is in the Player Hotbar slotarea + // The hotbar is always the last 9 slots + return ((a_SlotNum >= GetNumSlots() - 9) && (a_SlotNum < GetNumSlots())); +} + + + + + +bool cWindow::IsSlotInPlayerInventory(int a_SlotNum) const +{ + // Returns true if the specified slot is in the Player Main Inventory or Hotbar slotareas. Note that returns false for Armor. + // The player combined inventory is always the last 36 slots + return ((a_SlotNum >= GetNumSlots() - 36) && (a_SlotNum < GetNumSlots())); +} + + + + + +void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const +{ + a_Slots.clear(); + a_Slots.reserve(GetNumSlots()); + for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + int NumSlots = (*itr)->GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + + const cItem * Item = (*itr)->GetSlot(i, a_Player); + if (Item == NULL) + { + a_Slots.push_back(cItem()); + } + else + { + a_Slots.push_back(*Item); + } + } + } // for itr - m_SlotAreas[] +} + + + + + +void cWindow::Clicked( + cPlayer & a_Player, + int a_WindowID, short a_SlotNum, eClickAction a_ClickAction, + const cItem & a_ClickedItem +) +{ + if (a_WindowID != m_WindowID) + { + LOGWARNING("%s: Wrong window ID (exp %d, got %d) received from \"%s\"; ignoring click.", __FUNCTION__, m_WindowID, a_WindowID, a_Player.GetName().c_str()); + return; + } + + switch (a_ClickAction) + { + case caRightClickOutside: + { + // Toss one of the dragged items: + a_Player.TossItem(true); + return; + } + case caLeftClickOutside: + { + // Toss all dragged items: + a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount); + return; + } + case caLeftClickOutsideHoldNothing: + case caRightClickOutsideHoldNothing: + { + // Nothing needed + return; + } + case caLeftPaintBegin: OnPaintBegin (a_Player); return; + case caRightPaintBegin: OnPaintBegin (a_Player); return; + case caLeftPaintProgress: OnPaintProgress(a_Player, a_SlotNum); return; + case caRightPaintProgress: OnPaintProgress(a_Player, a_SlotNum); return; + case caLeftPaintEnd: OnLeftPaintEnd (a_Player); return; + case caRightPaintEnd: OnRightPaintEnd(a_Player); return; + } + + if (a_SlotNum < 0) + { + // TODO: Other click actions with irrelevant slot number (FS #371) + return; + } + + int LocalSlotNum = a_SlotNum; + int idx = 0; + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (LocalSlotNum < (*itr)->GetNumSlots()) + { + (*itr)->Clicked(a_Player, LocalSlotNum, a_ClickAction, a_ClickedItem); + return; + } + LocalSlotNum -= (*itr)->GetNumSlots(); + idx++; + } + + LOGWARNING("Slot number higher than available window slots: %d, max %d received from \"%s\"; ignoring.", + a_SlotNum, GetNumSlots(), a_Player.GetName().c_str() + ); +} + + + + + +void cWindow::OpenedByPlayer(cPlayer & a_Player) +{ + { + cCSLock Lock(m_CS); + // If player is already in OpenedBy remove player first + m_OpenedBy.remove(&a_Player); + // Then add player + m_OpenedBy.push_back(&a_Player); + + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + (*itr)->OnPlayerAdded(a_Player); + } // for itr - m_SlotAreas[] + } + + a_Player.GetClientHandle()->SendWindowOpen(*this); +} + + + + + +bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + // Checks whether the player is still holding an item + if (a_Player.IsDraggingItem()) + { + LOGD("Player holds item! Dropping it..."); + a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount); + } + + cClientHandle * ClientHandle = a_Player.GetClientHandle(); + if (ClientHandle != NULL) + { + ClientHandle->SendWindowClose(*this); + } + + { + cCSLock Lock(m_CS); + + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + (*itr)->OnPlayerRemoved(a_Player); + } // for itr - m_SlotAreas[] + + m_OpenedBy.remove(&a_Player); + + if ((m_WindowType != wtInventory) && m_OpenedBy.empty()) + { + Destroy(); + } + } + if (m_IsDestroyed) + { + delete this; + } + + return true; +} + + + + + +void cWindow::OwnerDestroyed() +{ + m_Owner = NULL; + // Close window for each player. Note that the last one needs special handling + while (m_OpenedBy.size() > 1) + { + (*m_OpenedBy.begin() )->CloseWindow(); + } + (*m_OpenedBy.begin() )->CloseWindow(); +} + + + + + +bool cWindow::ForEachPlayer(cItemCallback<cPlayer> & a_Callback) +{ + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr) + { + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_OpenedBy[] + return true; +} + + + + + +bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback) +{ + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr) + { + if (a_Callback.Item((*itr)->GetClientHandle())) + { + return false; + } + } // for itr - m_OpenedBy[] + return true; +} + + + + + +void cWindow::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply) +{ + // Ask each slot area to take as much of the stack as it can. + // First ask only slots that already have the same kind of item + // Then ask any remaining slots + for (int Pass = 0; Pass < 2; ++Pass) + { + if (m_ShouldDistributeToHotbarFirst) + { + // First distribute into the hotbar: + if (a_ExcludeArea != m_SlotAreas.back()) + { + m_SlotAreas.back()->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0)); + if (a_ItemStack.IsEmpty()) + { + // Distributed it all + return; + } + } + } + + // The distribute to all other areas: + cSlotAreas::iterator end = m_ShouldDistributeToHotbarFirst ? (m_SlotAreas.end() - 1) : m_SlotAreas.end(); + for (cSlotAreas::iterator itr = m_SlotAreas.begin(); itr != end; ++itr) + { + if (*itr == a_ExcludeArea) + { + continue; + } + (*itr)->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0)); + if (a_ItemStack.IsEmpty()) + { + // Distributed it all + return; + } + } // for itr - m_SlotAreas[] + } // for Pass - repeat twice +} + + + + + +bool cWindow::CollectItemsToHand(cItem & a_Dragging, cSlotArea & a_Area, cPlayer & a_Player, bool a_CollectFullStacks) +{ + // First ask the slot areas from a_Area till the end of list: + bool ShouldCollect = false; + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (&a_Area == *itr) + { + ShouldCollect = true; + } + if (!ShouldCollect) + { + continue; + } + if ((*itr)->CollectItemsToHand(a_Dragging, a_Player, a_CollectFullStacks)) + { + // a_Dragging is full + return true; + } + } + + // a_Dragging still not full, ask slot areas before a_Area in the list: + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (*itr == &a_Area) + { + // All areas processed + return false; + } + if ((*itr)->CollectItemsToHand(a_Dragging, a_Player, a_CollectFullStacks)) + { + // a_Dragging is full + return true; + } + } + // Shouldn't reach here + // a_Area is expected to be part of m_SlotAreas[], so the "return false" in the loop above should have returned already + ASSERT(!"This branch should not be reached"); + return false; +} + + + + + +void cWindow::SendSlot(cPlayer & a_Player, cSlotArea * a_SlotArea, int a_RelativeSlotNum) +{ + int SlotBase = 0; + bool Found = false; + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (*itr == a_SlotArea) + { + Found = true; + break; + } + SlotBase += (*itr)->GetNumSlots(); + } // for itr - m_SlotAreas[] + if (!Found) + { + LOGERROR("cWindow::SendSlot(): unknown a_SlotArea"); + ASSERT(!"cWindow::SendSlot(): unknown a_SlotArea"); + return; + } + + a_Player.GetClientHandle()->SendInventorySlot( + m_WindowID, a_RelativeSlotNum + SlotBase, *(a_SlotArea->GetSlot(a_RelativeSlotNum, a_Player)) + ); +} + + + + + +void cWindow::Destroy(void) +{ + if (m_Owner != NULL) + { + m_Owner->CloseWindow(); + m_Owner = NULL; + } + m_IsDestroyed = true; +} + + + + + +cSlotArea * cWindow::GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum) +{ + if ((a_GlobalSlotNum < 0) || (a_GlobalSlotNum >= GetNumSlots())) + { + LOGWARNING("%s: requesting an invalid SlotNum: %d out of %d slots", __FUNCTION__, a_GlobalSlotNum, GetNumSlots() - 1); + ASSERT(!"Invalid SlotNum"); + return NULL; + } + + // Iterate through all the SlotAreas, find the correct one + int LocalSlotNum = a_GlobalSlotNum; + for (cSlotAreas::iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (LocalSlotNum < (*itr)->GetNumSlots()) + { + a_LocalSlotNum = LocalSlotNum; + return *itr; + } + LocalSlotNum -= (*itr)->GetNumSlots(); + } // for itr - m_SlotAreas[] + + // We shouldn't be here - the check at the beginnning should prevent this. Log and assert + LOGWARNING("%s: GetNumSlots() is out of sync: %d; LocalSlotNum = %d", __FUNCTION__, GetNumSlots(), LocalSlotNum); + ASSERT(!"Invalid GetNumSlots"); + return NULL; +} + + + + + +const cSlotArea * cWindow::GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum) const +{ + if ((a_GlobalSlotNum < 0) || (a_GlobalSlotNum >= GetNumSlots())) + { + LOGWARNING("%s: requesting an invalid SlotNum: %d out of %d slots", __FUNCTION__, a_GlobalSlotNum, GetNumSlots() - 1); + ASSERT(!"Invalid SlotNum"); + return NULL; + } + + // Iterate through all the SlotAreas, find the correct one + int LocalSlotNum = a_GlobalSlotNum; + for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (LocalSlotNum < (*itr)->GetNumSlots()) + { + a_LocalSlotNum = LocalSlotNum; + return *itr; + } + LocalSlotNum -= (*itr)->GetNumSlots(); + } // for itr - m_SlotAreas[] + + // We shouldn't be here - the check at the beginnning should prevent this. Log and assert + LOGWARNING("%s: GetNumSlots() is out of sync: %d; LocalSlotNum = %d", __FUNCTION__, GetNumSlots(), LocalSlotNum); + ASSERT(!"Invalid GetNumSlots"); + return NULL; +} + + + + + +void cWindow::OnPaintBegin(cPlayer & a_Player) +{ + // Prepares the internal structures for inventory painting from the specified player + a_Player.ClearInventoryPaintSlots(); +} + + + + + +void cWindow::OnPaintProgress(cPlayer & a_Player, int a_SlotNum) +{ + // Add the slot to the internal structures for inventory painting by the specified player + a_Player.AddInventoryPaintSlot(a_SlotNum); +} + + + + + +void cWindow::OnLeftPaintEnd(cPlayer & a_Player) +{ + // Process the entire action stored in the internal structures for inventory painting + // distribute as many items as possible + + const cSlotNums & SlotNums = a_Player.GetInventoryPaintSlots(); + cItem ToDistribute(a_Player.GetDraggingItem()); + int ToEachSlot = (int)ToDistribute.m_ItemCount / SlotNums.size(); + + int NumDistributed = DistributeItemToSlots(a_Player, ToDistribute, ToEachSlot, SlotNums); + + // Remove the items distributed from the dragging item: + a_Player.GetDraggingItem().m_ItemCount -= NumDistributed; + if (a_Player.GetDraggingItem().m_ItemCount == 0) + { + a_Player.GetDraggingItem().Empty(); + } + + SendWholeWindow(*a_Player.GetClientHandle()); +} + + + + + +void cWindow::OnRightPaintEnd(cPlayer & a_Player) +{ + // Process the entire action stored in the internal structures for inventory painting + // distribute one item into each slot + + const cSlotNums & SlotNums = a_Player.GetInventoryPaintSlots(); + cItem ToDistribute(a_Player.GetDraggingItem()); + + int NumDistributed = DistributeItemToSlots(a_Player, ToDistribute, 1, SlotNums); + + // Remove the items distributed from the dragging item: + a_Player.GetDraggingItem().m_ItemCount -= NumDistributed; + if (a_Player.GetDraggingItem().m_ItemCount == 0) + { + a_Player.GetDraggingItem().Empty(); + } + + SendWholeWindow(*a_Player.GetClientHandle()); +} + + + + + +int cWindow::DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int a_NumToEachSlot, const cSlotNums & a_SlotNums) +{ + if ((size_t)(a_Item.m_ItemCount) < a_SlotNums.size()) + { + LOGWARNING("%s: Distributing less items (%d) than slots (%u)", __FUNCTION__, (int)a_Item.m_ItemCount, a_SlotNums.size()); + // This doesn't seem to happen with the 1.5.1 client, so we don't worry about it for now + return 0; + } + + // Distribute to individual slots, keep track of how many items were actually distributed (full stacks etc.) + int NumDistributed = 0; + for (cSlotNums::const_iterator itr = a_SlotNums.begin(), end = a_SlotNums.end(); itr != end; ++itr) + { + int LocalSlotNum = 0; + cSlotArea * Area = GetSlotArea(*itr, LocalSlotNum); + if (Area == NULL) + { + LOGWARNING("%s: Bad SlotArea for slot %d", __FUNCTION__, *itr); + continue; + } + + // Modify the item at the slot + cItem AtSlot(*Area->GetSlot(LocalSlotNum, a_Player)); + int MaxStack = AtSlot.GetMaxStackSize(); + if (AtSlot.IsEmpty()) + { + // Empty, just move all of it there: + cItem ToStore(a_Item); + ToStore.m_ItemCount = std::min(a_NumToEachSlot, (int)MaxStack); + Area->SetSlot(LocalSlotNum, a_Player, ToStore); + NumDistributed += ToStore.m_ItemCount; + } + else if (AtSlot.IsStackableWith(a_Item)) + { + // Occupied, add and cap at MaxStack: + int CanStore = std::min(a_NumToEachSlot, (int)MaxStack - AtSlot.m_ItemCount); + AtSlot.m_ItemCount += CanStore; + Area->SetSlot(LocalSlotNum, a_Player, AtSlot); + NumDistributed += CanStore; + } + } // for itr - SlotNums[] + return NumDistributed; +} + + + + + +void cWindow::BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum) +{ + // Translate local slot num into global slot num: + int SlotNum = 0; + bool HasFound = false; + for (cSlotAreas::const_iterator itr = m_SlotAreas.begin(), end = m_SlotAreas.end(); itr != end; ++itr) + { + if (a_Area == *itr) + { + SlotNum += a_LocalSlotNum; + HasFound = true; + break; + } + SlotNum += (*itr)->GetNumSlots(); + } // for itr - m_SlotAreas[] + if (!HasFound) + { + LOGWARNING("%s: Invalid slot area parameter", __FUNCTION__); + ASSERT(!"Invalid slot area"); + return; + } + + // Broadcast the update packet: + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr) + { + (*itr)->GetClientHandle()->SendInventorySlot(m_WindowID, SlotNum, *a_Area->GetSlot(a_LocalSlotNum, **itr)); + } // for itr - m_OpenedBy[] +} + + + + + +void cWindow::SendWholeWindow(cClientHandle & a_Client) +{ + a_Client.SendWholeInventory(*this); +} + + + + + +void cWindow::BroadcastWholeWindow(void) +{ + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr) + { + SendWholeWindow(*(*itr)->GetClientHandle()); + } // for itr - m_OpenedBy[] +} + + + + + +void cWindow::BroadcastProgress(int a_Progressbar, int a_Value) +{ + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(); itr != m_OpenedBy.end(); ++itr) + { + (*itr)->GetClientHandle()->SendWindowProperty(*this, a_Progressbar, a_Value); + } // for itr - m_OpenedBy[] +} + + + + + +void cWindow::SetProperty(int a_Property, int a_Value) +{ + cCSLock Lock(m_CS); + for (cPlayerList::iterator itr = m_OpenedBy.begin(), end = m_OpenedBy.end(); itr != end; ++itr) + { + (*itr)->GetClientHandle()->SendWindowProperty(*this, a_Property, a_Value); + } // for itr - m_OpenedBy[] +} + + + + + +void cWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Player) +{ + a_Player.GetClientHandle()->SendWindowProperty(*this, a_Property, a_Value); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cInventoryWindow: + +cInventoryWindow::cInventoryWindow(cPlayer & a_Player) : + cWindow(wtInventory, "Inventory"), + m_Player(a_Player) +{ + m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers + m_SlotAreas.push_back(new cSlotAreaArmor(*this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCraftingWindow: + +cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : + cWindow(wtWorkbench, "Crafting Table") +{ + m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChestWindow: + +cChestWindow::cChestWindow(cChestEntity * a_Chest) : + cWindow(wtChest, "Chest"), + m_World(a_Chest->GetWorld()), + m_BlockX(a_Chest->GetPosX()), + m_BlockY(a_Chest->GetPosY()), + m_BlockZ(a_Chest->GetPosZ()) +{ + m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + // Play the opening sound: + m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + + // Send out the chest-open packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST); +} + + + + + +cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) : + cWindow(wtChest, "Double Chest"), + m_World(a_PrimaryChest->GetWorld()), + m_BlockX(a_PrimaryChest->GetPosX()), + m_BlockY(a_PrimaryChest->GetPosY()), + m_BlockZ(a_PrimaryChest->GetPosZ()) +{ + m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + m_ShouldDistributeToHotbarFirst = false; + + // Play the opening sound: + m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + + // Send out the chest-open packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST); +} + + + + + +cChestWindow::~cChestWindow() +{ + // Send out the chest-close packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_CHEST); + + m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cDropSpenserWindow: + +cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) : + cWindow(wtDropSpenser, "Dropspenser") +{ + m_ShouldDistributeToHotbarFirst = false; + m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cHopperWindow: + +cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) : + super(wtHopper, "Hopper") +{ + m_ShouldDistributeToHotbarFirst = false; + m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFurnaceWindow: + +cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) : + cWindow(wtFurnace, "Furnace") +{ + m_ShouldDistributeToHotbarFirst = false; + m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + diff --git a/src/UI/Window.h b/src/UI/Window.h new file mode 100644 index 000000000..c44b900d7 --- /dev/null +++ b/src/UI/Window.h @@ -0,0 +1,300 @@ + +// Window.h + +// Interfaces to the cWindow class representing a UI window for a specific block + + + + + +#pragma once + +#include "../ItemGrid.h" + + + + + +class cPlayer; +class cWindowOwner; +class cClientHandle; +class cChestEntity; +class cDropSpenserEntity; +class cFurnaceEntity; +class cHopperEntity; +class cSlotArea; +class cWorld; + +typedef std::list<cPlayer *> cPlayerList; +typedef std::vector<cSlotArea *> cSlotAreas; + + + + + +// tolua_begin + +/** +Represents a UI window. + +Each window has a list of players that are currently using it +When there's no player using a window, it is destroyed. +A window consists of several areas of slots with similar functionality - for example the crafting grid area, or +the inventory area. Each area knows what its slots are (GetSlot() function) and can handle mouse clicks. +The window acts only as a top-level container for those areas, redirecting the click events to the correct areas. +Inventory painting, introduced in 1.5, is handled by the window, too +*/ +class cWindow +{ +public: + enum WindowType + { + wtInventory = -1, // This value is never actually sent to a client + wtChest = 0, + wtWorkbench = 1, + wtFurnace = 2, + wtDropSpenser = 3, // Dropper or Dispenser + wtEnchantment = 4, + wtBrewery = 5, + wtNPCTrade = 6, + wtBeacon = 7, + wtAnvil = 8, + wtHopper = 9, + // Unknown: 10 + wtAnimalChest = 11, + }; + + // tolua_end + + static const int c_NumInventorySlots = 36; + + cWindow(WindowType a_WindowType, const AString & a_WindowTitle); + virtual ~cWindow(); + + char GetWindowID(void) const { return m_WindowID; } // tolua_export + int GetWindowType(void) const { return m_WindowType; } // tolua_export + + cWindowOwner * GetOwner(void) { return m_Owner; } + void SetOwner( cWindowOwner * a_Owner ) { m_Owner = a_Owner; } + + /// Returns the total number of slots + int GetNumSlots(void) const; + + /// Returns the number of slots, excluding the player's inventory (used for network protocols) + int GetNumNonInventorySlots(void) const { return GetNumSlots() - c_NumInventorySlots; } + + // tolua_begin + + /// Returns the item at the specified slot for the specified player. Returns NULL if invalid SlotNum requested + const cItem * GetSlot(cPlayer & a_Player, int a_SlotNum) const; + + /// Sets the item to the specified slot for the specified player + void SetSlot(cPlayer & a_Player, int a_SlotNum, const cItem & a_Item); + + /// Returns true if the specified slot is in the Player Main Inventory slotarea + bool IsSlotInPlayerMainInventory(int a_SlotNum) const; + + /// Returns true if the specified slot is in the Player Hotbar slotarea + bool IsSlotInPlayerHotbar(int a_SlotNum) const; + + /// Returns true if the specified slot is in the Player Main Inventory or Hotbar slotareas. Note that returns false for Armor. + bool IsSlotInPlayerInventory(int a_SlotNum) const; + + // tolua_end + + /// Fills a_Slots with the slots read from m_SlotAreas[], for the specified player + void GetSlots(cPlayer & a_Player, cItems & a_Slots) const; + + /// Handles a click event from a player + void Clicked( + cPlayer & a_Player, int a_WindowID, + short a_SlotNum, eClickAction a_ClickAction, + const cItem & a_ClickedItem + ); + + void OpenedByPlayer(cPlayer & a_Player); + + /// Called when a player closes this window; notifies all slot areas. Returns true if close accepted + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse); + + /// Sends the specified slot's contents to all clients of this window; the slot is specified as local in an area + void BroadcastSlot(cSlotArea * a_Area, int a_LocalSlotNum); + + /// Sends the contents of the whole window to the specified client + void SendWholeWindow(cClientHandle & a_Client); + + /// Sends the contents of the whole window to all clients of this window. + void BroadcastWholeWindow(void); + + /// Sends the progressbar to all clients of this window (same as SetProperty) + void BroadcastProgress(int a_Progressbar, int a_Value); + + // tolua_begin + + const AString & GetWindowTitle() const { return m_WindowTitle; } + void SetWindowTitle(const AString & a_WindowTitle ) { m_WindowTitle = a_WindowTitle; } + + /// Sends the UpdateWindowProperty (0x69) packet to all clients of the window + void SetProperty(int a_Property, int a_Value); + + /// Sends the UpdateWindowPropert(0x69) packet to the specified player + void SetProperty(int a_Property, int a_Value, cPlayer & a_Player); + + // tolua_end + + void OwnerDestroyed(void); + + /// Calls the callback safely for each player that has this window open; returns true if all players have been enumerated + bool ForEachPlayer(cItemCallback<cPlayer> & a_Callback); + + /// Calls the callback safely for each client that has this window open; returns true if all clients have been enumerated + bool ForEachClient(cItemCallback<cClientHandle> & a_Callback); + + /** Called on shift-clicking to distribute the stack into other areas; Modifies a_ItemStack as it is distributed! + if a_ShouldApply is true, the changes are written into the slots; + if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) + */ + void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply); + + /// Called on DblClicking to collect all stackable items from all areas into hand, starting with the specified area. + /// The items are accumulated in a_Dragging and removed from the SlotAreas immediately. + /// If a_CollectFullStacks is false, slots with full stacks in the area are skipped while collecting. + /// Returns true if full stack has been collected, false if there's space remaining to fill. + bool CollectItemsToHand(cItem & a_Dragging, cSlotArea & a_Area, cPlayer & a_Player, bool a_CollectFullStacks); + + /// Used by cSlotAreas to send individual slots to clients, a_RelativeSlotNum is the slot number relative to a_SlotArea + void SendSlot(cPlayer & a_Player, cSlotArea * a_SlotArea, int a_RelativeSlotNum); + +protected: + cSlotAreas m_SlotAreas; + + char m_WindowID; + int m_WindowType; + AString m_WindowTitle; + + cCriticalSection m_CS; + cPlayerList m_OpenedBy; + + bool m_IsDestroyed; + bool m_ShouldDistributeToHotbarFirst; ///< If set (default), shift+click tries to distribute to hotbar first, then other areas. False for doublechests + + cWindowOwner * m_Owner; + + static char m_WindowIDCounter; + + /// Sets the internal flag as "destroyed"; notifies the owner that the window is destroying + virtual void Destroy(void); + + /** Returns the correct slot area for the specified window-global SlotNum + Also returns the area-local SlotNum corresponding to the GlobalSlotNum + If the global SlotNum is out of range, returns NULL + */ + cSlotArea * GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum); + + /** Returns the correct slot area for the specified window-global SlotNum + Also returns the area-local SlotNum corresponding to the GlobalSlotNum + If the global SlotNum is out of range, returns NULL. + Const version. + */ + const cSlotArea * GetSlotArea(int a_GlobalSlotNum, int & a_LocalSlotNum) const; + + /// Prepares the internal structures for inventory painting from the specified player + void OnPaintBegin(cPlayer & a_Player); + + /// Adds the slot to the internal structures for inventory painting by the specified player + void OnPaintProgress(cPlayer & a_Player, int a_SlotNum); + + /// Processes the entire action stored in the internal structures for inventory painting; distributes as many items as possible + void OnLeftPaintEnd(cPlayer & a_Player); + + /// Processes the entire action stored in the internal structures for inventory painting; distributes one item into each slot + void OnRightPaintEnd(cPlayer & a_Player); + + /// Distributes a_NumToEachSlot items into the slots specified in a_SlotNums; returns the total number of items distributed + int DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int a_NumToEachSlot, const cSlotNums & a_SlotNums); +} ; // tolua_export + + + + + +class cCraftingWindow : + public cWindow +{ + typedef cWindow super; +public: + cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ); +} ; + + + + + +class cFurnaceWindow : + public cWindow +{ + typedef cWindow super; +public: + cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace); +} ; + + + + + +class cDropSpenserWindow : + public cWindow +{ + typedef cWindow super; +public: + cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_Dispenser); +} ; + + + + + +class cHopperWindow : + public cWindow +{ + typedef cWindow super; +public: + cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper); +} ; + + + + + +class cChestWindow : + public cWindow +{ +public: + cChestWindow(cChestEntity * a_Chest); + cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest); + ~cChestWindow(); + +protected: + cWorld * m_World; + int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet +} ; + + + + + +class cInventoryWindow : + public cWindow +{ +public: + cInventoryWindow(cPlayer & a_Player); + +protected: + cPlayer & m_Player; + +} ; + + + + + diff --git a/src/UI/WindowOwner.h b/src/UI/WindowOwner.h new file mode 100644 index 000000000..d41abf66d --- /dev/null +++ b/src/UI/WindowOwner.h @@ -0,0 +1,125 @@ + +#pragma once + +#include "../BlockEntities/BlockEntity.h" +#include "../Entities/Entity.h" +#include "Window.h" + +/* +Being a descendant of cWindowOwner means that the class can own one window. That window can be +queried, opened by other players, closed by players and finally destroyed. +Also, a cWindowOwner can be queried for the block coords where the window is displayed. That will be used +for entities / players in motion to close their windows when they get too far away from the window "source". +*/ + + + + + +// class cWindow; + + + + + +/** +Base class for the window owning +*/ +class cWindowOwner +{ +public: + cWindowOwner() : + m_Window(NULL) + { + } + + void CloseWindow(void) + { + m_Window = NULL; + } + + void OpenWindow(cWindow * a_Window) + { + m_Window = a_Window; + m_Window->SetOwner(this); + } + + cWindow * GetWindow(void) const + { + return m_Window; + } + + /// Returns the block position at which the element owning the window is + virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) = 0; + +private: + cWindow * m_Window; +} ; + + + + + +/** +Window owner that is associated with a block entity (chest, furnace, ...) +*/ +class cBlockEntityWindowOwner : + public cWindowOwner +{ +public: + cBlockEntityWindowOwner(void) : + m_BlockEntity(NULL) + { + } + + void SetBlockEntity(cBlockEntity * a_BlockEntity) + { + m_BlockEntity = a_BlockEntity; + } + + virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) override + { + a_BlockX = m_BlockEntity->GetPosX(); + a_BlockY = m_BlockEntity->GetPosY(); + a_BlockZ = m_BlockEntity->GetPosZ(); + } + +private: + cBlockEntity * m_BlockEntity; +} ; + + + + + +/** +Window owner that is associated with an entity (chest minecart) +*/ +class cEntityWindowOwner : + public cWindowOwner +{ +public: + cEntityWindowOwner(void) : + m_Entity(NULL) + { + } + + void SetEntity(cEntity * a_Entity) + { + m_Entity = a_Entity; + } + + virtual void GetBlockPos(int & a_BlockX, int & a_BlockY, int & a_BlockZ) override + { + a_BlockX = (int)floor(m_Entity->GetPosX() + 0.5); + a_BlockY = (int)floor(m_Entity->GetPosY() + 0.5); + a_BlockZ = (int)floor(m_Entity->GetPosZ() + 0.5); + } + +private: + cEntity * m_Entity; +} ; + + + + diff --git a/src/Vector3d.cpp b/src/Vector3d.cpp new file mode 100644 index 000000000..96ebebab5 --- /dev/null +++ b/src/Vector3d.cpp @@ -0,0 +1,77 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Vector3d.h" +#include "Vector3f.h" + + + + + +const double Vector3d::EPS = 0.000001; ///< The max difference between two coords for which the coords are assumed equal +const double Vector3d::NO_INTERSECTION = 1e70; ///< Return value of LineCoeffToPlane() if the line is parallel to the plane + + + + + +Vector3d::Vector3d(const Vector3f & v) : + x(v.x), + y(v.y), + z(v.z) +{ +} + + + + + +Vector3d::Vector3d(const Vector3f * v) : + x(v->x), + y(v->y), + z(v->z) +{ +} + + + + + +double Vector3d::LineCoeffToXYPlane(const Vector3d & a_OtherEnd, double a_Z) const +{ + if (abs(z - a_OtherEnd.z) < EPS) + { + return NO_INTERSECTION; + } + return (a_Z - z) / (a_OtherEnd.z - z); +} + + + + + +double Vector3d::LineCoeffToXZPlane(const Vector3d & a_OtherEnd, double a_Y) const +{ + if (abs(y - a_OtherEnd.y) < EPS) + { + return NO_INTERSECTION; + } + return (a_Y - y) / (a_OtherEnd.y - y); +} + + + + + +double Vector3d::LineCoeffToYZPlane(const Vector3d & a_OtherEnd, double a_X) const +{ + if (abs(x - a_OtherEnd.x) < EPS) + { + return NO_INTERSECTION; + } + return (a_X - x) / (a_OtherEnd.x - x); +} + + + + diff --git a/src/Vector3d.h b/src/Vector3d.h new file mode 100644 index 000000000..a06a17c09 --- /dev/null +++ b/src/Vector3d.h @@ -0,0 +1,81 @@ +#pragma once + +#include <math.h> + +class Vector3f; + + + +// tolua_begin + +class Vector3d +{ +public: + // convert from float + Vector3d(const Vector3f & v); + Vector3d(const Vector3f * v); + + Vector3d() : x(0), y(0), z(0) {} + Vector3d(double a_x, double a_y, double a_z) : x(a_x), y(a_y), z(a_z) {} + + inline void Set(double a_x, double a_y, double a_z) { x = a_x, y = a_y, z = a_z; } + inline void Normalize() { double l = 1.0f / Length(); x *= l; y *= l; z *= l; } + inline Vector3d NormalizeCopy() { double l = 1.0f / Length(); return Vector3d( x * l, y * l, z * l ); } + inline void NormalizeCopy(Vector3d & a_V) { double l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); } + inline double Length() const { return (double)sqrt( x * x + y * y + z * z ); } + inline double SqrLength() const { return x * x + y * y + z * z; } + inline double Dot( const Vector3d & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } + inline Vector3d Cross( const Vector3d & v ) const { return Vector3d( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); } + + /** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified Z coord + The result satisfies the following equation: + (*this + Result * (a_OtherEnd - *this)).z = a_Z + If the line is too close to being parallel, this function returns NO_INTERSECTION + */ + double LineCoeffToXYPlane(const Vector3d & a_OtherEnd, double a_Z) const; + + /** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified Y coord + The result satisfies the following equation: + (*this + Result * (a_OtherEnd - *this)).y = a_Y + If the line is too close to being parallel, this function returns NO_INTERSECTION + */ + double LineCoeffToXZPlane(const Vector3d & a_OtherEnd, double a_Y) const; + + /** Returns the coefficient for the (a_OtherEnd - this) line to reach the specified X coord + The result satisfies the following equation: + (*this + Result * (a_OtherEnd - *this)).x = a_X + If the line is too close to being parallel, this function returns NO_INTERSECTION + */ + double LineCoeffToYZPlane(const Vector3d & a_OtherEnd, double a_X) const; + + inline bool Equals(const Vector3d & v) const { return ((x == v.x) && (y == v.y) && (z == v.z)); } + + // tolua_end + + void operator += ( const Vector3d& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; } + void operator += ( Vector3d* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; } + void operator -= ( const Vector3d& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; } + void operator -= ( Vector3d* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; } + void operator *= ( double a_f ) { x *= a_f; y *= a_f; z *= a_f; } + + // tolua_begin + + Vector3d operator + (const Vector3d & v2) const { return Vector3d(x + v2.x, y + v2.y, z + v2.z ); } + Vector3d operator + (const Vector3d * v2) const { return Vector3d(x + v2->x, y + v2->y, z + v2->z ); } + Vector3d operator - (const Vector3d & v2) const { return Vector3d(x - v2.x, y - v2.y, z - v2.z ); } + Vector3d operator - (const Vector3d * v2) const { return Vector3d(x - v2->x, y - v2->y, z - v2->z ); } + Vector3d operator * (const double f) const { return Vector3d(x * f, y * f, z * f ); } + Vector3d operator * (const Vector3d & v2) const { return Vector3d(x * v2.x, y * v2.y, z * v2.z ); } + Vector3d operator / (const double f) const { return Vector3d(x / f, y / f, z / f ); } + + double x, y, z; + + static const double EPS; ///< The max difference between two coords for which the coords are assumed equal + static const double NO_INTERSECTION; ///< Return value of LineCoeffToPlane() if the line is parallel to the plane +} ; + +// tolua_end + + + + diff --git a/src/Vector3f.cpp b/src/Vector3f.cpp new file mode 100644 index 000000000..59d71d371 --- /dev/null +++ b/src/Vector3f.cpp @@ -0,0 +1,34 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Vector3f.h" +#include "Vector3d.h" +#include "Vector3i.h" + +Vector3f::Vector3f( const Vector3d & v ) + : x( (float)v.x ) + , y( (float)v.y ) + , z( (float)v.z ) +{ +} + +Vector3f::Vector3f( const Vector3d * v ) + : x( (float)v->x ) + , y( (float)v->y ) + , z( (float)v->z ) +{ +} + +Vector3f::Vector3f( const Vector3i & v ) + : x( (float)v.x ) + , y( (float)v.y ) + , z( (float)v.z ) +{ +} + +Vector3f::Vector3f( const Vector3i * v ) + : x( (float)v->x ) + , y( (float)v->y ) + , z( (float)v->z ) +{ +}
\ No newline at end of file diff --git a/src/Vector3f.h b/src/Vector3f.h new file mode 100644 index 000000000..adb154ad7 --- /dev/null +++ b/src/Vector3f.h @@ -0,0 +1,47 @@ +#pragma once + +#include <math.h> + +class Vector3i; +class Vector3d; +class Vector3f // tolua_export +{ // tolua_export +public: // tolua_export + Vector3f( const Vector3d & v ); // tolua_export + Vector3f( const Vector3d * v ); // tolua_export + Vector3f( const Vector3i & v ); // tolua_export + Vector3f( const Vector3i * v ); // tolua_export + + + Vector3f() : x(0), y(0), z(0) {} // tolua_export + Vector3f(float a_x, float a_y, float a_z) : x(a_x), y(a_y), z(a_z) {} // tolua_export + + inline void Set(float a_x, float a_y, float a_z) { x = a_x, y = a_y, z = a_z; } // tolua_export + inline void Normalize() { float l = 1.0f / Length(); x *= l; y *= l; z *= l; } // tolua_export + inline Vector3f NormalizeCopy() const { float l = 1.0f / Length(); return Vector3f( x * l, y * l, z * l ); }// tolua_export + inline void NormalizeCopy(Vector3f & a_V) const { float l = 1.0f / Length(); a_V.Set(x*l, y*l, z*l ); } // tolua_export + inline float Length() const { return (float)sqrtf( x * x + y * y + z * z ); } // tolua_export + inline float SqrLength() const { return x * x + y * y + z * z; } // tolua_export + inline float Dot( const Vector3f & a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } // tolua_export + inline Vector3f Cross( const Vector3f & v ) const { return Vector3f( y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x ); } // tolua_export + + inline bool Equals( const Vector3f & v ) const { return (x == v.x && y == v.y && z == v.z ); } // tolua_export + + void operator += ( const Vector3f& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; } + void operator += ( Vector3f* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; } + void operator -= ( const Vector3f& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; } + void operator -= ( Vector3f* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; } + void operator *= ( float a_f ) { x *= a_f; y *= a_f; z *= a_f; } + void operator *= ( Vector3f* a_V ) { x *= a_V->x; y *= a_V->y; z *= a_V->z; } + void operator *= ( const Vector3f& a_V ) { x *= a_V.x; y *= a_V.y; z *= a_V.z; } + + Vector3f operator + ( const Vector3f& v2 ) const { return Vector3f( x + v2.x, y + v2.y, z + v2.z ); } // tolua_export + Vector3f operator + ( const Vector3f* v2 ) const { return Vector3f( x + v2->x, y + v2->y, z + v2->z ); } // tolua_export + Vector3f operator - ( const Vector3f& v2 ) const { return Vector3f( x - v2.x, y - v2.y, z - v2.z ); } // tolua_export + Vector3f operator - ( const Vector3f* v2 ) const { return Vector3f( x - v2->x, y - v2->y, z - v2->z ); } // tolua_export + Vector3f operator * ( const float f ) const { return Vector3f( x * f, y * f, z * f ); } // tolua_export + Vector3f operator * ( const Vector3f& v2 ) const { return Vector3f( x * v2.x, y * v2.y, z * v2.z ); } // tolua_export + + float x, y, z; // tolua_export + +};// tolua_export diff --git a/src/Vector3i.cpp b/src/Vector3i.cpp new file mode 100644 index 000000000..4ce1e2cf3 --- /dev/null +++ b/src/Vector3i.cpp @@ -0,0 +1,16 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Vector3i.h" +#include "Vector3d.h" + + + + + +Vector3i::Vector3i( const Vector3d & v ) + : x( (int)v.x ) + , y( (int)v.y ) + , z( (int)v.z ) +{ +}
\ No newline at end of file diff --git a/src/Vector3i.h b/src/Vector3i.h new file mode 100644 index 000000000..7d726a7b3 --- /dev/null +++ b/src/Vector3i.h @@ -0,0 +1,45 @@ +#pragma once + +#include <math.h> + +class Vector3d; +class Vector3i // tolua_export +{ // tolua_export +public: // tolua_export + Vector3i( const Vector3d & v ); // tolua_export + + Vector3i() : x(0), y(0), z(0) {} // tolua_export + Vector3i(int a_x, int a_y, int a_z) : x(a_x), y(a_y), z(a_z) {} // tolua_export + + inline void Set(int a_x, int a_y, int a_z) { x = a_x, y = a_y, z = a_z; } // tolua_export + inline float Length() const { return sqrtf( (float)( x * x + y * y + z * z) ); } // tolua_export + inline int SqrLength() const { return x * x + y * y + z * z; } // tolua_export + + inline bool Equals( const Vector3i & v ) const { return (x == v.x && y == v.y && z == v.z ); } // tolua_export + inline bool Equals( const Vector3i * v ) const { return (x == v->x && y == v->y && z == v->z ); } // tolua_export + + void operator += ( const Vector3i& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; } + void operator += ( Vector3i* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; } + void operator -= ( const Vector3i& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; } + void operator -= ( Vector3i* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; } + void operator *= ( int a_f ) { x *= a_f; y *= a_f; z *= a_f; } + + friend Vector3i operator + ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z ); } + friend Vector3i operator + ( const Vector3i& v1, Vector3i* v2 ) { return Vector3i( v1.x + v2->x, v1.y + v2->y, v1.z + v2->z ); } + friend Vector3i operator - ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z ); } + friend Vector3i operator - ( const Vector3i& v1, Vector3i* v2 ) { return Vector3i( v1.x - v2->x, v1.y - v2->y, v1.z - v2->z ); } + friend Vector3i operator - ( const Vector3i* v1, Vector3i& v2 ) { return Vector3i( v1->x - v2.x, v1->y - v2.y, v1->z - v2.z ); } + friend Vector3i operator * ( const Vector3i& v, const int f ) { return Vector3i( v.x * f, v.y * f, v.z * f ); } + friend Vector3i operator * ( const Vector3i& v1, const Vector3i& v2 ) { return Vector3i( v1.x * v2.x, v1.y * v2.y, v1.z * v2.z ); } + friend Vector3i operator * ( const int f, const Vector3i& v ) { return Vector3i( v.x * f, v.y * f, v.z * f ); } + friend bool operator < ( const Vector3i& v1, const Vector3i& v2 ) { return (v1.x<v2.x)||(v1.x==v2.x && v1.y<v2.y)||(v1.x==v2.x && v1.y == v2.y && v1.z<v2.z); } + + int x, y, z; // tolua_export +}; // tolua_export + +typedef std::list<Vector3i> cVector3iList; +typedef std::vector<Vector3i> cVector3iArray; + + + + diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp new file mode 100644 index 000000000..ecc131d21 --- /dev/null +++ b/src/WebAdmin.cpp @@ -0,0 +1,527 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WebAdmin.h" +#include "WebPlugin.h" + +#include "PluginManager.h" +#include "Plugin.h" + +#include "World.h" +#include "Entities/Player.h" +#include "Server.h" +#include "Root.h" + +#include "HTTPServer/HTTPMessage.h" +#include "HTTPServer/HTTPConnection.h" + + + + + +/// Helper class - appends all player names together in a HTML list +class cPlayerAccum : + public cPlayerListCallback +{ + virtual bool Item(cPlayer * a_Player) override + { + m_Contents.append("<li>"); + m_Contents.append(a_Player->GetName()); + m_Contents.append("</li>"); + return false; + } + +public: + + AString m_Contents; +} ; + + + + + +cWebAdmin::cWebAdmin(void) : + m_IsInitialized(false), + m_TemplateScript("<webadmin_template>") +{ +} + + + + + +cWebAdmin::~cWebAdmin() +{ + if (m_IsInitialized) + { + LOGD("Stopping WebAdmin..."); + } +} + + + + + +void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) +{ + m_Plugins.remove( a_Plugin ); + m_Plugins.push_back( a_Plugin ); +} + + + + + +void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) +{ + m_Plugins.remove( a_Plugin ); +} + + + + + +bool cWebAdmin::Init(void) +{ + if (!m_IniFile.ReadFile("webadmin.ini")) + { + LOGWARN("Regenerating webadmin.ini, all settings will be reset"); + m_IniFile.AddHeaderComment(" This file controls the webadmin feature of MCServer"); + m_IniFile.AddHeaderComment(" Username format: [User:*username*] | Password format: Password=*password*; for example:"); + m_IniFile.AddHeaderComment(" [User:admin]"); + m_IniFile.AddHeaderComment(" Password=admin"); + } + + if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) + { + // WebAdmin is disabled, bail out faking a success + return true; + } + + LOGD("Initialising WebAdmin..."); + + AString PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080"); + AString PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", ""); + + if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6)) + { + return false; + } + m_IsInitialized = true; + m_IniFile.WriteFile("webadmin.ini"); + return true; +} + + + + + +bool cWebAdmin::Start(void) +{ + if (!m_IsInitialized) + { + // Not initialized + return false; + } + + LOGD("Starting WebAdmin..."); + + // 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); +} + + + + + +AString cWebAdmin::GetTemplate() +{ + 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::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + if (!a_Request.HasAuth()) + { + a_Connection.SendNeedAuth("MCServer WebAdmin"); + return; + } + + // Check auth: + AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); + if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) + { + a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password"); + return; + } + + // 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) + { + a_Connection.SendStatusAndReason(500, "Bad UserData"); + return; + } + + // 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()) + { + 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; + } // for itr - Data->m_Form[] + + // Parse the URL into individual params: + size_t idxQM = a_Request.GetURL().find('?'); + if (idxQM != AString::npos) + { + cHTTPFormParser URLParams(cHTTPFormParser::fpkURL, a_Request.GetURL().c_str() + idxQM + 1, a_Request.GetURL().length() - idxQM - 1, *Data); + URLParams.Finish(); + for (cHTTPFormParser::const_iterator itr = URLParams.begin(), end = URLParams.end(); itr != end; ++itr) + { + TemplateRequest.Request.Params[itr->first] = itr->second; + } // for itr - URLParams[] + } + } + + // Try to get the template from the Lua template script + if (ShouldWrapInTemplate) + { + if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template)) + { + 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; + + 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) + { + Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; + } + } + + sWebAdminPage Page = GetPage(TemplateRequest.Request); + AString Content = Page.Content; + FoundPlugin = Page.PluginName; + if (!Page.TabName.empty()) + { + FoundPlugin += " - " + Page.TabName; + } + + if (FoundPlugin.empty()) // Default page + { + Content = GetDefaultPage(); + } + + if (ShouldWrapInTemplate && (URL.size() > 1)) + { + Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; + } + + int MemUsageKiB = cRoot::GetPhysicalRAMUsage(); + if (MemUsageKiB > 0) + { + ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024)); + ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB)); + } + 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()); +} + + + + + +void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) +{ + 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 Page; + AStringVector Split = StringSplit(a_Request.Path, "/"); + + // Find the plugin that corresponds to the requested path + AString FoundPlugin; + if (Split.size() > 1) + { + for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + { + if ((*itr)->GetWebTitle() == Split[1]) + { + Page.Content = (*itr)->HandleWebRequest(&a_Request); + cWebPlugin * WebPlugin = *itr; + FoundPlugin = WebPlugin->GetWebTitle(); + AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first; + Page.PluginName = FoundPlugin; + Page.TabName = TabName; + break; + } + } + } + + // Return the page contents + return Page; +} + + + + + +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, "/")); +} + + + + + +AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input) +{ + AString dst; + dst.reserve(a_Input.length()); + + // Loop over input and substitute HTML characters for their alternatives: + size_t len = a_Input.length(); + for (size_t i = 0; i < len; i++) + { + switch (a_Input[i]) + { + case '&': dst.append("&"); break; + case '\'': dst.append("'"); break; + case '"': dst.append("""); break; + case '<': dst.append("<"); break; + case '>': dst.append(">"); break; + default: + { + dst.push_back(a_Input[i]); + break; + } + } // switch (a_Input[i]) + } // for i - a_Input[] + + return dst; +} + + + + + +AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit) +{ + AString BaseURL = "./"; + if (a_URLSplit.size() > 1) + { + for (unsigned int i = 0; i < a_URLSplit.size(); i++) + { + BaseURL += "../"; + } + BaseURL += "webadmin/"; + } + return BaseURL; +} + + + + + +void cWebAdmin::OnRequestBegun(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) + ) + { + a_Request.SetUserData(new cWebadminRequestData(a_Request)); + return; + } + 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 == "/") + { + // The root needs no body handler and is fully handled in the OnRequestFinished() call + HandleRootRequest(a_Connection, a_Request); + } + else + { + // TODO: Handle other requests + } + + // 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/src/WebAdmin.h b/src/WebAdmin.h new file mode 100644 index 000000000..a775acec2 --- /dev/null +++ b/src/WebAdmin.h @@ -0,0 +1,215 @@ + +// WebAdmin.h + +// Declares the cWebAdmin class representing the admin interface over http protocol, and related services (API) + +#pragma once + +#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 + + + + + +// fwd: +class cEvent; +class cWebPlugin; + + + + + +// tolua_begin +struct HTTPFormData +{ + std::string Name; + std::string Value; + std::string Type; +} ; +// tolua_end + + + + +// tolua_begin +struct HTTPRequest +{ + typedef std::map< std::string, std::string > StringStringMap; + typedef std::map< std::string, HTTPFormData > FormDataMap; + + AString Method; + AString Path; + AString Username; + // tolua_end + + /// Parameters given in the URL, after the questionmark + StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << + + /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) + StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << + + /// Same as PostParams + FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << +} ; // tolua_export + + + + + +// tolua_begin +struct HTTPTemplateRequest +{ + HTTPRequest Request; +} ; +// tolua_end + + + + + +// tolua_begin +struct sWebAdminPage +{ + AString Content; + AString PluginName; + AString TabName; +}; +// tolua_end + + + + + +// tolua_begin +class cWebAdmin : + public cHTTPServer::cCallbacks +{ +public: + // tolua_end + + typedef std::list< cWebPlugin* > PluginList; + + + cWebAdmin(void); + ~cWebAdmin(); + + /// 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 ); + + // 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 << + + // tolua_begin + + sWebAdminPage GetPage(const HTTPRequest & a_Request); + + /// Returns the contents of the default page - the list of plugins and players + AString GetDefaultPage(void); + + /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + AString GetBaseURL(const AString & a_URL); + + /// Escapes text passed into it, so it can be embedded into html. + static AString GetHTMLEscapedString(const AString & a_Input); + + // tolua_end + + /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + 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; + + PluginList m_Plugins; + + /// 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; + + + 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 + + + + + +// Revert MSVC warnings back to orignal state: +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + + + diff --git a/src/WebPlugin.cpp b/src/WebPlugin.cpp new file mode 100644 index 000000000..48ddb2076 --- /dev/null +++ b/src/WebPlugin.cpp @@ -0,0 +1,113 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WebPlugin.h" +#include "WebAdmin.h" +#include "Server.h" +#include "Root.h" + + + + + +cWebPlugin::cWebPlugin() +{ + cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); + if (WebAdmin != NULL) + { + WebAdmin->AddPlugin(this); + } +} + + + + + +cWebPlugin::~cWebPlugin() +{ + cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); + if (WebAdmin != NULL) + { + WebAdmin->RemovePlugin(this); + } + + for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr) + { + delete *itr; + } + m_Tabs.clear(); +} + + + + + +std::list<std::pair<AString, AString> > cWebPlugin::GetTabNames(void) +{ + std::list< std::pair< AString, AString > > NameList; + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + std::pair< AString, AString > StringPair; + StringPair.first = (*itr)->Title; + StringPair.second = (*itr)->SafeTitle; + NameList.push_back( StringPair ); + } + return NameList; +} + + + + + +std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request) +{ + std::pair< AString, AString > Names; + AStringVector Split = StringSplit(a_Request->Path, "/"); + + if( Split.size() > 1 ) + { + sWebPluginTab* Tab = 0; + if( Split.size() > 2 ) // If we got the tab name, show that page + { + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + if( (*itr)->SafeTitle.compare( Split[2] ) == 0 ) // This is the one! Rawr + { + Tab = *itr; + break; + } + } + } + else // Otherwise show the first tab + { + if( GetTabs().size() > 0 ) + Tab = *GetTabs().begin(); + } + + if( Tab ) + { + Names.first = Tab->Title; + Names.second = Tab->SafeTitle; + } + } + + return Names; +} + + + + +AString cWebPlugin::SafeString( const AString & a_String ) +{ + AString RetVal; + for( unsigned int i = 0; i < a_String.size(); ++i ) + { + char c = a_String[i]; + if( c == ' ' ) + { + c = '_'; + } + RetVal.push_back( c ); + } + return RetVal; +}
\ No newline at end of file diff --git a/src/WebPlugin.h b/src/WebPlugin.h new file mode 100644 index 000000000..22587b892 --- /dev/null +++ b/src/WebPlugin.h @@ -0,0 +1,48 @@ + +#pragma once + +struct lua_State; +struct HTTPRequest; + + + + + +// tolua_begin +class cWebPlugin +{ +public: + // tolua_end + cWebPlugin(); + virtual ~cWebPlugin(); + + // tolua_begin + virtual const AString GetWebTitle(void) const = 0; + + virtual AString HandleWebRequest(const HTTPRequest * a_Request ) = 0; + + static AString SafeString( const AString & a_String ); + // tolua_end + + struct sWebPluginTab + { + std::string Title; + std::string SafeTitle; + + int UserData; + }; + + typedef std::list< sWebPluginTab* > TabList; + TabList & GetTabs() { return m_Tabs; } + + typedef std::list< std::pair<AString, AString> > TabNameList; + TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS << + std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request ); + +private: + TabList m_Tabs; +}; // tolua_export + + + + diff --git a/src/World.cpp b/src/World.cpp new file mode 100644 index 000000000..fb8ee0cb3 --- /dev/null +++ b/src/World.cpp @@ -0,0 +1,2745 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BlockID.h" +#include "World.h" +#include "ChunkDef.h" +#include "ClientHandle.h" +#include "Server.h" +#include "Item.h" +#include "Root.h" +#include "inifile/iniFile.h" +#include "ChunkMap.h" +#include "OSSupport/Timer.h" + +// Entities (except mobs): +#include "Entities/ExpOrb.h" +#include "Entities/Pickup.h" +#include "Entities/Player.h" +#include "Entities/TNTEntity.h" + +// Simulators: +#include "Simulator/SimulatorManager.h" +#include "Simulator/FloodyFluidSimulator.h" +#include "Simulator/FluidSimulator.h" +#include "Simulator/FireSimulator.h" +#include "Simulator/NoopFluidSimulator.h" +#include "Simulator/SandSimulator.h" +#include "Simulator/RedstoneSimulator.h" +#include "Simulator/VaporizeFluidSimulator.h" + +// Mobs: +#include "Mobs/IncludeAllMonsters.h" +#include "MobCensus.h" +#include "MobSpawner.h" + +#include "MersenneTwister.h" +#include "Generating/Trees.h" +#include "PluginManager.h" +#include "Blocks/BlockHandler.h" +#include "Vector3d.h" + +#include "Tracer.h" +#include "tolua++.h" + +// DEBUG: Test out the cLineBlockTracer class by tracing a few lines: +#include "LineBlockTracer.h" + +#ifndef _WIN32 + #include <stdlib.h> +#endif + + + + + +/// Up to this many m_SpreadQueue elements are handled each world tick +const int MAX_LIGHTING_SPREAD_PER_TICK = 10; + +const int TIME_SUNSET = 12000; +const int TIME_NIGHT_START = 13187; +const int TIME_NIGHT_END = 22812; +const int TIME_SUNRISE = 23999; +const int TIME_SPAWN_DIVISOR = 148; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldLoadProgress: + +/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn() +class cWorldLoadProgress : + public cIsThread +{ +public: + cWorldLoadProgress(cWorld * a_World) : + cIsThread("cWorldLoadProgress"), + m_World(a_World) + { + Start(); + } + + void Stop(void) + { + m_ShouldTerminate = true; + Wait(); + } + +protected: + + cWorld * m_World; + + virtual void Execute(void) override + { + for (;;) + { + LOG("%d chunks to load, %d chunks to generate", + m_World->GetStorage().GetLoadQueueLength(), + m_World->GetGenerator().GetQueueLength() + ); + + // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish + for (int i = 0; i < 20; i++) + { + cSleep::MilliSleep(100); + if (m_ShouldTerminate) + { + return; + } + } + } // for (-ever) + } + +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldLightingProgress: + +/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn() +class cWorldLightingProgress : + public cIsThread +{ +public: + cWorldLightingProgress(cLightingThread * a_Lighting) : + cIsThread("cWorldLightingProgress"), + m_Lighting(a_Lighting) + { + Start(); + } + + void Stop(void) + { + m_ShouldTerminate = true; + Wait(); + } + +protected: + + cLightingThread * m_Lighting; + + virtual void Execute(void) override + { + for (;;) + { + LOG("%d chunks remaining to light", m_Lighting->GetQueueLength() + ); + + // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish + for (int i = 0; i < 20; i++) + { + cSleep::MilliSleep(100); + if (m_ShouldTerminate) + { + return; + } + } + } // for (-ever) + } + +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cLock: + +cWorld::cLock::cLock(cWorld & a_World) : + super(&(a_World.m_ChunkMap->GetCS())) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cTickThread: + +cWorld::cTickThread::cTickThread(cWorld & a_World) : + super(Printf("WorldTickThread: %s", a_World.GetName().c_str())), + m_World(a_World) +{ +} + + + + + +void cWorld::cTickThread::Execute(void) +{ + cTimer Timer; + + long long msPerTick = 50; + long long LastTime = Timer.GetNowTime(); + + while (!m_ShouldTerminate) + { + long long NowTime = Timer.GetNowTime(); + float DeltaTime = (float)(NowTime - LastTime); + m_World.Tick(DeltaTime); + long long TickTime = Timer.GetNowTime() - NowTime; + + if (TickTime < msPerTick) + { + // Stretch tick time until it's at least msPerTick + cSleep::MilliSleep((unsigned int)(msPerTick - TickTime)); + } + + LastTime = NowTime; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld: + +cWorld::cWorld(const AString & a_WorldName) : + m_WorldName(a_WorldName), + m_IniFileName(m_WorldName + "/world.ini"), + m_StorageSchema("Default"), + m_WorldAgeSecs(0), + m_TimeOfDaySecs(0), + m_WorldAge(0), + m_TimeOfDay(0), + m_LastTimeUpdate(0), + m_RSList(0), + m_Weather(eWeather_Sunny), + m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) + m_TickThread(*this), + m_SkyDarkness(0) +{ + LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); + + cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName); +} + + + + + +cWorld::~cWorld() +{ + delete m_SimulatorManager; + delete m_SandSimulator; + delete m_WaterSimulator; + delete m_LavaSimulator; + delete m_FireSimulator; + delete m_RedstoneSimulator; + + UnloadUnusedChunks(); + + m_Storage.WaitForFinish(); + + delete m_ChunkMap; +} + + + + + +void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::SetWeather(eWeather a_NewWeather) +{ + // Do the plugins agree? Do they want a different weather? + cRoot::Get()->GetPluginManager()->CallHookWeatherChanging(*this, a_NewWeather); + + // Set new period for the selected weather: + switch (a_NewWeather) + { + case eWeather_Sunny: m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); break; // 12 - 16 minutes + case eWeather_Rain: m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); break; // 8 - 14 minutes + case eWeather_ThunderStorm: m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); break; // 2 - 6 minutes + default: + { + LOGWARNING("Requested unknown weather %d, setting sunny for a minute instead.", a_NewWeather); + a_NewWeather = eWeather_Sunny; + m_WeatherInterval = 1200; + break; + } + } // switch (NewWeather) + m_Weather = a_NewWeather; + BroadcastWeather(m_Weather); + + // Let the plugins know about the change: + cPluginManager::Get()->CallHookWeatherChanged(*this); +} + + + + + +void cWorld::ChangeWeather(void) +{ + // In the next tick the weather will be changed + m_WeatherInterval = 0; +} + + + + + +void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::InitializeSpawn(void) +{ + int ChunkX = 0, ChunkY = 0, ChunkZ = 0; + BlockToChunk((int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ); + + // For the debugging builds, don't make the server build too much world upon start: + #if defined(_DEBUG) || defined(ANDROID_NDK) + int ViewDist = 9; + #else + int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is + #endif // _DEBUG + + LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str()); + for (int x = 0; x < ViewDist; x++) + { + for (int z = 0; z < ViewDist; z++) + { + m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader + } + } + + { + // Display progress during this process: + cWorldLoadProgress Progress(this); + + // Wait for the loader to finish loading + m_Storage.WaitForQueuesEmpty(); + + // Wait for the generator to finish generating + m_Generator.WaitForQueueEmpty(); + + Progress.Stop(); + } + + // Light all chunks that have been newly generated: + LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str()); + + for (int x = 0; x < ViewDist; x++) + { + int ChX = x + ChunkX-(ViewDist - 1) / 2; + for (int z = 0; z < ViewDist; z++) + { + int ChZ = z + ChunkZ-(ViewDist - 1) / 2; + if (!m_ChunkMap->IsChunkLighted(ChX, ChZ)) + { + m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread + } + } // for z + } // for x + + { + cWorldLightingProgress Progress(&m_Lighting); + m_Lighting.WaitForQueueEmpty(); + Progress.Stop(); + } + + // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already + m_SpawnY = (double)GetHeight((int)m_SpawnX, (int)m_SpawnZ) + 1.6f; // +1.6f eye height + + + #ifdef TEST_LINEBLOCKTRACER + // DEBUG: Test out the cLineBlockTracer class by tracing a few lines: + class cTracerCallbacks : + public cBlockTracer::cCallbacks + { + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + LOGD("Block {%d, %d, %d}: %d:%d (%s)", + a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, + ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() + ); + return false; + } + + virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) override + { + LOGD("Block {%d, %d, %d}: no data available", + a_BlockX, a_BlockY, a_BlockZ + ); + return false; + } + + virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + LOGD("Out of world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ); + return false; + } + + virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override + { + LOGD("Into world at {%f, %f, %f}", a_BlockX, a_BlockY, a_BlockZ); + return false; + } + + virtual void OnNoMoreHits(void) override + { + LOGD("No more hits"); + } + } Callbacks; + LOGD("Spawn is at {%f, %f, %f}", m_SpawnX, m_SpawnY, m_SpawnZ); + LOGD("Tracing a line along +X:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY, m_SpawnZ, m_SpawnX + 10, m_SpawnY, m_SpawnZ); + LOGD("Tracing a line along -Z:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, m_SpawnY, m_SpawnZ + 10, m_SpawnX, m_SpawnY, m_SpawnZ - 10); + LOGD("Tracing a line along -Y, out of world:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX, 260, m_SpawnZ, m_SpawnX, -5, m_SpawnZ); + LOGD("Tracing a line along XY:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 10, m_SpawnY - 10, m_SpawnZ, m_SpawnX + 10, m_SpawnY + 10, m_SpawnZ); + LOGD("Tracing a line in generic direction:"); + cLineBlockTracer::Trace(*this, Callbacks, m_SpawnX - 15, m_SpawnY - 5, m_SpawnZ + 7.5, m_SpawnX + 13, m_SpawnY - 10, m_SpawnZ + 8.5); + LOGD("Tracing tests done"); + #endif // TEST_LINEBLOCKTRACER +} + + + + + +void cWorld::Start(void) +{ + // TODO: Find a proper spawn location, based on the biomes (not in ocean) + m_SpawnX = (double)((m_TickRand.randInt() % 1000) - 500); + m_SpawnY = cChunkDef::Height; + m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500); + m_GameMode = eGameMode_Creative; + + cIniFile IniFile; + if (!IniFile.ReadFile(m_IniFileName)) + { + LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str()); + } + AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld"); + m_Dimension = StringToDimension(Dimension); + switch (m_Dimension) + { + case dimNether: + case dimOverworld: + case dimEnd: + { + break; + } + default: + { + LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str()); + m_Dimension = dimOverworld; + break; + } + } // switch (m_Dimension) + m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX); + m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY); + m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ); + m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema); + m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3); + m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3); + m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false); + m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true); + m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true); + m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true); + m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true); + m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false); + m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true); + m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true); + m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false); + m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true); + m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false); + m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true); + m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false); + + m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode); + + // Load allowed mobs: + const char * DefaultMonsters = ""; + switch (m_Dimension) + { + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; + case dimEnd: DefaultMonsters = "enderman"; break; + default: + { + ASSERT(!"Unhandled world dimension"); + DefaultMonsters = "wither"; + break; + } + } + m_bAnimals = IniFile.GetValueSetB("Monsters", "AnimalsOn", true); + AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters); + AStringVector SplitList = StringSplitAndTrim(AllMonsters, ","); + for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr) + { + cMonster::eType ToAdd = cMonster::StringToMobType(*itr); + if (ToAdd != cMonster::mtInvalidType) + { + m_AllowedMobs.insert(ToAdd); + LOGD("Allowed mob: %s", itr->c_str()); + } + else + { + LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str()); + } + } + + m_ChunkMap = new cChunkMap(this); + + m_LastSave = 0; + m_LastUnload = 0; + + // preallocate some memory for ticking blocks so we don't need to allocate that often + m_BlockTickQueue.reserve(1000); + m_BlockTickQueueCopy.reserve(1000); + + // Simulators: + m_SimulatorManager = new cSimulatorManager(*this); + m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); + m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); + m_SandSimulator = new cSandSimulator(*this, IniFile); + m_FireSimulator = new cFireSimulator(*this, IniFile); + m_RedstoneSimulator = new cRedstoneSimulator(*this); + + // Water and Lava simulators get registered in InitializeFluidSimulator() + m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1); + m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1); + m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); + + m_Lighting.Start(this); + m_Storage.Start(this, m_StorageSchema); + m_Generator.Start(this, IniFile); + m_ChunkSender.Start(this); + m_TickThread.Start(); + + // Init of the spawn monster time (as they are supposed to have different spawn rate) + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0)); + + + // Save any changes that the defaults may have done to the ini file: + if (!IniFile.WriteFile(m_IniFileName)) + { + LOGWARNING("Could not write world config to %s", m_IniFileName.c_str()); + } + +} + + + + + +void cWorld::Stop(void) +{ + // Delete the clients that have been in this world: + { + cCSLock Lock(m_CSClients); + for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + (*itr)->Destroy(); + delete *itr; + } // for itr - m_Clients[] + m_Clients.clear(); + } + + m_TickThread.Stop(); + m_Lighting.Stop(); + m_Generator.Stop(); + m_ChunkSender.Stop(); + m_Storage.Stop(); +} + + + + + +void cWorld::Tick(float a_Dt) +{ + // Call the plugins + cPluginManager::Get()->CallHookWorldTick(*this, a_Dt); + + // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it + m_WorldAgeSecs += (double)a_Dt / 1000.0; + m_TimeOfDaySecs += (double)a_Dt / 1000.0; + + // Wrap time of day each 20 minutes (1200 seconds) + if (m_TimeOfDaySecs > 1200.0) + { + m_TimeOfDaySecs -= 1200.0; + } + + m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0); + m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0); + + // Updates the sky darkness based on current time of day + UpdateSkyDarkness(); + + // Broadcast time update every 40 ticks (2 seconds) + if (m_LastTimeUpdate < m_WorldAge - 40) + { + BroadcastTimeUpdate(); + m_LastTimeUpdate = m_WorldAge; + } + + m_ChunkMap->Tick(a_Dt); + + TickClients(a_Dt); + TickQueuedBlocks(); + TickQueuedTasks(); + + GetSimulatorManager()->Simulate(a_Dt); + + TickWeather(a_Dt); + + // Asynchronously set blocks: + sSetBlockList FastSetBlockQueueCopy; + { + cCSLock Lock(m_CSFastSetBlock); + std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue); + } + m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy); + if (!FastSetBlockQueueCopy.empty()) + { + // Some blocks failed, store them for next tick: + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy); + } + + if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes + { + SaveAllChunks(); + } + + if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds + { + UnloadUnusedChunks(); + } + + TickMobs(a_Dt); + + std::vector<int> m_RSList_copy(m_RSList); + + m_RSList.clear(); + + std::vector<int>::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) + for (cii = m_RSList_copy.begin(); cii != m_RSList_copy.end();) + { + int tempX = *cii; cii++; + int tempY = *cii; cii++; + int tempZ = *cii; cii++; + int state = *cii; cii++; + + if ((state == 11111) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_OFF)) + { + FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta(tempX, tempY, tempZ)); + } + else if ((state == 00000) && ((int)GetBlock(tempX, tempY, tempZ) == E_BLOCK_REDSTONE_TORCH_ON)) + { + FastSetBlock(tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta(tempX, tempY, tempZ)); + } + } + m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); +} + + + + + +void cWorld::TickWeather(float a_Dt) +{ + // There are no weather changes anywhere but in the Overworld: + if (GetDimension() != dimOverworld) + { + return; + } + + if (m_WeatherInterval > 0) + { + // Not yet, wait for the weather period to end + m_WeatherInterval--; + } + else + { + // Change weather: + + // Pick a new weather. Only reasonable transitions allowed: + eWeather NewWeather = m_Weather; + switch (m_Weather) + { + case eWeather_Sunny: NewWeather = eWeather_Rain; break; + case eWeather_ThunderStorm: NewWeather = eWeather_Rain; break; + case eWeather_Rain: + { + // 1/8 chance of turning into a thunderstorm + NewWeather = ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny; + break; + } + + default: + { + LOGWARNING("Unknown current weather: %d. Setting sunny.", m_Weather); + ASSERT(!"Unknown weather"); + NewWeather = eWeather_Sunny; + } + } + + SetWeather(NewWeather); + } // else (m_WeatherInterval > 0) + + if (m_Weather == eWeather_ThunderStorm) + { + // 0.5% chance per tick of thunderbolt + if (m_TickRand.randInt() % 199 == 0) + { + CastThunderbolt(0, 0, 0); // TODO: find random possitions near players to cast thunderbolts. + } + } +} + + + + + +void cWorld::TickMobs(float a_Dt) +{ + // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs + cWorld::cLock Lock(*this); + + // before every Mob action, we have to count them depending on the distance to players, on their family ... + cMobCensus MobCensus; + m_ChunkMap->CollectMobCensus(MobCensus); + if (m_bAnimals) + { + // Spawning is enabled, spawn now: + static const cMonster::eFamily AllFamilies[] = + { + cMonster::mfHostile, + cMonster::mfPassive, + cMonster::mfAmbient, + cMonster::mfWater, + } ; + for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++) + { + cMonster::eFamily Family = AllFamilies[i]; + int SpawnDelay = cMonster::GetSpawnDelay(Family); + if ( + (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round + MobCensus.IsCapped(Family) + ) + { + continue; + } + m_LastSpawnMonster[Family] = m_WorldAge; + cMobSpawner Spawner(Family, m_AllowedMobs); + if (Spawner.CanSpawnAnything()) + { + m_ChunkMap->SpawnMobs(Spawner); + // do the spawn + for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++) + { + SpawnMobFinalize(*itr2); + } + } + } // for i - AllFamilies[] + } // if (Spawning enabled) + + // move close mobs + cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++) + { + itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk); + } + + // remove too far mobs + cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++) + { + itr->second.m_Monster.Destroy(true); + } +} + + + + + +void cWorld::TickQueuedTasks(void) +{ + // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks + cTasks Tasks; + { + cCSLock Lock(m_CSTasks); + std::swap(Tasks, m_Tasks); + } + + // Execute and delete each task: + for (cTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr) + { + (*itr)->Run(*this); + delete *itr; + } // for itr - m_Tasks[] +} + + + + + +void cWorld::TickClients(float a_Dt) +{ + cClientHandleList RemoveClients; + { + cCSLock Lock(m_CSClients); + + // Remove clients scheduled for removal: + for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) + { + m_Clients.remove(*itr); + } // for itr - m_ClientsToRemove[] + m_ClientsToRemove.clear(); + + // Add clients scheduled for adding: + for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr) + { + if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end()) + { + ASSERT(!"Adding a client that is already in the clientlist"); + continue; + } + m_Clients.push_back(*itr); + } // for itr - m_ClientsToRemove[] + m_ClientsToAdd.clear(); + + // Tick the clients, take out those that have been destroyed into RemoveClients + for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) + { + if ((*itr)->IsDestroyed()) + { + // Remove the client later, when CS is not held, to avoid deadlock + RemoveClients.push_back(*itr); + itr = m_Clients.erase(itr); + continue; + } + (*itr)->Tick(a_Dt); + ++itr; + } // for itr - m_Clients[] + } + + // Delete the clients that have been destroyed + for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) + { + delete *itr; + } // for itr - RemoveClients[] +} + + + + + +void cWorld::UpdateSkyDarkness(void) +{ + int TempTime = (int)m_TimeOfDay; + if (TempTime <= TIME_SUNSET) + { + m_SkyDarkness = 0; + } + else if (TempTime <= TIME_NIGHT_START) + { + m_SkyDarkness = (TIME_NIGHT_START - TempTime) / TIME_SPAWN_DIVISOR; + } + else if (TempTime <= TIME_NIGHT_END) + { + m_SkyDarkness = 8; + } + else + { + m_SkyDarkness = (TIME_SUNRISE - TempTime) / TIME_SPAWN_DIVISOR; + } +} + + + + + +void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +/// Wakes up the simulators for the specified area of blocks +void cWorld::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) +{ + return m_ChunkMap->WakeUpSimulatorsInArea(a_MinBlockX, a_MaxBlockX, a_MinBlockY, a_MaxBlockY, a_MinBlockZ, a_MaxBlockZ); +} + + + + + +bool cWorld::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachBlockEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) +{ + return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) +{ + return m_ChunkMap->ForEachDispenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback) +{ + return m_ChunkMap->ForEachDropperInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback) +{ + return m_ChunkMap->ForEachDropSpenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) +{ + return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData) +{ + if (cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) || (a_ExplosionSize <= 0)) + { + return; + } + + // TODO: Add damage to entities, add support for pickups, and implement block hardiness + Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); + cVector3iArray BlocksAffected; + m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected); + BroadcastSoundEffect("random.explode", (int)floor(a_BlockX * 8), (int)floor(a_BlockY * 8), (int)floor(a_BlockZ * 8), 1.0f, 0.6f); + { + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos; + if (distance_explosion.SqrLength() < 4096.0) + { + double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength())); + double power = a_ExplosionSize / real_distance; + if (power <= 1) + { + power = 0; + } + distance_explosion.Normalize(); + distance_explosion *= power; + ch->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, (float)a_ExplosionSize, BlocksAffected, distance_explosion); + } + } + } + cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData); +} + + + + + +bool cWorld::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback) +{ + return m_ChunkMap->DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + return m_ChunkMap->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback) +{ + return m_ChunkMap->DoWithDropperAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback) +{ + return m_ChunkMap->DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback) +{ + return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +void cWorld::GrowTree(int a_X, int a_Y, int a_Z) +{ + if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) + { + // There is a sapling here, grow a tree according to its type: + GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); + } + else + { + // There is nothing here, grow a tree based on the current biome here: + GrowTreeByBiome(a_X, a_Y, a_Z); + } +} + + + + + +void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_SaplingMeta) +{ + cNoise Noise(m_Generator.GetSeed()); + sSetBlockVector Logs, Other; + switch (a_SaplingMeta & 0x07) + { + case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + } + Other.insert(Other.begin(), Logs.begin(), Logs.end()); + Logs.clear(); + GrowTreeImage(Other); +} + + + + + +void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) +{ + cNoise Noise(m_Generator.GetSeed()); + sSetBlockVector Logs, Other; + GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), (EMCSBiome)GetBiomeAt(a_X, a_Z), Logs, Other); + Other.insert(Other.begin(), Logs.begin(), Logs.end()); + Logs.clear(); + GrowTreeImage(Other); +} + + + + + +void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) +{ + // Check that the tree has place to grow + + // Make a copy of the log blocks: + sSetBlockVector b2; + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + if (itr->BlockType == E_BLOCK_LOG) + { + b2.push_back(*itr); + } + } // for itr - a_Blocks[] + + // Query blocktypes and metas at those log blocks: + if (!GetBlocks(b2, false)) + { + return; + } + + // Check that at each log's coord there's an block allowed to be overwritten: + for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr) + { + switch (itr->BlockType) + { + CASE_TREE_ALLOWED_BLOCKS: + { + break; + } + default: + { + return; + } + } + } // for itr - b2[] + + // All ok, replace blocks with the tree image: + m_ChunkMap->ReplaceTreeBlocks(a_Blocks); +} + + + + + +bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + switch (BlockType) + { + case E_BLOCK_CARROTS: + { + if (a_IsByBonemeal && !m_IsCarrotsBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_CROPS: + { + if (a_IsByBonemeal && !m_IsCropsBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_MELON_STEM: + { + if (BlockMeta < 7) + { + if (a_IsByBonemeal && !m_IsMelonStemBonemealable) + { + return false; + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + else + { + if (a_IsByBonemeal && !m_IsMelonBonemealable) + { + return false; + } + GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + } + return true; + } + + case E_BLOCK_POTATOES: + { + if (a_IsByBonemeal && !m_IsPotatoesBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_PUMPKIN_STEM: + { + if (BlockMeta < 7) + { + if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable) + { + return false; + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + else + { + if (a_IsByBonemeal && !m_IsPumpkinBonemealable) + { + return false; + } + GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + } + return true; + } + + case E_BLOCK_SAPLING: + { + if (a_IsByBonemeal && !m_IsSaplingBonemealable) + { + return false; + } + GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); + return true; + } + + case E_BLOCK_GRASS: + { + if (a_IsByBonemeal && !m_IsGrassBonemealable) + { + return false; + } + MTRand r1; + for (int i = 0; i < 60; i++) + { + int OfsX = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + int OfsY = r1.randInt(3) + r1.randInt(3) - 3; + int OfsZ = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); + if (Ground != E_BLOCK_GRASS) + { + continue; + } + BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ); + if (Above != E_BLOCK_AIR) + { + continue; + } + BLOCKTYPE SpawnType; + NIBBLETYPE SpawnMeta = 0; + switch (r1.randInt(10)) + { + case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; + case 1: SpawnType = E_BLOCK_RED_ROSE; break; + default: + { + SpawnType = E_BLOCK_TALL_GRASS; + SpawnMeta = E_META_TALL_GRASS_GRASS; + break; + } + } // switch (random spawn block type) + FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta); + } // for i - 50 times + return true; + } + + case E_BLOCK_SUGARCANE: + { + if (a_IsByBonemeal && !m_IsSugarcaneBonemealable) + { + return false; + } + m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight); + return true; + } + + case E_BLOCK_CACTUS: + { + if (a_IsByBonemeal && !m_IsCactusBonemealable) + { + return false; + } + m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight); + return true; + } + } // switch (BlockType) + return false; +} + + + + + +void cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); +} + + + + + +void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +{ + MTRand Rand; + m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); +} + + + + + +void cWorld::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); +} + + + + + +int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ) +{ + return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ); +} + + + + + +void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if (a_BlockType == E_BLOCK_AIR) + { + BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(this, a_BlockX, a_BlockY, a_BlockZ); + } + m_ChunkMap->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + + BlockHandler(a_BlockType)->OnPlaced(this, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cWorld::FastSetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta)); +} + + + + + +void cWorld::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay) +{ + m_ChunkMap->QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, GetWorldAge() + a_TickDelay); +} + + + + + +BLOCKTYPE cWorld::GetBlock(int a_X, int a_Y, int a_Z) +{ + // First check if it isn't queued in the m_FastSetBlockQueue: + { + int X = a_X, Y = a_Y, Z = a_Z; + int ChunkX, ChunkY, ChunkZ; + AbsoluteToRelative(X, Y, Z, ChunkX, ChunkY, ChunkZ); + + cCSLock Lock(m_CSFastSetBlock); + for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) + { + if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + return itr->BlockType; + } + } // for itr - m_FastSetBlockQueue[] + } + + return m_ChunkMap->GetBlock(a_X, a_Y, a_Z); +} + + + + + +NIBBLETYPE cWorld::GetBlockMeta(int a_X, int a_Y, int a_Z) +{ + // First check if it isn't queued in the m_FastSetBlockQueue: + { + cCSLock Lock(m_CSFastSetBlock); + for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) + { + if ((itr->x == a_X) && (itr->y == a_Y) && (itr->y == a_Y)) + { + return itr->BlockMeta; + } + } // for itr - m_FastSetBlockQueue[] + } + + return m_ChunkMap->GetBlockMeta(a_X, a_Y, a_Z); +} + + + + + +void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData) +{ + m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); +} + + + + + +NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z) +{ + return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); +} + + + + + +NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +bool cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + return m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); +} + + + + + +bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +{ + return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); +} + + + + + +bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + return m_ChunkMap->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); +} + + + + + +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated) +{ + MTRand r1; + a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop + for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) + { + float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + float SpeedY = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + + cPickup * Pickup = new cPickup( + a_BlockX, a_BlockY, a_BlockZ, + *itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ + ); + Pickup->Initialize(this); + } +} + + + + + +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated) +{ + for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) + { + cPickup * Pickup = new cPickup( + a_BlockX, a_BlockY, a_BlockZ, + *itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ + ); + Pickup->Initialize(this); + } +} + + + + + +int cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) +{ + cExpOrb * ExpOrb = new cExpOrb(a_X, a_Y, a_Z, a_Reward); + ExpOrb->Initialize(this); + return ExpOrb->GetUniqueID(); +} + + + + + +void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff) +{ + cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec); + TNT->Initialize(this); + // TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff +} + + + + + +void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) +{ + m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); +} + + + + + +bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) +{ + return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure); +} + + + + + +bool cWorld::DigBlock(int a_X, int a_Y, int a_Z) +{ + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); + Handler->OnDestroyed(this, a_X, a_Y, a_Z); + return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); +} + + + + + +void cWorld::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) +{ + m_ChunkMap->SendBlockTo(a_X, a_Y, a_Z, a_Player); +} + + + + + +int cWorld::GetHeight(int a_X, int a_Z) +{ + return m_ChunkMap->GetHeight(a_X, a_Z); +} + + + + + +bool cWorld::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height) +{ + return m_ChunkMap->TryGetHeight(a_BlockX, a_BlockZ, a_Height); +} + + + + + +void cWorld::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + return m_ChunkMap->BroadcastAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); +} + + + + + +void cWorld::BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockBreakAnimation(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage, a_Exclude); +} + + + + + +void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendChat(a_Message); + } +} + + + + + +void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); +} + + + + + +void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); +} + + + + + +void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastDestroyEntity(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityHeadLook(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityLook(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityMetadata(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityVelocity(a_Entity, a_Exclude); +} + + + + +void cWorld::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); +} + + + + + +void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendPlayerListItem(a_Player, a_IsOnline); + } +} + + + + + +void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); +} + + + + + +void cWorld::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data, a_Exclude); +} + + + + + +void cWorld::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSpawnEntity(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastTeleportEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendTeleportEntity(a_Entity); + } +} + + + + + +void cWorld::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay); + } +} + + + + + +void cWorld::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_ChunkMap->BroadcastUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::BroadcastWeather(eWeather a_Weather, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendWeather(a_Weather); + } +} + + + + + +void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + m_ChunkMap->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); +} + + + + + +void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkSaving(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkSaving(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkSaved (a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap * a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty +) +{ + // Validate biomes, if needed: + cChunkDef::BiomeMap BiomeMap; + const cChunkDef::BiomeMap * Biomes = a_BiomeMap; + if (a_BiomeMap == NULL) + { + // The biomes are not assigned, get them from the generator: + Biomes = &BiomeMap; + m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap); + } + + m_ChunkMap->SetChunkData( + a_ChunkX, a_ChunkZ, + a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, + a_HeightMap, *Biomes, + a_BlockEntities, + a_MarkDirty + ); + + // Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347): + for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr) + { + (*itr)->Initialize(this); + } + + // If a client is requesting this chunk, send it to them: + if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) + { + m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkZ); + } + + // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk): + m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight); +} + + + + + +bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback) +{ + return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) +{ + return m_ChunkMap->GetChunkBlockTypes(a_ChunkX, a_ChunkZ, a_BlockTypes); +} + + + + + +bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkZ) const +{ + return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkZ); +} + + + + + +bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const +{ + return m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::UnloadUnusedChunks(void) +{ + m_LastUnload = m_WorldAge; + m_ChunkMap->UnloadUnusedChunks(); +} + + + + + +void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) +{ + m_ChunkMap->CollectPickupsByPlayer(a_Player); +} + + + + + +void cWorld::AddPlayer(cPlayer * a_Player) +{ + { + cCSLock Lock(m_CSPlayers); + + ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW? + + m_Players.remove(a_Player); // Make sure the player is registered only once + m_Players.push_back(a_Player); + } + + // Add the player's client to the list of clients to be ticked: + if (a_Player->GetClientHandle() != NULL) + { + cCSLock Lock(m_CSClients); + m_ClientsToAdd.push_back(a_Player->GetClientHandle()); + } + + // The player has already been added to the chunkmap as the entity, do NOT add again! +} + + + + + +void cWorld::RemovePlayer(cPlayer * a_Player) +{ + m_ChunkMap->RemoveEntity(a_Player); + { + cCSLock Lock(m_CSPlayers); + m_Players.remove(a_Player); + } + + // Remove the player's client from the list of clients to be ticked: + if (a_Player->GetClientHandle() != NULL) + { + cCSLock Lock(m_CSClients); + m_ClientsToRemove.push_back(a_Player->GetClientHandle()); + } +} + + + + + +bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Players[] + return true; +} + + + + + +bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0) + { + a_Callback.Item(*itr); + return true; + } + } // for itr - m_Players[] + return false; +} + + + + + +bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback) +{ + cPlayer * BestMatch = NULL; + unsigned int BestRating = 0; + unsigned int NameLength = a_PlayerNameHint.length(); + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + unsigned int Rating = RateCompareString (a_PlayerNameHint, (*itr)->GetName()); + if (Rating >= BestRating) + { + BestMatch = *itr; + BestRating = Rating; + } + if (Rating == NameLength) // Perfect match + { + break; + } + } // for itr - m_Players[] + + if (BestMatch != NULL) + { + LOG("Compared %s and %s with rating %i", a_PlayerNameHint.c_str(), BestMatch->GetName().c_str(), BestRating); + return a_Callback.Item (BestMatch); + } + return false; +} + + + + + +// TODO: This interface is dangerous! +cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) +{ + cTracer LineOfSight(this); + + float ClosestDistance = a_SightLimit; + cPlayer* ClosestPlayer = NULL; + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Vector3f Pos = (*itr)->GetPosition(); + float Distance = (Pos - a_Pos).Length(); + + if (Distance < ClosestDistance) + { + if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + { + ClosestDistance = Distance; + ClosestPlayer = *itr; + } + } + } + return ClosestPlayer; +} + + + + + +void cWorld::SendPlayerList(cPlayer * a_DestPlayer) +{ + // Sends the playerlist to a_DestPlayer + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch != NULL) && !ch->IsDestroyed()) + { + a_DestPlayer->GetClientHandle()->SendPlayerListItem(*(*itr), true); + } + } +} + + + + + +bool cWorld::ForEachEntity(cEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachEntity(a_Callback); +} + + + + + +bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) +{ + return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback); +} + + + + + +void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback) +{ + m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkZ1, a_ChunkX2, a_ChunkZ2, a_Callback); +} + + + + + +bool cWorld::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + return m_ChunkMap->AddChunkClient(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkMap->RemoveChunkClient(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunks(cClientHandle * a_Client) +{ + m_ChunkMap->RemoveClientFromChunks(a_Client); +} + + + + + +void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) +{ + m_ChunkSender.RemoveClient(a_Client); +} + + + + + +void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks) +{ + m_ChunkMap->LoadChunks(a_Chunks); +} + + + + + +void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + AString Line1(a_Line1); + AString Line2(a_Line2); + AString Line3(a_Line3); + AString Line4(a_Line4); + if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player)) + { + return false; + } + if (m_ChunkMap->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4)) + { + cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player); + return true; + } + return false; +} + + + + + +bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + return SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player); +} + + + + + +void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) +{ + m_ChunkMap->ChunksStay(a_Chunks, a_Stay); +} + + + + + +void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); + + // Trick: use Y=1 to force the chunk generation even though the chunk data is already present + m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ); +} + + + + + +void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) +{ + m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); +} + + + + + +void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ) +{ + return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ); +} + + + + + +bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) +{ + return m_ChunkMap->ForEachChunkInRect(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ, a_Callback); +} + + + + + +void cWorld::SaveAllChunks(void) +{ + LOGINFO("Saving all chunks..."); + m_LastSave = m_WorldAge; + m_ChunkMap->SaveAllChunks(); + m_Storage.QueueSavedMessage(); +} + + + + + +void cWorld::QueueSaveAllChunks(void) +{ + QueueTask(new cWorld::cTaskSaveAllChunks); +} + + + + + +void cWorld::QueueTask(cTask * a_Task) +{ + cCSLock Lock(m_CSTasks); + m_Tasks.push_back(a_Task); +} + + + + + +void cWorld::AddEntity(cEntity * a_Entity) +{ + m_ChunkMap->AddEntity(a_Entity); +} + + + + + +bool cWorld::HasEntity(int a_UniqueID) +{ + return m_ChunkMap->HasEntity(a_UniqueID); +} + + + + + +void cWorld::RemoveEntity(cEntity * a_Entity) +{ + m_ChunkMap->RemoveEntity(a_Entity); +} + + + + + +/* +unsigned int cWorld::GetNumPlayers(void) +{ + cCSLock Lock(m_CSPlayers); + return m_Players.size(); +} +*/ + + + + + +int cWorld::GetNumChunks(void) const +{ + return m_ChunkMap->GetNumChunks(); +} + + + + + +void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue) +{ + m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty); + a_NumInLightingQueue = (int) m_Lighting.GetQueueLength(); +} + + + + + +void cWorld::TickQueuedBlocks(void) +{ + if (m_BlockTickQueue.empty()) + { + return; + } + m_BlockTickQueueCopy.clear(); + m_BlockTickQueue.swap(m_BlockTickQueueCopy); + + for (std::vector<BlockTickQueueItem *>::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++) + { + BlockTickQueueItem *Block = (*itr); + Block->TicksToWait -= 1; + if (Block->TicksToWait <= 0) + { + // TODO: Handle the case when the chunk is already unloaded + BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z); + delete Block; // We don't have to remove it from the vector, this will happen automatically on the next tick + } + else + { + m_BlockTickQueue.push_back(Block); // Keep the block in the queue + } + } // for itr - m_BlockTickQueueCopy[] +} + + + + + +void cWorld::QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait) +{ + BlockTickQueueItem * Block = new BlockTickQueueItem; + Block->X = a_BlockX; + Block->Y = a_BlockY; + Block->Z = a_BlockZ; + Block->TicksToWait = a_TicksToWait; + + m_BlockTickQueue.push_back(Block); +} + + + + + +bool cWorld::IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return ( + IsBlockWater(GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ)) || + IsBlockWater(GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ)) || + IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1)) || + IsBlockWater(GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1)) + ); +} + + + + + +int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) +{ + cMonster * Monster = NULL; + + Monster = cMonster::NewMonsterFromType(a_MonsterType); + if (Monster != NULL) + { + Monster->SetPosition(a_PosX, a_PosY, a_PosZ); + } + + // Because it's logical that ALL mob spawns need spawn effects, not just spawners + BroadcastSoundParticleEffect(2004, (int)a_PosX, (int)a_PosY, (int)a_PosZ, 0); + + return SpawnMobFinalize(Monster); +} + + + + +int cWorld::SpawnMobFinalize(cMonster * a_Monster) +{ + if (!a_Monster) + return -1; + a_Monster->SetHealth(a_Monster->GetMaxHealth()); + if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster)) + { + delete a_Monster; + return -1; + } + if (!a_Monster->Initialize(this)) + { + delete a_Monster; + return -1; + } + BroadcastSpawnEntity(*a_Monster); + cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); + + return a_Monster->GetUniqueID(); +} + + + + + +int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed) +{ + cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Speed); + if (Projectile == NULL) + { + return -1; + } + if (!Projectile->Initialize(this)) + { + delete Projectile; + return -1; + } + BroadcastSpawnEntity(*Projectile); + return Projectile->GetUniqueID(); +} + + + + + +void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr) + { + size_t LastSpace = a_Text.find_last_of(" "); //Find the position of the last space + + std::string LastWord = a_Text.substr(LastSpace + 1, a_Text.length()); //Find the last word + std::string PlayerName ((*itr)->GetName()); + std::size_t Found = PlayerName.find(LastWord); //Try to find last word in playername + + if (Found!=0) + { + continue; //No match + } + + a_Results.push_back((*itr)->GetName()); //Match! + } +} + + + + + +cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock) +{ + AString SimulatorNameKey; + Printf(SimulatorNameKey, "%sSimulator", a_FluidName); + AString SimulatorSectionName; + Printf(SimulatorSectionName, "%sSimulator", a_FluidName); + AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, ""); + if (SimulatorName.empty()) + { + LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str()); + SimulatorName = "Floody"; + } + + cFluidSimulator * res = NULL; + bool IsWater = (strcmp(a_FluidName, "Water") == 0); // Used for defaults + int Rate = 1; + if ( + (NoCaseCompare(SimulatorName, "vaporize") == 0) || + (NoCaseCompare(SimulatorName, "vaporise") == 0) + ) + { + res = new cVaporizeFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock); + } + else if ( + (NoCaseCompare(SimulatorName, "noop") == 0) || + (NoCaseCompare(SimulatorName, "nop") == 0) || + (NoCaseCompare(SimulatorName, "null") == 0) || + (NoCaseCompare(SimulatorName, "nil") == 0) + ) + { + res = new cNoopFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock); + } + else + { + if (NoCaseCompare(SimulatorName, "floody") != 0) + { + // The simulator name doesn't match anything we have, issue a warning: + LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Floody\".", GetIniFileName().c_str(), SimulatorNameKey.c_str()); + } + int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2); + int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30); + int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1); + res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); + } + + m_SimulatorManager->RegisterSimulator(res, Rate); + + return res; +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cTaskSaveAllChunks: + +void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) +{ + a_World.SaveAllChunks(); +} + + + + + diff --git a/src/World.h b/src/World.h new file mode 100644 index 000000000..284e96bbb --- /dev/null +++ b/src/World.h @@ -0,0 +1,753 @@ + +#pragma once + +#ifndef _WIN32 + #include "BlockID.h" +#else + enum ENUM_ITEM_ID; +#endif + +#define MAX_PLAYERS 65535 + +#include "Simulator/SimulatorManager.h" +#include "MersenneTwister.h" +#include "ChunkMap.h" +#include "WorldStorage/WorldStorage.h" +#include "Generating/ChunkGenerator.h" +#include "Vector3i.h" +#include "Vector3f.h" +#include "ChunkSender.h" +#include "Defines.h" +#include "LightingThread.h" +#include "Item.h" +#include "Mobs/Monster.h" +#include "Entities/ProjectileEntity.h" + + + + + +class cRedstone; +class cFireSimulator; +class cFluidSimulator; +class cSandSimulator; +class cRedstoneSimulator; +class cItem; +class cPlayer; +class cClientHandle; +class cEntity; +class cBlockEntity; +class cWorldGenerator; // The generator that actually generates the chunks for a single world +class cChunkGenerator; // The thread responsible for generating chunks +class cChestEntity; +class cDispenserEntity; +class cFurnaceEntity; +class cMobCensus; + +typedef std::list< cPlayer * > cPlayerList; + +typedef cItemCallback<cPlayer> cPlayerListCallback; +typedef cItemCallback<cEntity> cEntityCallback; +typedef cItemCallback<cChestEntity> cChestCallback; +typedef cItemCallback<cDispenserEntity> cDispenserCallback; +typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; + + + + + + +// tolua_begin +class cWorld +{ +public: + + // tolua_end + + /// A simple RAII locker for the chunkmap - locks the chunkmap in its constructor, unlocks it in the destructor + class cLock : + public cCSLock + { + typedef cCSLock super; + public: + cLock(cWorld & a_World); + } ; + + /// A common ancestor for all tasks queued onto the tick thread + class cTask + { + public: + virtual void Run(cWorld & a_World) = 0; + } ; + + typedef std::vector<cTask *> cTasks; + + class cTaskSaveAllChunks : + public cTask + { + protected: + // cTask overrides: + virtual void Run(cWorld & a_World) override; + } ; + + + static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates + { + return "cWorld"; + } + + // tolua_begin + + int GetTicksUntilWeatherChange(void) const { return m_WeatherInterval; } + Int64 GetWorldAge(void) const { return m_WorldAge; } + Int64 GetTimeOfDay(void) const { return m_TimeOfDay; } + + void SetTicksUntilWeatherChange(int a_WeatherInterval) + { + m_WeatherInterval = a_WeatherInterval; + } + + void SetTimeOfDay(Int64 a_TimeOfDay) + { + m_TimeOfDay = a_TimeOfDay; + m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0; + BroadcastTimeUpdate(); + } + + /// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable + eGameMode GetGameMode(void) const { return m_GameMode; } + + /// Returns true if the world is in Creative mode + bool IsGameModeCreative(void) const { return (m_GameMode == gmCreative); } + + /// Returns true if the world is in Survival mode + bool IsGameModeSurvival(void) const { return (m_GameMode == gmSurvival); } + + /// Returns true if the world is in Adventure mode + bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); } + + bool IsPVPEnabled(void) const { return m_bEnabledPVP; } + bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; } + + eDimension GetDimension(void) const { return m_Dimension; } + + /// Returns the world height at the specified coords; waits for the chunk to get loaded / generated + int GetHeight(int a_BlockX, int a_BlockZ); + + // tolua_end + + /// Retrieves the world height at the specified coords; returns false if chunk not loaded / generated + bool TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height); // Exported in ManualBindings.cpp + + // Broadcast respective packets to all clients of the chunk where the event is taking place + // (Please keep these alpha-sorted) + void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle); + void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude + void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export + void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityMetadata (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation (const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8 + void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export + void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL); + void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ); + void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL); + + /// If there is a block entity at the specified coords, sends it to the client specified + void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); + void MarkChunkSaving(int a_ChunkX, int a_ChunkZ); + void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); + + /** Sets the chunk data as either loaded from the storage or generated. + a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. + a_BiomeMap is optional, if not present, biomes will be calculated by the generator + a_HeightMap is optional, if not present, will be calculated. + If a_MarkDirty is set, the chunk is set as dirty (used after generating) + */ + void SetChunkData( + int a_ChunkX, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap * a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty + ); + + void ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback); + + /// Gets the chunk's blocks, only the block types + bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); + + bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; + bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const; + + void UnloadUnusedChunks(void); // tolua_export + + void CollectPickupsByPlayer(cPlayer * a_Player); + + void AddPlayer( cPlayer* a_Player ); + void RemovePlayer( cPlayer* a_Player ); + + /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored + bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action) + cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit); + + void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player + + /// Adds the entity into its appropriate chunk; takes ownership of the entity ptr + void AddEntity(cEntity * a_Entity); + + bool HasEntity(int a_UniqueID); + + /// Removes the entity, the entity ptr ownership is assumed taken by the caller + void RemoveEntity(cEntity * a_Entity); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. + bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); + + /// Adds client to a chunk, if not already present; returns true if added, false if present + bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from the chunk specified + void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from all chunks it is present in + void RemoveClientFromChunks(cClientHandle * a_Client); + + /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) + void SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from ChunkSender's queue of chunks to be sent + void RemoveClientFromChunkSender(cClientHandle * a_Client); + + /// Touches the chunk, causing it to be loaded or generated + void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() + void LoadChunks(const cChunkCoordsList & a_Chunks); + + /// Marks the chunk as failed-to-load: + void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as UpdateSign() + bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp + + /// Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as SetSignLines() + bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp + + /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! + void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); + + /// Regenerate the given chunk: + void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export + + /// Generates the given chunk, if not already generated + void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export + + /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted + void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL); + + bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); + + /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully + bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); + + // tolua_begin + + /** Sets the block at the specified coords to the specified value. + Full processing, incl. updating neighbors, is performed. + */ + void SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Sets the block at the specified coords to the specified value. + The replacement doesn't trigger block updates. + The replaced blocks aren't checked for block entities (block entity is leaked if it exists at this block) + */ + void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Queues a SetBlock() with the specified parameters after the specified number of ticks. + Calls SetBlock(), so performs full processing of the replaced block. + */ + void QueueSetBlock(int a_BlockX, int a_BLockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, int a_TickDelay); + + BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_MetaData); + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); + NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // TODO: Exported in ManualBindings.cpp + bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); // TODO: Exported in ManualBindings.cpp + // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_begin + + // Vector3i variants: + void FastSetBlock(const Vector3i & a_Pos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { FastSetBlock( a_Pos.x, a_Pos.y, a_Pos.z, a_BlockType, a_BlockMeta ); } + BLOCKTYPE GetBlock (const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); } + NIBBLETYPE GetBlockMeta(const Vector3i & a_Pos ) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); } + void SetBlockMeta(const Vector3i & a_Pos, NIBBLETYPE a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); } + // tolua_end + + /** Writes the block area into the specified coords. + Returns true if all chunks have been processed. + Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too. + a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together. + */ + bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + + // tolua_begin + + /// Spawns item pickups for each item in the list. May compress pickups if too many entities: + void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false); + + /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: + void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); + + /// Spawns an experience orb at the given location with the given reward. It returns the UniqueID of the spawned experience orb. + int SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward); + + /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided + void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1); + + // tolua_end + + /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType + void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); + + /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. + bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); + + // tolua_begin + bool DigBlock (int a_X, int a_Y, int a_Z); + void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); + + double GetSpawnX(void) const { return m_SpawnX; } + double GetSpawnY(void) const { return m_SpawnY; } + double GetSpawnZ(void) const { return m_SpawnZ; } + + /// Wakes up the simulators for the specified block + void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Wakes up the simulators for the specified area of blocks + void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); + + // tolua_end + + inline cSimulatorManager * GetSimulatorManager(void) { return m_SimulatorManager; } + + inline cFluidSimulator * GetWaterSimulator(void) { return m_WaterSimulator; } + inline cFluidSimulator * GetLavaSimulator (void) { return m_LavaSimulator; } + + /// Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true + bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); + + /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true + bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback); + + /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true + bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback); + + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp + + /** Does an explosion with the specified strength at the specified coordinate + a_SourceData exact type depends on the a_Source: + | esOther | void * | + | esPrimedTNT | cTNTEntity * | + | esCreeper | cCreeper * | + | esBed | cVector3i * | + | esEnderCrystal | Vector3i * | + | esGhastFireball | cGhastFireball * | + | esWitherSkullBlack | TBD | + | esWitherSkullBlue | TBD | + | esWitherBirth | TBD | + | esPlugin | void * | + */ + void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export + + /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found + bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found + bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found + bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp + + /// a_Player is using block entity at [x, y, z], handle that: + void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } // tolua_export + + /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback + bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback); + + void GrowTreeImage(const sSetBlockVector & a_Blocks); + + // tolua_begin + + /// Grows a tree at the specified coords, either from a sapling there, or based on the biome + void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Grows a tree at the specified coords, based on the sapling meta provided + void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta); + + /// Grows a tree at the specified coords, based on the biome in the place + void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini + bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false); + + /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + + /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config + void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value + int GetBiomeAt(int a_BlockX, int a_BlockZ); + + /// Returns the name of the world + const AString & GetName(void) const { return m_WorldName; } + + /// Returns the name of the world.ini file used by this world + const AString & GetIniFileName(void) const {return m_IniFileName; } + + // tolua_end + + inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) + { + // TODO: Use floor() instead of weird if statements + // Also fix Y + a_ChunkX = a_X/cChunkDef::Width; + if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; + a_ChunkY = 0; + a_ChunkZ = a_Z/cChunkDef::Width; + if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; + + a_X = a_X - a_ChunkX*cChunkDef::Width; + a_Y = a_Y - a_ChunkY*cChunkDef::Height; + a_Z = a_Z - a_ChunkZ*cChunkDef::Width; + } + + inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) + { + // TODO: Use floor() instead of weird if statements + // Also fix Y + (void)a_Y; // not unused anymore + a_ChunkX = a_X/cChunkDef::Width; + if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; + a_ChunkY = 0; + a_ChunkZ = a_Z/cChunkDef::Width; + if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; + } + + /// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead + void SaveAllChunks(void); + + /// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources + void QueueSaveAllChunks(void); // tolua_export + + /// Queues a task onto the tick thread. The task object will be deleted once the task is finished + void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp + + /// Returns the number of chunks loaded + int GetNumChunks() const; // tolua_export + + /// Returns the number of chunks loaded and dirty, and in the lighting queue + void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue); + + // Various queues length queries (cannot be const, they lock their CS): + inline int GetGeneratorQueueLength (void) { return m_Generator.GetQueueLength(); } // tolua_export + inline int GetLightingQueueLength (void) { return m_Lighting.GetQueueLength(); } // tolua_export + inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export + inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export + + void InitializeSpawn(void); + + /// Starts threads that belong to this world + void Start(void); + + /// Stops threads that belong to this world (part of deinit) + void Stop(void); + + /// Processes the blocks queued for ticking with a delay (m_BlockTickQueue[]) + void TickQueuedBlocks(void); + + struct BlockTickQueueItem + { + int X; + int Y; + int Z; + int TicksToWait; + }; + + /// Queues the block to be ticked after the specified number of game ticks + void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait); // tolua_export + + // tolua_begin + /// Casts a thunderbolt at the specified coords + void CastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Sets the specified weather; resets weather interval; asks and notifies plugins of the change + void SetWeather (eWeather a_NewWeather); + + /// Forces a weather change in the next game tick + void ChangeWeather (void); + + /// 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; } + cWorldStorage & GetStorage (void) { return m_Storage; } + cChunkMap * GetChunkMap (void) { return m_ChunkMap; } + + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + + int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export + int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export + + bool IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + + /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise + int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export + int SpawnMobFinalize(cMonster* a_Monster); + + /// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise + int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export + + /// Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! + int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } + + /// Appends all usernames starting with a_Text (case-insensitive) into Results + void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results); + + /// Get the current darkness level based on the time + NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; } + +private: + + friend class cRoot; + + class cTickThread : + public cIsThread + { + typedef cIsThread super; + public: + cTickThread(cWorld & a_World); + + protected: + cWorld & m_World; + + // cIsThread overrides: + virtual void Execute(void) override; + } ; + + + AString m_WorldName; + AString m_IniFileName; + + /// Name of the storage schema used to load and save chunks + AString m_StorageSchema; + + /// The dimension of the world, used by the client to provide correct lighting scheme + eDimension m_Dimension; + + /// This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) + MTRand m_TickRand; + + double m_SpawnX; + double m_SpawnY; + double m_SpawnZ; + + double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins. + double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day. + Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs + Int64 m_TimeOfDay; // Time in ticks, calculated off of m_TimeOfDaySecs + Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent. + Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred + Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred + std::map<cMonster::eFamily,Int64> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) + + NIBBLETYPE m_SkyDarkness; + + eGameMode m_GameMode; + bool m_bEnabledPVP; + bool m_IsDeepSnowEnabled; + + // The cRedstone class simulates redstone and needs access to m_RSList + // friend class cRedstone; + std::vector<int> m_RSList; + + std::vector<BlockTickQueueItem *> m_BlockTickQueue; + std::vector<BlockTickQueueItem *> m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue + + cSimulatorManager * m_SimulatorManager; + cSandSimulator * m_SandSimulator; + cFluidSimulator * m_WaterSimulator; + cFluidSimulator * m_LavaSimulator; + cFireSimulator * m_FireSimulator; + cRedstoneSimulator * m_RedstoneSimulator; + + cCriticalSection m_CSPlayers; + cPlayerList m_Players; + + cWorldStorage m_Storage; + + unsigned int m_MaxPlayers; + + cChunkMap * m_ChunkMap; + + bool m_bAnimals; + std::set<cMonster::eType> m_AllowedMobs; + + eWeather m_Weather; + int m_WeatherInterval; + + int m_MaxCactusHeight; + int m_MaxSugarcaneHeight; + bool m_IsCactusBonemealable; + bool m_IsCarrotsBonemealable; + bool m_IsCropsBonemealable; + bool m_IsGrassBonemealable; + bool m_IsMelonStemBonemealable; + bool m_IsMelonBonemealable; + bool m_IsPotatoesBonemealable; + bool m_IsPumpkinStemBonemealable; + bool m_IsPumpkinBonemealable; + bool m_IsSaplingBonemealable; + bool m_IsSugarcaneBonemealable; + + cCriticalSection m_CSFastSetBlock; + sSetBlockList m_FastSetBlockQueue; + + cChunkGenerator m_Generator; + + cChunkSender m_ChunkSender; + cLightingThread m_Lighting; + cTickThread m_TickThread; + + /// Guards the m_Tasks + cCriticalSection m_CSTasks; + + /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks + cTasks m_Tasks; + + /// Guards m_Clients + cCriticalSection m_CSClients; + + /// List of clients in this world, these will be ticked by this world + cClientHandleList m_Clients; + + /// Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them + cClientHandleList m_ClientsToRemove; + + /// Clients that are scheduled for adding, waiting for TickClients to add them + cClientHandleList m_ClientsToAdd; + + + cWorld(const AString & a_WorldName); + ~cWorld(); + + void Tick(float a_Dt); + + /// Handles the weather in each tick + void TickWeather(float a_Dt); + + /// Handles the mob spawning/moving/destroying each tick + void TickMobs(float a_Dt); + + /// Executes all tasks queued onto the tick thread + void TickQueuedTasks(void); + + /// Ticks all clients that are in this world + void TickClients(float a_Dt); + + void UpdateSkyDarkness(); + + /// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section) + cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock); +}; // tolua_export + + + + diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp new file mode 100644 index 000000000..e55011069 --- /dev/null +++ b/src/WorldStorage/FastNBT.cpp @@ -0,0 +1,547 @@ + +// FastNBT.cpp + +// Implements the fast NBT parser and writer + +#include "Globals.h" +#include "FastNBT.h" + + + + + +// The number of NBT tags that are reserved when an NBT parsing is started. +// You can override this by using a cmdline define +#ifndef NBT_RESERVE_SIZE + #define NBT_RESERVE_SIZE 200 +#endif // NBT_RESERVE_SIZE + +#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cParsedNBT: + +#define NEEDBYTES(N) \ + if (m_Length - m_Pos < N) \ + { \ + return false; \ + } + + + + + +cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) : + m_Data(a_Data), + m_Length(a_Length), + m_Pos(0) +{ + m_IsValid = Parse(); +} + + + + + +bool cParsedNBT::Parse(void) +{ + if (m_Length < 3) + { + // Data too short + return false; + } + if (m_Data[0] != TAG_Compound) + { + // The top-level tag must be a Compound + return false; + } + + m_Tags.reserve(NBT_RESERVE_SIZE); + + m_Tags.push_back(cFastNBTTag(TAG_Compound, -1)); + + m_Pos = 1; + + RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); + RETURN_FALSE_IF_FALSE(ReadCompound()); + + return true; +} + + + + + +bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen) +{ + NEEDBYTES(2); + a_StringStart = m_Pos + 2; + a_StringLen = ntohs(*((short *)(m_Data + m_Pos))); + if (a_StringLen < 0) + { + // Invalid string length + return false; + } + m_Pos += 2 + a_StringLen; + return true; +} + + + + + +bool cParsedNBT::ReadCompound(void) +{ + // Reads the latest tag as a compound + int ParentIdx = m_Tags.size() - 1; + int PrevSibling = -1; + while (true) + { + NEEDBYTES(1); + eTagType TagType = (eTagType)(m_Data[m_Pos]); + m_Pos++; + if (TagType == TAG_End) + { + break; + } + m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling)); + if (PrevSibling >= 0) + { + m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; + } + else + { + m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; + } + PrevSibling = m_Tags.size() - 1; + RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); + RETURN_FALSE_IF_FALSE(ReadTag()); + } // while (true) + m_Tags[ParentIdx].m_LastChild = PrevSibling; + return true; +} + + + + + +bool cParsedNBT::ReadList(eTagType a_ChildrenType) +{ + // Reads the latest tag as a list of items of type a_ChildrenType + + // Read the count: + NEEDBYTES(4); + int Count = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (Count < 0) + { + return false; + } + + // Read items: + int ParentIdx = m_Tags.size() - 1; + int PrevSibling = -1; + for (int i = 0; i < Count; i++) + { + m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling)); + if (PrevSibling >= 0) + { + m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; + } + else + { + m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; + } + PrevSibling = m_Tags.size() - 1; + RETURN_FALSE_IF_FALSE(ReadTag()); + } // for (i) + m_Tags[ParentIdx].m_LastChild = PrevSibling; + return true; +} + + + + + +#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \ + case TAG_##TAGTYPE: \ + { \ + NEEDBYTES(LEN); \ + Tag.m_DataStart = m_Pos; \ + Tag.m_DataLength = LEN; \ + m_Pos += LEN; \ + return true; \ + } + +bool cParsedNBT::ReadTag(void) +{ + cFastNBTTag & Tag = m_Tags.back(); + switch (Tag.m_Type) + { + CASE_SIMPLE_TAG(Byte, 1) + CASE_SIMPLE_TAG(Short, 2) + CASE_SIMPLE_TAG(Int, 4) + CASE_SIMPLE_TAG(Long, 8) + CASE_SIMPLE_TAG(Float, 4) + CASE_SIMPLE_TAG(Double, 8) + + case TAG_String: + { + return ReadString(Tag.m_DataStart, Tag.m_DataLength); + } + + case TAG_ByteArray: + { + NEEDBYTES(4); + int len = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (len < 0) + { + // Invalid length + return false; + } + NEEDBYTES(len); + Tag.m_DataLength = len; + Tag.m_DataStart = m_Pos; + m_Pos += len; + return true; + } + + case TAG_List: + { + NEEDBYTES(1); + eTagType ItemType = (eTagType)m_Data[m_Pos]; + m_Pos++; + RETURN_FALSE_IF_FALSE(ReadList(ItemType)); + return true; + } + + case TAG_Compound: + { + RETURN_FALSE_IF_FALSE(ReadCompound()); + return true; + } + + case TAG_IntArray: + { + NEEDBYTES(4); + int len = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (len < 0) + { + // Invalid length + return false; + } + len *= 4; + NEEDBYTES(len); + Tag.m_DataLength = len; + Tag.m_DataStart = m_Pos; + m_Pos += len; + return true; + } + + default: + { + ASSERT(!"Unhandled NBT tag type"); + return false; + } + } // switch (iType) +} + +#undef CASE_SIMPLE_TAG + + + + + +int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const +{ + if (a_Tag < 0) + { + return -1; + } + if (m_Tags[a_Tag].m_Type != TAG_Compound) + { + return -1; + } + + if (a_NameLength == 0) + { + a_NameLength = strlen(a_Name); + } + for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling) + { + if ( + (m_Tags[Child].m_NameLength == a_NameLength) && + (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0) + ) + { + return Child; + } + } // for Child - children of a_Tag + return -1; +} + + + + + +int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const +{ + if (a_Tag < 0) + { + return -1; + } + size_t Begin = 0; + size_t Length = a_Path.length(); + int Tag = a_Tag; + for (size_t i = 0; i < Length; i++) + { + if (a_Path[i] != '\\') + { + continue; + } + Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1); + if (Tag < 0) + { + return -1; + } + Begin = i + 1; + } // for i - a_Path[] + + if (Begin < Length) + { + Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin); + } + return Tag; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFastNBTWriter: + +cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) : + m_CurrentStack(0) +{ + m_Stack[0].m_Type = TAG_Compound; + m_Result.reserve(100 * 1024); + m_Result.push_back(TAG_Compound); + WriteString(a_RootTagName.data(), a_RootTagName.size()); +} + + + + + +void cFastNBTWriter::BeginCompound(const AString & a_Name) +{ + if (m_CurrentStack >= MAX_STACK) + { + ASSERT(!"Stack overflow"); + return; + } + + TagCommon(a_Name, TAG_Compound); + + ++m_CurrentStack; + m_Stack[m_CurrentStack].m_Type = TAG_Compound; +} + + + + + +void cFastNBTWriter::EndCompound(void) +{ + ASSERT(m_CurrentStack > 0); + ASSERT(IsStackTopCompound()); + + m_Result.push_back(TAG_End); + --m_CurrentStack; +} + + + + + +void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType) +{ + if (m_CurrentStack >= MAX_STACK) + { + ASSERT(!"Stack overflow"); + return; + } + + TagCommon(a_Name, TAG_List); + + m_Result.push_back((char)a_ChildrenType); + m_Result.append(4, (char)0); + + ++m_CurrentStack; + m_Stack[m_CurrentStack].m_Type = TAG_List; + m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4; + m_Stack[m_CurrentStack].m_Count = 0; + m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType; +} + + + + + +void cFastNBTWriter::EndList(void) +{ + ASSERT(m_CurrentStack > 0); + ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List); + + // Update the list count: + *((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count); + + --m_CurrentStack; +} + + + + + +void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) +{ + TagCommon(a_Name, TAG_Byte); + m_Result.push_back(a_Value); +} + + + + + +void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) +{ + TagCommon(a_Name, TAG_Short); + Int16 Value = htons(a_Value); + m_Result.append((const char *)&Value, 2); +} + + + + + +void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) +{ + TagCommon(a_Name, TAG_Int); + Int32 Value = htonl(a_Value); + m_Result.append((const char *)&Value, 4); +} + + + + + +void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) +{ + TagCommon(a_Name, TAG_Long); + Int64 Value = HostToNetwork8(&a_Value); + m_Result.append((const char *)&Value, 8); +} + + + + + +void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) +{ + TagCommon(a_Name, TAG_Float); + Int32 Value = HostToNetwork4(&a_Value); + m_Result.append((const char *)&Value, 4); +} + + + + + +void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) +{ + TagCommon(a_Name, TAG_Double); + Int64 Value = HostToNetwork8(&a_Value); + m_Result.append((const char *)&Value, 8); +} + + + + + +void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) +{ + TagCommon(a_Name, TAG_String); + Int16 len = htons((short)(a_Value.size())); + m_Result.append((const char *)&len, 2); + m_Result.append(a_Value.c_str(), a_Value.size()); +} + + + + + +void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements) +{ + TagCommon(a_Name, TAG_ByteArray); + Int32 len = htonl(a_NumElements); + m_Result.append((const char *)&len, 4); + m_Result.append(a_Value, a_NumElements); +} + + + + + +void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements) +{ + TagCommon(a_Name, TAG_IntArray); + Int32 len = htonl(a_NumElements); + m_Result.append((const char *)&len, 4); +#if defined(ANDROID_NDK) + // Android has alignment issues - cannot byteswap (htonl) an int that is not 32-bit-aligned, which happens in the regular version + for (size_t i = 0; i < a_NumElements; i++) + { + int Element = htonl(a_Value[i]); + m_Result.append((const char *)&Element, 4); + } +#else + int * Elements = (int *)(m_Result.data() + m_Result.size()); + m_Result.append(a_NumElements * 4, (char)0); + for (size_t i = 0; i < a_NumElements; i++) + { + Elements[i] = htonl(a_Value[i]); + } +#endif +} + + + + + +void cFastNBTWriter::Finish(void) +{ + ASSERT(m_CurrentStack == 0); + m_Result.push_back(TAG_End); +} + + + + + +void cFastNBTWriter::WriteString(const char * a_Data, short a_Length) +{ + Int16 Len = htons(a_Length); + m_Result.append((const char *)&Len, 2); + m_Result.append(a_Data, a_Length); +} + + + + diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h new file mode 100644 index 000000000..7323c29cb --- /dev/null +++ b/src/WorldStorage/FastNBT.h @@ -0,0 +1,293 @@ + +// FastNBT.h + +// Interfaces to the fast NBT parser and writer + +/* +The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree, +but themselves are allocated in a vector, thus minimizing reallocation. +The structures have a minimal constructor, setting all member "pointers" to "invalid". + +The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags +(just like XML); it keeps the internal tag stack and reports errors in usage. +It directly outputs a string containing the serialized NBT data. +*/ + + + + + +#pragma once + +#include "../Endianness.h" + + + + + +enum eTagType +{ + TAG_Min = 0, // The minimum value for a tag type + TAG_End = 0, + TAG_Byte = 1, + TAG_Short = 2, + TAG_Int = 3, + TAG_Long = 4, + TAG_Float = 5, + TAG_Double = 6, + TAG_ByteArray = 7, + TAG_String = 8, + TAG_List = 9, + TAG_Compound = 10, + TAG_IntArray = 11, + TAG_Max = 11, // The maximum value for a tag type +} ; + + + + + +/** This structure is used for all NBT tags. +It contains indices to the parent array of tags, building the NBT tree this way. +Also contains indices into the data stream being parsed, used for values; +NO dynamically allocated memory is used! +Structure (all with the tree structure it describes) supports moving in memory (std::vector reallocation) +*/ +struct cFastNBTTag +{ +public: + + eTagType m_Type; + + // The following members are indices into the data stream. m_DataLength == 0 if no data available + // They must not be pointers, because the datastream may be copied into another AString object in the meantime. + int m_NameStart; + int m_NameLength; + int m_DataStart; + int m_DataLength; + + // The following members are indices into the array returned; -1 if not valid + // They must not be pointers, because pointers would not survive std::vector reallocation + int m_Parent; + int m_PrevSibling; + int m_NextSibling; + int m_FirstChild; + int m_LastChild; + + cFastNBTTag(eTagType a_Type, int a_Parent) : + m_Type(a_Type), + m_NameLength(0), + m_DataLength(0), + m_Parent(a_Parent), + m_PrevSibling(-1), + m_NextSibling(-1), + m_FirstChild(-1), + m_LastChild(-1) + { + } + + cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) : + m_Type(a_Type), + m_NameLength(0), + m_DataLength(0), + m_Parent(a_Parent), + m_PrevSibling(a_PrevSibling), + m_NextSibling(-1), + m_FirstChild(-1), + m_LastChild(-1) + { + } +} ; + + + + + +/** Parses and contains the parsed data +Also implements data accessor functions for tree traversal and value getters +The data pointer passed in the constructor is assumed to be valid throughout the object's life. Care must be taken not to initialize from a temporary. +*/ +class cParsedNBT +{ +public: + cParsedNBT(const char * a_Data, int a_Length); + + bool IsValid(void) const {return m_IsValid; } + + int GetRoot(void) const {return 0; } + int GetFirstChild (int a_Tag) const { return m_Tags[a_Tag].m_FirstChild; } + int GetLastChild (int a_Tag) const { return m_Tags[a_Tag].m_LastChild; } + int GetNextSibling(int a_Tag) const { return m_Tags[a_Tag].m_NextSibling; } + int GetPrevSibling(int a_Tag) const { return m_Tags[a_Tag].m_PrevSibling; } + int GetDataLength (int a_Tag) const { return m_Tags[a_Tag].m_DataLength; } + + const char * GetData(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type != TAG_List); + ASSERT(m_Tags[a_Tag].m_Type != TAG_Compound); + return m_Data + m_Tags[a_Tag].m_DataStart; + } + + int FindChildByName(int a_Tag, const AString & a_Name) const + { + return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length()); + } + + int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const; + int FindTagByPath (int a_Tag, const AString & a_Path) const; + + eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; } + + /// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End + eTagType GetChildrenType(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_List); + return (m_Tags[a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[m_Tags[a_Tag].m_FirstChild].m_Type; + } + + inline unsigned char GetByte(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Byte); + return (unsigned char)(m_Data[m_Tags[a_Tag].m_DataStart]); + } + + inline Int16 GetShort(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Short); + return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + } + + inline Int32 GetInt(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Int); + return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + } + + inline Int64 GetLong(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Long); + return NetworkToHostLong8(m_Data + m_Tags[a_Tag].m_DataStart); + } + + inline float GetFloat(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Float); + Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + return *((float *)&tmp); + } + + inline double GetDouble(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Double); + return NetworkToHostDouble8(m_Data + m_Tags[a_Tag].m_DataStart); + } + + inline AString GetString(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_String); + AString res; + res.assign(m_Data + m_Tags[a_Tag].m_DataStart, m_Tags[a_Tag].m_DataLength); + return res; + } + + inline AString GetName(int a_Tag) const + { + AString res; + res.assign(m_Data + m_Tags[a_Tag].m_NameStart, m_Tags[a_Tag].m_NameLength); + return res; + } + +protected: + const char * m_Data; + int m_Length; + std::vector<cFastNBTTag> m_Tags; + bool m_IsValid; // True if parsing succeeded + + // Used while parsing: + int m_Pos; + + bool Parse(void); + bool ReadString(int & a_StringStart, int & a_StringLen); // Reads a simple string (2 bytes length + data), sets the string descriptors + bool ReadCompound(void); // Reads the latest tag as a compound + bool ReadList(eTagType a_ChildrenType); // Reads the latest tag as a list of items of type a_ChildrenType + bool ReadTag(void); // Reads the latest tag, depending on its m_Type setting +} ; + + + + + +class cFastNBTWriter +{ +public: + cFastNBTWriter(const AString & a_RootTagName = ""); + + void BeginCompound(const AString & a_Name); + void EndCompound(void); + + void BeginList(const AString & a_Name, eTagType a_ChildrenType); + void EndList(void); + + void AddByte (const AString & a_Name, unsigned char a_Value); + void AddShort (const AString & a_Name, Int16 a_Value); + void AddInt (const AString & a_Name, Int32 a_Value); + void AddLong (const AString & a_Name, Int64 a_Value); + void AddFloat (const AString & a_Name, float a_Value); + void AddDouble (const AString & a_Name, double a_Value); + void AddString (const AString & a_Name, const AString & a_Value); + void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements); + void AddIntArray (const AString & a_Name, const int * a_Value, size_t a_NumElements); + + void AddByteArray(const AString & a_Name, const AString & a_Value) + { + AddByteArray(a_Name, a_Value.data(), a_Value.size()); + } + + const AString & GetResult(void) const {return m_Result; } + + void Finish(void); + +protected: + + struct sParent + { + int m_Type; // TAG_Compound or TAG_List + int m_Pos; // for TAG_List, the position of the list count + int m_Count; // for TAG_List, the element count + eTagType m_ItemType; // for TAG_List, the element type + } ; + + static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep + + // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed. + sParent m_Stack[MAX_STACK]; + int m_CurrentStack; + + AString m_Result; + + bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); } + + void WriteString(const char * a_Data, short a_Length); + + inline void TagCommon(const AString & a_Name, eTagType a_Type) + { + // If we're directly inside a list, check that the list is of the correct type: + ASSERT((m_Stack[m_CurrentStack].m_Type != TAG_List) || (m_Stack[m_CurrentStack].m_ItemType == a_Type)); + + if (IsStackTopCompound()) + { + // Compound: add the type and name: + m_Result.push_back((char)a_Type); + WriteString(a_Name.c_str(), (short)a_Name.length()); + } + else + { + // List: add to the counter + m_Stack[m_CurrentStack].m_Count++; + } + } +} ; + + + + diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp new file mode 100644 index 000000000..c9013b1b3 --- /dev/null +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -0,0 +1,533 @@ + +// NBTChunkSerializer.cpp + + +#include "Globals.h" +#include "NBTChunkSerializer.h" +#include "../BlockID.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/DropperEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" +#include "../Entities/Entity.h" +#include "FastNBT.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Boat.h" +#include "../Entities/Minecart.h" +#include "../Mobs/Monster.h" +#include "../Entities/Pickup.h" +#include "../Entities/ProjectileEntity.h" + + + + + +cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) : + m_BiomesAreValid(false), + m_Writer(a_Writer), + m_IsTagOpen(false), + m_HasHadEntity(false), + m_HasHadBlockEntity(false), + m_IsLightValid(false) +{ +} + + + + + +void cNBTChunkSerializer::Finish(void) +{ + if (m_IsTagOpen) + { + m_Writer.EndList(); + } + + // If light not valid, reset it to all zeroes: + if (!m_IsLightValid) + { + memset(m_BlockLight, 0, sizeof(m_BlockLight)); + memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight)); + } +} + + + + + +void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName) +{ + m_Writer.BeginCompound(a_CompoundName); + m_Writer.AddShort("id", (short)(a_Item.m_ItemType)); + m_Writer.AddShort("Damage", a_Item.m_ItemDamage); + m_Writer.AddByte ("Count", a_Item.m_ItemCount); + if (a_Slot >= 0) + { + m_Writer.AddByte ("Slot", (unsigned char)a_Slot); + } + + // Write the enchantments: + if (!a_Item.m_Enchantments.IsEmpty()) + { + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + m_Writer.BeginCompound("tag"); + a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName); + m_Writer.EndCompound(); + } + + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum) +{ + int NumSlots = a_Grid.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + const cItem & Item = a_Grid.GetSlot(i); + if (Item.IsEmpty()) + { + continue; + } + AddItem(Item, i + a_BeginSlotNum); + } // for i - chest slots[] +} + + + + + +void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID) +{ + m_Writer.AddInt ("x", a_Entity->GetPosX()); + m_Writer.AddInt ("y", a_Entity->GetPosY()); + m_Writer.AddInt ("z", a_Entity->GetPosZ()); + m_Writer.AddString("id", a_EntityTypeID); +} + + + + + +void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Chest"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Trap"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Dropper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Furnace, "Furnace"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Furnace->GetContents()); + m_Writer.EndList(); + m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft()); + m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Hopper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Jukebox, "RecordPlayer"); + m_Writer.AddInt("Record", a_Jukebox->GetRecord()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Note, "Music"); + m_Writer.AddByte("note", a_Note->GetPitch()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Sign, "Sign"); + m_Writer.AddString("Text1", a_Sign->GetLine(0)); + m_Writer.AddString("Text2", a_Sign->GetLine(1)); + m_Writer.AddString("Text3", a_Sign->GetLine(2)); + m_Writer.AddString("Text4", a_Sign->GetLine(3)); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName) +{ + m_Writer.AddString("id", a_ClassName); + m_Writer.BeginList("Pos", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetPosX()); + m_Writer.AddDouble("", a_Entity->GetPosY()); + m_Writer.AddDouble("", a_Entity->GetPosZ()); + m_Writer.EndList(); + m_Writer.BeginList("Motion", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetSpeedX()); + m_Writer.AddDouble("", a_Entity->GetSpeedY()); + m_Writer.AddDouble("", a_Entity->GetSpeedZ()); + m_Writer.EndList(); + m_Writer.BeginList("Rotation", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetRotation()); + m_Writer.AddDouble("", a_Entity->GetPitch()); + m_Writer.EndList(); +} + + + + + +void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Boat, "Boat"); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_FallingBlock, "FallingSand"); + m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType()); + m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta()); + m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero + m_Writer.AddByte("DropItem", 1); + m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) +{ + const char * EntityClass = NULL; + switch (a_Minecart->GetPayload()) + { + case cMinecart::mpNone: EntityClass = "MinecartRideable"; break; + case cMinecart::mpChest: EntityClass = "MinecartChest"; break; + case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break; + case cMinecart::mpTNT: EntityClass = "MinecartTNT"; break; + case cMinecart::mpHopper: EntityClass = "MinecartHopper"; break; + default: + { + ASSERT(!"Unhandled minecart payload type"); + return; + } + } // switch (payload) + + m_Writer.BeginCompound(""); + AddBasicEntity(a_Minecart, EntityClass); + switch (a_Minecart->GetPayload()) + { + case cMinecart::mpChest: + { + // Add chest contents into the Items tag: + AddMinecartChestContents((cMinecartWithChest *)a_Minecart); + break; + } + + case cMinecart::mpFurnace: + { + // TODO: Add "Push" and "Fuel" tags + break; + } + } // switch (Payload) + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) +{ + // TODO +} + + + + + +void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Pickup, "Item"); + AddItem(a_Pickup->GetItem(), -1, "Item"); + m_Writer.AddShort("Health", a_Pickup->GetHealth()); + m_Writer.AddShort("Age", a_Pickup->GetAge()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName()); + Vector3d Pos = a_Projectile->GetPosition(); + m_Writer.AddShort("xTile", (Int16)floor(Pos.x)); + m_Writer.AddShort("yTile", (Int16)floor(Pos.y)); + m_Writer.AddShort("zTile", (Int16)floor(Pos.z)); + m_Writer.AddShort("inTile", 0); // TODO: Query the block type + m_Writer.AddShort("shake", 0); // TODO: Any shake? + m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0); + + switch (a_Projectile->GetProjectileKind()) + { + case cProjectileEntity::pkArrow: + { + m_Writer.AddByte("inData", 0); // TODO: Query the block meta (is it needed?) + m_Writer.AddByte("pickup", ((cArrowEntity *)a_Projectile)->GetPickupState()); + m_Writer.AddDouble("damage", ((cArrowEntity *)a_Projectile)->GetDamageCoeff()); + break; + } + case cProjectileEntity::pkGhastFireball: + { + m_Writer.AddInt("ExplosionPower", 1); + // fall-through: + } + case cProjectileEntity::pkFireCharge: + case cProjectileEntity::pkWitherSkull: + case cProjectileEntity::pkEnderPearl: + { + m_Writer.BeginList("Motion", TAG_Double); + m_Writer.AddDouble("", a_Projectile->GetSpeedX()); + m_Writer.AddDouble("", a_Projectile->GetSpeedY()); + m_Writer.AddDouble("", a_Projectile->GetSpeedZ()); + m_Writer.EndList(); + break; + } + default: + { + ASSERT(!"Unsaved projectile entity!"); + } + } // switch (ProjectileKind) + cEntity * Creator = a_Projectile->GetCreator(); + if (Creator != NULL) + { + if (Creator->GetEntityType() == cEntity::etPlayer) + { + m_Writer.AddString("ownerName", ((cPlayer *)Creator)->GetName()); + } + } + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) +{ + m_Writer.BeginList("Items", TAG_Compound); + for (int i = 0; i < cMinecartWithChest::NumSlots; i++) + { + const cItem & Item = a_Minecart->GetSlot(i); + if (Item.IsEmpty()) + { + continue; + } + AddItem(Item, i); + } + m_Writer.EndList(); +} + + + + + +bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid) +{ + m_IsLightValid = a_IsLightValid; + return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother +} + + + + + +void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +{ + memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes)); + for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++) + { + if ((*a_BiomeMap)[i] < 255) + { + // Normal MC biome, copy as-is: + m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]); + } + else + { + // TODO: MCS-specific biome, need to map to some basic MC biome: + ASSERT(!"Unimplemented MCS-specific biome"); + return; + } + } // for i - m_BiomeMap[] + m_BiomesAreValid = true; +} + + + + + +void cNBTChunkSerializer::Entity(cEntity * a_Entity) +{ + // Add entity into NBT: + if (m_IsTagOpen) + { + if (!m_HasHadEntity) + { + m_Writer.EndList(); + m_Writer.BeginList("Entities", TAG_Compound); + } + } + else + { + m_Writer.BeginList("Entities", TAG_Compound); + } + m_IsTagOpen = true; + m_HasHadEntity = true; + + switch (a_Entity->GetEntityType()) + { + case cEntity::etBoat: AddBoatEntity ((cBoat *) a_Entity); break; + case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break; + case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break; + case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; + case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; + case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break; + case cEntity::etPlayer: return; // Players aren't saved into the world + default: + { + ASSERT(!"Unhandled entity type is being saved"); + break; + } + } +} + + + + + +void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) +{ + if (m_IsTagOpen) + { + if (!m_HasHadBlockEntity) + { + m_Writer.EndList(); + m_Writer.BeginList("TileEntities", TAG_Compound); + } + } + else + { + m_Writer.BeginList("TileEntities", TAG_Compound); + } + m_IsTagOpen = true; + + // Add tile-entity into NBT: + switch (a_Entity->GetBlockType()) + { + case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; + case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; + case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; + case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; + case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; + case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; + case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; + default: + { + ASSERT(!"Unhandled block entity saved into Anvil"); + } + } + m_HasHadBlockEntity = true; +} + + + + diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h new file mode 100644 index 000000000..9d4ac208c --- /dev/null +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -0,0 +1,116 @@ + +// NBTChunkSerializer.h + +// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil + + + + + +#pragma once + +#include "../ChunkDef.h" + + + + + +// fwd: +class cFastNBTWriter; +class cEntity; +class cBlockEntity; +class cBoat; +class cChestEntity; +class cDispenserEntity; +class cDropperEntity; +class cFurnaceEntity; +class cHopperEntity; +class cJukeboxEntity; +class cNoteEntity; +class cSignEntity; +class cFallingBlock; +class cMinecart; +class cMinecartWithChest; +class cMinecartWithFurnace; +class cMinecartWithTNT; +class cMinecartWithHopper; +class cMonster; +class cPickup; +class cItemGrid; +class cProjectileEntity; + + + + + +class cNBTChunkSerializer : + public cChunkDataSeparateCollector +{ +public: + cChunkDef::BiomeMap m_Biomes; + unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width]; + bool m_BiomesAreValid; + + + cNBTChunkSerializer(cFastNBTWriter & a_Writer); + + /// Close NBT tags that we've opened + void Finish(void); + + bool IsLightValid(void) const {return m_IsLightValid; } + +protected: + + /* From cChunkDataSeparateCollector we inherit: + - m_BlockTypes[] + - m_BlockMetas[] + - m_BlockLight[] + - m_BlockSkyLight[] + */ + + cFastNBTWriter & m_Writer; + + bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed. + bool m_HasHadEntity; // True if any Entity has already been received and processed + bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed + bool m_IsLightValid; // True if the chunk lighting is valid + + + /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. + void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = ""); + + /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag + void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0); + + // Block entities: + void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); + void AddChestEntity (cChestEntity * a_Entity); + void AddDispenserEntity(cDispenserEntity * a_Entity); + void AddDropperEntity (cDropperEntity * a_Entity); + void AddFurnaceEntity (cFurnaceEntity * a_Furnace); + void AddHopperEntity (cHopperEntity * a_Entity); + void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); + void AddNoteEntity (cNoteEntity * a_Note); + void AddSignEntity (cSignEntity * a_Sign); + + // Entities: + void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); + void AddBoatEntity (cBoat * a_Boat); + void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); + void AddMinecartEntity (cMinecart * a_Minecart); + void AddMonsterEntity (cMonster * a_Monster); + void AddPickupEntity (cPickup * a_Pickup); + void AddProjectileEntity (cProjectileEntity * a_Projectile); + + void AddMinecartChestContents(cMinecartWithChest * a_Minecart); + + // cChunkDataSeparateCollector overrides: + virtual bool LightIsValid(bool a_IsLightValid) override; + virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override; + virtual void Entity(cEntity * a_Entity) override; + virtual void BlockEntity(cBlockEntity * a_Entity) override; +} ; // class cNBTChunkSerializer + + + + diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp new file mode 100644 index 000000000..48163e222 --- /dev/null +++ b/src/WorldStorage/WSSAnvil.cpp @@ -0,0 +1,1555 @@ + +// WSSAnvil.cpp + +// Implements the cWSSAnvil class representing the Anvil world storage scheme + +#include "Globals.h" +#include "WSSAnvil.h" +#include "NBTChunkSerializer.h" +#include "../World.h" +#include "lib/zlib/zlib.h" +#include "../BlockID.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/DropperEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" +#include "../Item.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" +#include "FastNBT.h" +#include "../Mobs/Monster.h" +#include "../Entities/Boat.h" +#include "../Entities/FallingBlock.h" +#include "../Entities/Minecart.h" +#include "../Entities/Pickup.h" +#include "../Entities/ProjectileEntity.h" + + + + + +/** If defined, the BlockSkyLight values will be copied over to BlockLight upon chunk saving, +thus making skylight visible in Minutor's Lighting mode +*/ +// #define DEBUG_SKYLIGHT + +/** Maximum number of MCA files that are cached in memory. +Since only the header is actually in the memory, this number can be high, but still, each file means an OS FS handle. +*/ +#define MAX_MCA_FILES 32 + +/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities +#define CHUNK_INFLATE_MAX 256 KiB + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSAnvil: + +cWSSAnvil::cWSSAnvil(cWorld * a_World) : + super(a_World) +{ + // Create a level.dat file for mapping tools, if it doesn't already exist: + AString fnam; + Printf(fnam, "%s/level.dat", a_World->GetName().c_str()); + if (!cFile::Exists(fnam)) + { + cFastNBTWriter Writer; + Writer.BeginCompound(""); + Writer.AddInt("SpawnX", (int)(a_World->GetSpawnX())); + Writer.AddInt("SpawnY", (int)(a_World->GetSpawnY())); + Writer.AddInt("SpawnZ", (int)(a_World->GetSpawnZ())); + Writer.EndCompound(); + Writer.Finish(); + + #ifdef _DEBUG + cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size()); + ASSERT(TestParse.IsValid()); + #endif // _DEBUG + + gzFile gz = gzopen((FILE_IO_PREFIX + fnam).c_str(), "wb"); + if (gz != NULL) + { + gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size()); + } + gzclose(gz); + } +} + + + + + +cWSSAnvil::~cWSSAnvil() +{ + cCSLock Lock(m_CS); + for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr) + { + delete *itr; + } // for itr - m_Files[] +} + + + + + +bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) +{ + AString ChunkData; + if (!GetChunkData(a_Chunk, ChunkData)) + { + // The reason for failure is already printed in GetChunkData() + return false; + } + + return LoadChunkFromData(a_Chunk, ChunkData); +} + + + + + +bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) +{ + AString ChunkData; + if (!SaveChunkToData(a_Chunk, ChunkData)) + { + LOGWARNING("Cannot serialize chunk [%d, %d] into data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + if (!SetChunkData(a_Chunk, ChunkData)) + { + LOGWARNING("Cannot store chunk [%d, %d] data", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + + // Everything successful + return true; +} + + + + + +bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + cCSLock Lock(m_CS); + cMCAFile * File = LoadMCAFile(a_Chunk); + if (File == NULL) + { + return false; + } + return File->GetChunkData(a_Chunk, a_Data); +} + + + + + +bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + cCSLock Lock(m_CS); + cMCAFile * File = LoadMCAFile(a_Chunk); + if (File == NULL) + { + return false; + } + return File->SetChunkData(a_Chunk, a_Data); +} + + + + + +cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) +{ + // ASSUME m_CS is locked + ASSERT(m_CS.IsLocked()); + + const int RegionX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32); + const int RegionZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32); + ASSERT(a_Chunk.m_ChunkX - RegionX * 32 >= 0); + ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 >= 0); + ASSERT(a_Chunk.m_ChunkX - RegionX * 32 < 32); + ASSERT(a_Chunk.m_ChunkZ - RegionZ * 32 < 32); + + // Is it already cached? + for (cMCAFiles::iterator itr = m_Files.begin(); itr != m_Files.end(); ++itr) + { + if (((*itr) != NULL) && ((*itr)->GetRegionX() == RegionX) && ((*itr)->GetRegionZ() == RegionZ)) + { + // Move the file to front and return it: + cMCAFile * f = *itr; + if (itr != m_Files.begin()) + { + m_Files.erase(itr); + m_Files.push_front(f); + } + return f; + } + } + + // Load it anew: + AString FileName; + Printf(FileName, "%s/region", m_World->GetName().c_str()); + cFile::CreateFolder(FILE_IO_PREFIX + FileName); + AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); + cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); + if (f == NULL) + { + return NULL; + } + m_Files.push_front(f); + + // If there are too many MCA files cached, delete the last one used: + if (m_Files.size() > MAX_MCA_FILES) + { + delete m_Files.back(); + m_Files.pop_back(); + } + return f; +} + + + + + +bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + // Decompress the data: + char Uncompressed[CHUNK_INFLATE_MAX]; + z_stream strm; + strm.zalloc = (alloc_func)NULL; + strm.zfree = (free_func)NULL; + strm.opaque = NULL; + inflateInit(&strm); + strm.next_out = (Bytef *)Uncompressed; + strm.avail_out = sizeof(Uncompressed); + strm.next_in = (Bytef *)a_Data.data(); + strm.avail_in = a_Data.size(); + int res = inflate(&strm, Z_FINISH); + inflateEnd(&strm); + if (res != Z_STREAM_END) + { + return false; + } + + // Parse the NBT data: + cParsedNBT NBT(Uncompressed, strm.total_out); + if (!NBT.IsValid()) + { + // NBT Parsing failed + return false; + } + + // Load the data from NBT: + return LoadChunkFromNBT(a_Chunk, NBT); +} + + + + + +bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + cFastNBTWriter Writer; + if (!SaveChunkToNBT(a_Chunk, Writer)) + { + LOGWARNING("Cannot save chunk [%d, %d] to NBT", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + Writer.Finish(); + + CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data); + return true; +} + + + + + +bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT) +{ + // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data) + cChunkDef::BlockTypes BlockTypes; + cChunkDef::BlockNibbles MetaData; + cChunkDef::BlockNibbles BlockLight; + cChunkDef::BlockNibbles SkyLight; + + memset(BlockTypes, E_BLOCK_AIR, sizeof(BlockTypes)); + memset(MetaData, 0, sizeof(MetaData)); + memset(SkyLight, 0xff, sizeof(SkyLight)); // By default, data not present in the NBT means air, which means full skylight + memset(BlockLight, 0x00, sizeof(BlockLight)); + + // Load the blockdata, blocklight and skylight: + int Level = a_NBT.FindChildByName(0, "Level"); + if (Level < 0) + { + return false; + } + int Sections = a_NBT.FindChildByName(Level, "Sections"); + if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) + { + return false; + } + for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) + { + int y = 0; + int SectionY = a_NBT.FindChildByName(Child, "Y"); + if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) + { + continue; + } + y = a_NBT.GetByte(SectionY); + if ((y < 0) || (y > 15)) + { + continue; + } + CopyNBTData(a_NBT, Child, "Blocks", (char *)&(BlockTypes[y * 4096]), 4096); + CopyNBTData(a_NBT, Child, "Data", (char *)&(MetaData[y * 2048]), 2048); + CopyNBTData(a_NBT, Child, "SkyLight", (char *)&(SkyLight[y * 2048]), 2048); + CopyNBTData(a_NBT, Child, "BlockLight", (char *)&(BlockLight[y * 2048]), 2048); + } // for itr - LevelSections[] + + // Load the biomes from NBT, if present and valid. First try MCS-style, then Vanilla-style: + cChunkDef::BiomeMap BiomeMap; + cChunkDef::BiomeMap * Biomes = LoadBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "MCSBiomes")); + if (Biomes == NULL) + { + // MCS-style biomes not available, load vanilla-style: + Biomes = LoadVanillaBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes")); + } + + // Load the entities from NBT: + cEntityList Entities; + cBlockEntityList BlockEntities; + LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities")); + LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"), BlockTypes, MetaData); + + bool IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0); + + /* + // Uncomment this block for really cool stuff :) + // DEBUG magic: Invert the underground, so that we can see the MC generator in action :) + bool ShouldInvert[cChunkDef::Width * cChunkDef::Width]; + memset(ShouldInvert, 0, sizeof(ShouldInvert)); + for (int y = cChunkDef::Height - 1; y >= 0; y--) + { + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + int Index = cChunkDef::MakeIndexNoCheck(x, y, z); + if (ShouldInvert[x + cChunkDef::Width * z]) + { + BlockTypes[Index] = (BlockTypes[Index] == E_BLOCK_AIR) ? E_BLOCK_STONE : E_BLOCK_AIR; + } + else + { + switch (BlockTypes[Index]) + { + case E_BLOCK_AIR: + case E_BLOCK_LEAVES: + { + // nothing needed + break; + } + default: + { + ShouldInvert[x + cChunkDef::Width * z] = true; + } + } + BlockTypes[Index] = E_BLOCK_AIR; + } + } + } // for y + //*/ + + m_World->SetChunkData( + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, + BlockTypes, MetaData, + IsLightValid ? BlockLight : NULL, + IsLightValid ? SkyLight : NULL, + NULL, Biomes, + Entities, BlockEntities, + false + ); + return true; +} + + + + +void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length) +{ + int Child = a_NBT.FindChildByName(a_Tag, a_ChildName); + if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length)) + { + memcpy(a_Destination, a_NBT.GetData(Child), a_Length); + } +} + + + + + +bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer) +{ + a_Writer.BeginCompound("Level"); + a_Writer.AddInt("xPos", a_Chunk.m_ChunkX); + a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ); + cNBTChunkSerializer Serializer(a_Writer); + if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) + { + LOGWARNING("Cannot get chunk [%d, %d] data for NBT saving", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + Serializer.Finish(); // Close NBT tags + + // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray): + if (Serializer.m_BiomesAreValid) + { + a_Writer.AddByteArray("Biomes", (const char *)(Serializer.m_VanillaBiomes), ARRAYCOUNT(Serializer.m_VanillaBiomes)); + a_Writer.AddIntArray ("MCSBiomes", (const int *)(Serializer.m_Biomes), ARRAYCOUNT(Serializer.m_Biomes)); + } + + // Save blockdata: + a_Writer.BeginList("Sections", TAG_Compound); + int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16; + int SliceSizeNibble = SliceSizeBlock / 2; + const char * BlockTypes = (const char *)(Serializer.m_BlockTypes); + const char * BlockMetas = (const char *)(Serializer.m_BlockMetas); + #ifdef DEBUG_SKYLIGHT + const char * BlockLight = (const char *)(Serializer.m_BlockSkyLight); + #else + const char * BlockLight = (const char *)(Serializer.m_BlockLight); + #endif + const char * BlockSkyLight = (const char *)(Serializer.m_BlockSkyLight); + for (int Y = 0; Y < 16; Y++) + { + a_Writer.BeginCompound(""); + a_Writer.AddByteArray("Blocks", BlockTypes + Y * SliceSizeBlock, SliceSizeBlock); + a_Writer.AddByteArray("Data", BlockMetas + Y * SliceSizeNibble, SliceSizeNibble); + a_Writer.AddByteArray("SkyLight", BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble); + a_Writer.AddByteArray("BlockLight", BlockLight + Y * SliceSizeNibble, SliceSizeNibble); + a_Writer.AddByte("Y", (unsigned char)Y); + a_Writer.EndCompound(); + } + a_Writer.EndList(); // "Sections" + + // Store the information that the lighting is valid. + // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading. + if (Serializer.IsLightValid()) + { + a_Writer.AddByte("MCSIsLightValid", 1); + } + + a_Writer.EndCompound(); // "Level" + return true; +} + + + + + +cChunkDef::BiomeMap * cWSSAnvil::LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray)) + { + return NULL; + } + if (a_NBT.GetDataLength(a_TagIdx) != 16 * 16) + { + // The biomes stored don't match in size + return NULL; + } + const unsigned char * VanillaBiomeData = (const unsigned char *)(a_NBT.GetData(a_TagIdx)); + for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) + { + if ((VanillaBiomeData)[i] == 0xff) + { + // Unassigned biomes + return NULL; + } + (*a_BiomeMap)[i] = (EMCSBiome)(VanillaBiomeData[i]); + } + return a_BiomeMap; +} + + + + + +cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_IntArray)) + { + return NULL; + } + if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap)) + { + // The biomes stored don't match in size + return NULL; + } + const int * BiomeData = (const int *)(a_NBT.GetData(a_TagIdx)); + for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++) + { + (*a_BiomeMap)[i] = (EMCSBiome)(ntohl(BiomeData[i])); + if ((*a_BiomeMap)[i] == 0xff) + { + // Unassigned biomes + return NULL; + } + } + return a_BiomeMap; +} + + + + + +void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) + { + return; + } + + for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + if (a_NBT.GetType(Child) != TAG_Compound) + { + continue; + } + int sID = a_NBT.FindChildByName(Child, "id"); + if (sID < 0) + { + continue; + } + LoadEntityFromNBT(a_Entities, a_NBT, Child, a_NBT.GetData(sID), a_NBT.GetDataLength(sID)); + } // for Child - a_NBT[] +} + + + + + +void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) + { + return; + } + + for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + if (a_NBT.GetType(Child) != TAG_Compound) + { + continue; + } + int sID = a_NBT.FindChildByName(Child, "id"); + if (sID < 0) + { + continue; + } + if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0) + { + LoadChestFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0) + { + LoadDropperFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0) + { + LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas); + } + else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0) + { + LoadHopperFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0) + { + LoadNoteFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0) + { + LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0) + { + LoadSignFromNBT(a_BlockEntities, a_NBT, Child); + } + else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0) + { + LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child); + } + // TODO: Other block entities + } // for Child - tag children +} + + + + + +bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int ID = a_NBT.FindChildByName(a_TagIdx, "id"); + if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short)) + { + return false; + } + a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID)); + + int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage"); + if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short)) + { + return false; + } + a_Item.m_ItemDamage = a_NBT.GetShort(Damage); + + int Count = a_NBT.FindChildByName(a_TagIdx, "Count"); + if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte)) + { + return false; + } + a_Item.m_ItemCount = a_NBT.GetByte(Count); + + // Find the "tag" tag, used for enchantments and other extra data + int TagTag = a_NBT.FindChildByName(a_TagIdx, "tag"); + if (TagTag <= 0) + { + // No extra data + return true; + } + + // Load enchantments: + const char * EnchName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + int EnchTag = a_NBT.FindChildByName(TagTag, EnchName); + if (EnchTag > 0) + { + a_Item.m_Enchantments.ParseFromNBT(a_NBT, EnchTag); + } + + return true; +} + + + + + +void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int a_SlotOffset) +{ + int NumSlots = a_ItemGrid.GetNumSlots(); + for (int Child = a_NBT.GetFirstChild(a_ItemsTagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int SlotTag = a_NBT.FindChildByName(Child, "Slot"); + if ((SlotTag < 0) || (a_NBT.GetType(SlotTag) != TAG_Byte)) + { + continue; + } + int SlotNum = (int)(a_NBT.GetByte(SlotTag)) - a_SlotOffset; + if ((SlotNum < 0) || (SlotNum >= NumSlots)) + { + // SlotNum outside of the range + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + a_ItemGrid.SetSlot(SlotNum, Item); + } + } // for itr - ItemDefs[] +} + + + + + +void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this + } + std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Chest.release()); +} + + + + + +void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this + } + std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Dispenser.release()); +} + + + + + +void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this + } + std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Dropper.release()); +} + + + + + +void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this + } + + // Convert coords to relative: + int RelX = x; + int RelZ = z; + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ); + + // Create the furnace entity, with proper BlockType and BlockMeta info: + BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ); + NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ); + std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World)); + + // Load slots: + for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int Slot = a_NBT.FindChildByName(Child, "Slot"); + if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) + { + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + Furnace->SetSlot(a_NBT.GetByte(Slot), Item); + } + } // for itr - ItemDefs[] + + // Load burn time: + int BurnTime = a_NBT.FindChildByName(a_TagIdx, "BurnTime"); + if (BurnTime >= 0) + { + Int16 bt = a_NBT.GetShort(BurnTime); + // Anvil doesn't store the time that the fuel can burn. We simply "reset" the current value to be the 100% + Furnace->SetBurnTimes(bt, 0); + } + + // Load cook time: + int CookTime = a_NBT.FindChildByName(a_TagIdx, "CookTime"); + if (CookTime >= 0) + { + Int16 ct = a_NBT.GetShort(CookTime); + // Anvil doesn't store the time that an item takes to cook. We simply use the default - 10 seconds (200 ticks) + Furnace->SetCookTimes(200, ct); + } + + // Restart cooking: + Furnace->ContinueCooking(); + a_BlockEntities.push_back(Furnace.release()); +} + + + + + +void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this + } + std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World)); + LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); + a_BlockEntities.push_back(Hopper.release()); +} + + + + + +void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World)); + int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); + if (Record >= 0) + { + Jukebox->SetRecord(a_NBT.GetInt(Record)); + } + a_BlockEntities.push_back(Jukebox.release()); +} + + + + + +void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr<cNoteEntity> Note(new cNoteEntity(x, y, z, m_World)); + int note = a_NBT.FindChildByName(a_TagIdx, "note"); + if (note >= 0) + { + Note->SetPitch(a_NBT.GetByte(note)); + } + a_BlockEntities.push_back(Note.release()); +} + + + + + +void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World)); + + int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); + if (currentLine >= 0) + { + Sign->SetLine(0, a_NBT.GetString(currentLine)); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2"); + if (currentLine >= 0) + { + Sign->SetLine(1, a_NBT.GetString(currentLine)); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3"); + if (currentLine >= 0) + { + Sign->SetLine(2, a_NBT.GetString(currentLine)); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4"); + if (currentLine >= 0) + { + Sign->SetLine(3, a_NBT.GetString(currentLine)); + } + + a_BlockEntities.push_back(Sign.release()); +} + + + + + +void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength) +{ + if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0) + { + LoadBoatFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0) + { + LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Minecart", a_IDTagLength) == 0) + { + // It is a minecart, old style, find out the type: + int TypeTag = a_NBT.FindChildByName(a_EntityTagIdx, "Type"); + if ((TypeTag < 0) || (a_NBT.GetType(TypeTag) != TAG_Int)) + { + return; + } + switch (a_NBT.GetInt(TypeTag)) + { + case 0: LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Rideable minecart + case 1: LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with chest + case 2: LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with furnace + case 3: LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with TNT + case 4: LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); break; // Minecart with Hopper + } + } + else if (strncmp(a_IDTag, "MinecartRideable", a_IDTagLength) == 0) + { + LoadMinecartRFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartChest", a_IDTagLength) == 0) + { + LoadMinecartCFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartFurnace", a_IDTagLength) == 0) + { + LoadMinecartFFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartTNT", a_IDTagLength) == 0) + { + LoadMinecartTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "MinecartHopper", a_IDTagLength) == 0) + { + LoadMinecartHFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Item", a_IDTagLength) == 0) + { + LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0) + { + LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0) + { + LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Egg", a_IDTagLength) == 0) + { + LoadEggFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "Fireball", a_IDTagLength) == 0) + { + LoadFireballFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "SmallFireball", a_IDTagLength) == 0) + { + LoadFireChargeFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + else if (strncmp(a_IDTag, "ThrownEnderpearl", a_IDTagLength) == 0) + { + LoadThrownEnderpearlFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } + // TODO: other entities +} + + + + + +void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cBoat> Boat(new cBoat(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Boat.release()); +} + + + + + +void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + // TODO +} + + + + + +void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cEmptyMinecart> Minecart(new cEmptyMinecart(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) + { + return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this + } + std::auto_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) + { + int Slot = a_NBT.FindChildByName(Child, "Slot"); + if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) + { + continue; + } + cItem Item; + if (LoadItemFromNBT(Item, a_NBT, Child)) + { + Minecart->SetSlot(a_NBT.GetByte(Slot), Item); + } + } // for itr - ItemDefs[] + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Load the Push and Fuel tags + + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Everything to do with TNT carts + + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0)); + if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) + { + return; + } + + // TODO: Everything to do with hopper carts + + a_Entities.push_back(Minecart.release()); +} + + + + + +void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item"); + if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound)) + { + return; + } + cItem Item; + if (!LoadItemFromNBT(Item, a_NBT, ItemTag)) + { + return; + } + std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false + if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx)) + { + return; + } + a_Entities.push_back(Pickup.release()); +} + + + + + +void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Load pickup state: + int PickupIdx = a_NBT.FindChildByName(a_TagIdx, "pickup"); + if (PickupIdx > 0) + { + Arrow->SetPickupState((cArrowEntity::ePickupState)a_NBT.GetByte(PickupIdx)); + } + else + { + // Try the older "player" tag: + int PlayerIdx = a_NBT.FindChildByName(a_TagIdx, "player"); + if (PlayerIdx > 0) + { + Arrow->SetPickupState((a_NBT.GetByte(PlayerIdx) == 0) ? cArrowEntity::psNoPickup : cArrowEntity::psInSurvivalOrCreative); + } + } + + // Load damage: + int DamageIdx = a_NBT.FindChildByName(a_TagIdx, "damage"); + if (DamageIdx > 0) + { + Arrow->SetDamageCoeff(a_NBT.GetDouble(DamageIdx)); + } + + // Store the new arrow in the entities list: + a_Entities.push_back(Arrow.release()); +} + + + + + +void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new snowball in the entities list: + a_Entities.push_back(Snowball.release()); +} + + + + + +void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new egg in the entities list: + a_Entities.push_back(Egg.release()); +} + + + + + +void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new fireball in the entities list: + a_Entities.push_back(Fireball.release()); +} + + + + + +void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new FireCharge in the entities list: + a_Entities.push_back(FireCharge.release()); +} + + + + + +void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::auto_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0))); + if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx)) + { + return; + } + + // Store the new enderpearl in the entities list: + a_Entities.push_back(Enderpearl.release()); +} + + + + + +bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) +{ + double Pos[3]; + if (!LoadDoublesListFromNBT(Pos, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Pos"))) + { + return false; + } + a_Entity.SetPosition(Pos[0], Pos[1], Pos[2]); + + double Speed[3]; + if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion"))) + { + return false; + } + a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]); + + double Rotation[3]; + if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation"))) + { + return false; + } + a_Entity.SetRotation(Rotation[0]); + a_Entity.SetRoll (Rotation[1]); + + return true; +} + + + + + +bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx)) + { + return false; + } + + bool IsInGround = false; + int InGroundIdx = a_NBT.FindChildByName(a_TagIdx, "inGround"); + if (InGroundIdx > 0) + { + IsInGround = (a_NBT.GetByte(InGroundIdx) != 0); + } + a_Entity.SetIsInGround(IsInGround); + + // TODO: Load inTile, TileCoords + + return true; +} + + + + + +bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx) +{ + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Double)) + { + return false; + } + int idx = 0; + for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumDoubles); Tag = a_NBT.GetNextSibling(Tag), ++idx) + { + a_Doubles[idx] = a_NBT.GetDouble(Tag); + } // for Tag - PosTag[] + return (idx == a_NumDoubles); // Did we read enough doubles? +} + + + + + +bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z) +{ + int x = a_NBT.FindChildByName(a_TagIdx, "x"); + if ((x < 0) || (a_NBT.GetType(x) != TAG_Int)) + { + return false; + } + int y = a_NBT.FindChildByName(a_TagIdx, "y"); + if ((y < 0) || (a_NBT.GetType(y) != TAG_Int)) + { + return false; + } + int z = a_NBT.FindChildByName(a_TagIdx, "z"); + if ((z < 0) || (a_NBT.GetType(z) != TAG_Int)) + { + return false; + } + a_X = a_NBT.GetInt(x); + a_Y = a_NBT.GetInt(y); + a_Z = a_NBT.GetInt(z); + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSAnvil::cMCAFile: + +cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ) : + m_RegionX(a_RegionX), + m_RegionZ(a_RegionZ), + m_FileName(a_FileName) +{ +} + + + + + +bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading) +{ + if (m_File.IsOpen()) + { + // Already open + return true; + } + + if (a_IsForReading) + { + if (!cFile::Exists(m_FileName)) + { + // We want to read and the file doesn't exist. Fail. + return false; + } + } + + if (!m_File.Open(m_FileName, cFile::fmReadWrite)) + { + // The file failed to open + return false; + } + + // Load the header: + if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header)) + { + // Cannot read the header - perhaps the file has just been created? + // Try writing a NULL header (both chunk offsets and timestamps): + memset(m_Header, 0, sizeof(m_Header)); + if ( + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps + ) + { + LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str()); + m_File.Close(); + return false; + } + } + return true; +} + + + + + +bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + if (!OpenFile(true)) + { + return false; + } + + int LocalX = a_Chunk.m_ChunkX % 32; + if (LocalX < 0) + { + LocalX = 32 + LocalX; + } + int LocalZ = a_Chunk.m_ChunkZ % 32; + if (LocalZ < 0) + { + LocalZ = 32 + LocalZ; + } + unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]); + unsigned ChunkOffset = ChunkLocation >> 8; + + m_File.Seek(ChunkOffset * 4096); + + int ChunkSize = 0; + if (m_File.Read(&ChunkSize, 4) != 4) + { + return false; + } + ChunkSize = ntohl(ChunkSize); + char CompressionType = 0; + if (m_File.Read(&CompressionType, 1) != 1) + { + return false; + } + if (CompressionType != 2) + { + // Chunk is in an unknown compression + return false; + } + ChunkSize--; + + // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly + a_Data.assign(ChunkSize, '\0'); + return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize); +} + + + + + +bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + if (!OpenFile(false)) + { + LOGWARNING("Cannot save chunk [%d, %d], opening file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + + int LocalX = a_Chunk.m_ChunkX % 32; + if (LocalX < 0) + { + LocalX = 32 + LocalX; + } + int LocalZ = a_Chunk.m_ChunkZ % 32; + if (LocalZ < 0) + { + LocalZ = 32 + LocalZ; + } + + unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data); + + // Store the chunk data: + m_File.Seek(ChunkSector * 4096); + unsigned ChunkSize = htonl(a_Data.size() + 1); + if (m_File.Write(&ChunkSize, 4) != 4) + { + LOGWARNING("Cannot save chunk [%d, %d], writing(1) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + char CompressionType = 2; + if (m_File.Write(&CompressionType, 1) != 1) + { + LOGWARNING("Cannot save chunk [%d, %d], writing(2) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + if (m_File.Write(a_Data.data(), a_Data.size()) != (int)(a_Data.size())) + { + LOGWARNING("Cannot save chunk [%d, %d], writing(3) data to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + + // Store the header: + ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number + ASSERT(ChunkSize < 256); + m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); + if (m_File.Seek(0) < 0) + { + LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + if (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) + { + LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } + + return true; +} + + + + + +unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data) +{ + // See if it fits the current location: + unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]); + unsigned ChunkLen = ChunkLocation & 0xff; + if (a_Data.size() + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096)) + { + return ChunkLocation >> 8; + } + + // Doesn't fit, append to the end of file (we're wasting a lot of space, TODO: fix this later) + unsigned MaxLocation = 2 << 8; // Minimum sector is #2 - after the headers + for (int i = 0; i < ARRAYCOUNT(m_Header); i++) + { + ChunkLocation = ntohl(m_Header[i]); + ChunkLocation = ChunkLocation + ((ChunkLocation & 0xff) << 8); // Add the number of sectors used; don't care about the 4th byte + if (MaxLocation < ChunkLocation) + { + MaxLocation = ChunkLocation; + } + } // for i - m_Header[] + return MaxLocation >> 8; +} + + + + diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h new file mode 100644 index 000000000..7685d2236 --- /dev/null +++ b/src/WorldStorage/WSSAnvil.h @@ -0,0 +1,184 @@ + +// WSSAnvil.h + +// Interfaces to the cWSSAnvil class representing the Anvil world storage scheme + + + + +#pragma once + +#include "WorldStorage.h" +#include "FastNBT.h" + + + + + +// fwd: ItemGrid.h +class cItemGrid; + +class cProjectileEntity; + + + + + +enum +{ + /// Maximum number of chunks in an MCA file - also the count of the header items + MCA_MAX_CHUNKS = 32 * 32, + + /// The MCA header is 8 KiB + MCA_HEADER_SIZE = MCA_MAX_CHUNKS * 8, + + /// There are 5 bytes of header in front of each chunk + MCA_CHUNK_HEADER_LENGTH = 5, +} ; + + + + + +class cWSSAnvil : + public cWSSchema +{ + typedef cWSSchema super; + +public: + + cWSSAnvil(cWorld * a_World); + virtual ~cWSSAnvil(); + +protected: + + class cMCAFile + { + public: + + cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ); + + bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data); + bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data); + bool EraseChunkData(const cChunkCoords & a_Chunk); + + int GetRegionX (void) const {return m_RegionX; } + int GetRegionZ (void) const {return m_RegionZ; } + const AString & GetFileName(void) const {return m_FileName; } + + protected: + + int m_RegionX; + int m_RegionZ; + cFile m_File; + AString m_FileName; + + // The header, copied from the file so we don't have to seek to it all the time + // First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count + unsigned m_Header[MCA_MAX_CHUNKS]; + + // Chunk timestamps, following the chunk headers, are unused by MCS + + /// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number. + unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); + + /// Opens a MCA file either for a Read operation (fails if doesn't exist) or for a Write operation (creates new if not found) + bool OpenFile(bool a_IsForReading); + } ; + typedef std::list<cMCAFile *> cMCAFiles; + + cCriticalSection m_CS; + cMCAFiles m_Files; // a MRU cache of MCA files + + /// Gets chunk data from the correct file; locks file CS as needed + bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); + + /// Sets chunk data into the correct file; locks file CS as needed + bool SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data); + + /// Loads the chunk from the data (no locking needed) + bool LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data); + + /// Saves the chunk into datastream (no locking needed) + bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); + + /// Loads the chunk from NBT data (no locking needed) + bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT); + + /// Saves the chunk into NBT data using a_Writer; returns true on success + bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer); + + /// Loads the chunk's biome map from vanilla-format; returns a_BiomeMap if biomes present and valid, NULL otherwise + cChunkDef::BiomeMap * LoadVanillaBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads the chunk's biome map from MCS format; returns a_BiomeMap if biomes present and valid, NULL otherwise + cChunkDef::BiomeMap * LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1) + void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag); + + /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) + void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); + + /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag + bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx); + + /** Loads contentents of an Items[] list tag into a cItemGrid + ItemGrid begins at the specified slot offset + Slots outside the ItemGrid range are ignored + */ + void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0); + + void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); + void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + + void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength); + + void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartFFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadFireChargeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads entity common data from the NBT compound; returns true if successful + bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Loads projectile common data from the NBT compound; returns true if successful + bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx); + + /// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful + bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx); + + /// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful + bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z); + + /// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked + cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk); + + /// Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer + void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length); + + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; + virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; + virtual const AString GetName(void) const override {return "anvil"; } +} ; + + + + diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp new file mode 100644 index 000000000..dafa4d1b5 --- /dev/null +++ b/src/WorldStorage/WSSCompact.cpp @@ -0,0 +1,1009 @@ + +// WSSCompact.cpp + +// Interfaces to the cWSSCompact class representing the "compact" storage schema (PAK-files) + +#include "Globals.h" +#include "WSSCompact.h" +#include "../World.h" +#include "lib/zlib/zlib.h" +#include "lib/jsoncpp/include/json/json.h" +#include "../StringCompression.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" + + + + + +#pragma pack(push, 1) +/// The chunk header, as stored in the file: +struct cWSSCompact::sChunkHeader +{ + int m_ChunkX; + int m_ChunkZ; + int m_CompressedSize; + int m_UncompressedSize; +} ; +#pragma pack(pop) + + + + + +/// The maximum number of PAK files that are cached +const int MAX_PAK_FILES = 16; + +/// The maximum number of unsaved chunks before the cPAKFile saves them to disk +const int MAX_DIRTY_CHUNKS = 16; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cJsonChunkSerializer: + +cJsonChunkSerializer::cJsonChunkSerializer(void) : + m_HasJsonData(false) +{ +} + + + + + +void cJsonChunkSerializer::Entity(cEntity * a_Entity) +{ + // TODO: a_Entity->SaveToJson(m_Root); +} + + + + + +void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity) +{ + const char * SaveInto = NULL; + switch (a_BlockEntity->GetBlockType()) + { + case E_BLOCK_CHEST: SaveInto = "Chests"; break; + case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break; + case E_BLOCK_DROPPER: SaveInto = "Droppers"; break; + case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break; + case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break; + case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break; + case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break; + case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break; + + default: + { + ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON"); + break; + } + } // switch (BlockEntity->GetBlockType()) + if (SaveInto == NULL) + { + return; + } + + Json::Value val; + a_BlockEntity->SaveToJson(val); + m_Root[SaveInto].append(val); + m_HasJsonData = true; +} + + + + + +bool cJsonChunkSerializer::LightIsValid(bool a_IsLightValid) +{ + if (!a_IsLightValid) + { + return false; + } + m_Root["IsLightValid"] = true; + m_HasJsonData = true; + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSCompact: + +cWSSCompact::~cWSSCompact() +{ + for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) + { + delete *itr; + } +} + + + + + +bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk) +{ + AString ChunkData; + int UncompressedSize = 0; + if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData)) + { + // The reason for failure is already printed in GetChunkData() + return false; + } + + return LoadChunkFromData(a_Chunk, UncompressedSize, ChunkData, m_World); +} + + + + + +bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk) +{ + cCSLock Lock(m_CS); + + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + // For some reason we couldn't locate the file + LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ); + return false; + } + return f->SaveChunk(a_Chunk, m_World); +} + + + + + +cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk) +{ + // ASSUMES that m_CS has been locked + + // We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file + const int LayerX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32); + const int LayerZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32); + + // Is it already cached? + for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) + { + if (((*itr) != NULL) && ((*itr)->GetLayerX() == LayerX) && ((*itr)->GetLayerZ() == LayerZ)) + { + // Move the file to front and return it: + cPAKFile * f = *itr; + if (itr != m_PAKFiles.begin()) + { + m_PAKFiles.erase(itr); + m_PAKFiles.push_front(f); + } + return f; + } + } + + // Load it anew: + AString FileName; + Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ ); + cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ); + if (f == NULL) + { + return NULL; + } + m_PAKFiles.push_front(f); + + // If there are too many PAK files cached, delete the last one used: + if (m_PAKFiles.size() > MAX_PAK_FILES) + { + delete m_PAKFiles.back(); + m_PAKFiles.pop_back(); + } + return f; +} + + + + + +bool cWSSCompact::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data) +{ + cCSLock Lock(m_CS); + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + return false; + } + return f->GetChunkData(a_Chunk, a_UncompressedSize, a_Data); +} + + + + + +/* +// TODO: Rewrite saving to use the same principles as loading +bool cWSSCompact::SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data) +{ + cCSLock Lock(m_CS); + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + return false; + } + return f->SetChunkData(a_Chunk, a_UncompressedSize, a_Data); +} +*/ + + + + + +bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk) +{ + cCSLock Lock(m_CS); + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + return false; + } + return f->EraseChunkData(a_Chunk); +} + + + + + +void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World) +{ + // Load chests + Json::Value AllChests = a_Value.get("Chests", Json::nullValue); + if (!AllChests.empty()) + { + for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr ) + { + Json::Value & Chest = *itr; + cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World); + if (!ChestEntity->LoadFromJson( Chest ) ) + { + LOGERROR("ERROR READING CHEST FROM JSON!" ); + delete ChestEntity; + } + else + { + a_BlockEntities.push_back( ChestEntity ); + } + } // for itr - AllChests[] + } + + // Load dispensers + Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue); + if( !AllDispensers.empty() ) + { + for( Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr ) + { + Json::Value & Dispenser = *itr; + cDispenserEntity * DispenserEntity = new cDispenserEntity(0,0,0, a_World); + if( !DispenserEntity->LoadFromJson( Dispenser ) ) + { + LOGERROR("ERROR READING DISPENSER FROM JSON!" ); + delete DispenserEntity; + } + else + { + a_BlockEntities.push_back( DispenserEntity ); + } + } // for itr - AllDispensers[] + } + + // Load furnaces + Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue); + if( !AllFurnaces.empty() ) + { + for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr ) + { + Json::Value & Furnace = *itr; + // TODO: The block type and meta aren't correct, there's no way to get them here + cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World); + if (!FurnaceEntity->LoadFromJson(Furnace)) + { + LOGERROR("ERROR READING FURNACE FROM JSON!" ); + delete FurnaceEntity; + } + else + { + a_BlockEntities.push_back(FurnaceEntity); + } + } // for itr - AllFurnaces[] + } + + // Load signs + Json::Value AllSigns = a_Value.get("Signs", Json::nullValue); + if( !AllSigns.empty() ) + { + for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr ) + { + Json::Value & Sign = *itr; + cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World); + if ( !SignEntity->LoadFromJson( Sign ) ) + { + LOGERROR("ERROR READING SIGN FROM JSON!" ); + delete SignEntity; + } + else + { + a_BlockEntities.push_back( SignEntity ); + } + } // for itr - AllSigns[] + } + + // Load note blocks + Json::Value AllNotes = a_Value.get("Notes", Json::nullValue); + if( !AllNotes.empty() ) + { + for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr ) + { + Json::Value & Note = *itr; + cNoteEntity * NoteEntity = new cNoteEntity(0, 0, 0, a_World); + if ( !NoteEntity->LoadFromJson( Note ) ) + { + LOGERROR("ERROR READING NOTE BLOCK FROM JSON!" ); + delete NoteEntity; + } + else + { + a_BlockEntities.push_back( NoteEntity ); + } + } // for itr - AllNotes[] + } + + // Load jukeboxes + Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue); + if( !AllJukeboxes.empty() ) + { + for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr ) + { + Json::Value & Jukebox = *itr; + cJukeboxEntity * JukeboxEntity = new cJukeboxEntity(0, 0, 0, a_World); + if ( !JukeboxEntity->LoadFromJson( Jukebox ) ) + { + LOGERROR("ERROR READING JUKEBOX FROM JSON!" ); + delete JukeboxEntity; + } + else + { + a_BlockEntities.push_back( JukeboxEntity ); + } + } // for itr - AllJukeboxes[] + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSCompact::cPAKFile + +#define READ(Var) \ + if (f.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ + return; \ + } + +cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) : + m_FileName(a_FileName), + m_LayerX(a_LayerX), + m_LayerZ(a_LayerZ), + m_NumDirty(0), + m_ChunkVersion( CHUNK_VERSION ), // Init with latest version + m_PakVersion( PAK_VERSION ) +{ + cFile f; + if (!f.Open(m_FileName, cFile::fmRead)) + { + return; + } + + // Read headers: + READ(m_PakVersion); + if (m_PakVersion != 1) + { + LOGERROR("File \"%s\" is in an unknown pak format (%d)", m_FileName.c_str(), m_PakVersion); + return; + } + + READ(m_ChunkVersion); + switch( m_ChunkVersion ) + { + case 1: + m_ChunkSize.Set(16, 128, 16); + break; + case 2: + case 3: + m_ChunkSize.Set(16, 256, 16); + break; + default: + LOGERROR("File \"%s\" is in an unknown chunk format (%d)", m_FileName.c_str(), m_ChunkVersion); + return; + }; + + short NumChunks = 0; + READ(NumChunks); + + // Read chunk headers: + for (int i = 0; i < NumChunks; i++) + { + sChunkHeader * Header = new sChunkHeader; + READ(*Header); + m_ChunkHeaders.push_back(Header); + } // for i - chunk headers + + // Read chunk data: + if (f.ReadRestOfFile(m_DataContents) == -1) + { + LOGERROR("Cannot read file \"%s\" contents", m_FileName.c_str()); + return; + } + + if( m_ChunkVersion == 1 ) // Convert chunks to version 2 + { + UpdateChunk1To2(); + } +#if AXIS_ORDER == AXIS_ORDER_XZY + if( m_ChunkVersion == 2 ) // Convert chunks to version 3 + { + UpdateChunk2To3(); + } +#endif +} + + + + + +cWSSCompact::cPAKFile::~cPAKFile() +{ + if (m_NumDirty > 0) + { + SynchronizeFile(); + } + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + delete *itr; + } +} + + + + + +bool cWSSCompact::cPAKFile::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data) +{ + int ChunkX = a_Chunk.m_ChunkX; + int ChunkZ = a_Chunk.m_ChunkZ; + sChunkHeader * Header = NULL; + int Offset = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) + { + Header = *itr; + break; + } + Offset += (*itr)->m_CompressedSize; + } + if ((Header == NULL) || (Offset + Header->m_CompressedSize > (int)m_DataContents.size())) + { + // Chunk not found / data invalid + return false; + } + + a_UncompressedSize = Header->m_UncompressedSize; + a_Data.assign(m_DataContents, Offset, Header->m_CompressedSize); + return true; +} + + + + + +bool cWSSCompact::cPAKFile::SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World) +{ + if (!SaveChunkToData(a_Chunk, a_World)) + { + return false; + } + if (m_NumDirty > MAX_DIRTY_CHUNKS) + { + SynchronizeFile(); + } + return true; +} + + + + + +void cWSSCompact::cPAKFile::UpdateChunk1To2() +{ + int Offset = 0; + AString NewDataContents; + int ChunksConverted = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + sChunkHeader * Header = *itr; + + if( ChunksConverted % 32 == 0 ) + { + LOGINFO("Updating \"%s\" version 1 to version 2: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() ); + } + ChunksConverted++; + + AString Data; + int UncompressedSize = Header->m_UncompressedSize; + Data.assign(m_DataContents, Offset, Header->m_CompressedSize); + Offset += Header->m_CompressedSize; + + // Crude data integrity check: + int ExpectedSize = (16*128*16)*2 + (16*128*16)/2; // For version 1 + if (UncompressedSize < ExpectedSize) + { + LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", + Header->m_ChunkX, Header->m_ChunkZ, + UncompressedSize, ExpectedSize + ); + Offset += Header->m_CompressedSize; + continue; + } + + // Decompress the data: + AString UncompressedData; + { + int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + } + + if (UncompressedSize != (int)UncompressedData.size()) + { + LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", + UncompressedSize, UncompressedData.size(), + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + + + // Old version is 128 blocks high with YZX axis order + char ConvertedData[cChunkDef::BlockDataSize]; + int Index = 0; + unsigned int InChunkOffset = 0; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) + { + for( int y = 0; y < 128; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 128 + x * 128 * 16 + InChunkOffset]; + } + // Add 128 empty blocks after an old y column + memset(ConvertedData + Index, E_BLOCK_AIR, 128); + Index += 128; + } + InChunkOffset += (16 * 128 * 16); + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Metadata + { + for( int y = 0; y < 64; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; + } + memset(ConvertedData + Index, 0, 64); + Index += 64; + } + InChunkOffset += (16 * 128 * 16) / 2; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Block light + { + for( int y = 0; y < 64; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; + } + memset(ConvertedData + Index, 0, 64); + Index += 64; + } + InChunkOffset += (16*128*16)/2; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) // Sky light + { + for( int y = 0; y < 64; ++y ) + { + ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset]; + } + memset(ConvertedData + Index, 0, 64); + Index += 64; + } + InChunkOffset += (16 * 128 * 16) / 2; + + AString Converted(ConvertedData, ARRAYCOUNT(ConvertedData)); + + // Add JSON data afterwards + if (UncompressedData.size() > InChunkOffset) + { + Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() ); + } + + // Re-compress data + AString CompressedData; + { + int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); + if (errorcode != Z_OK) + { + LOGERROR("Error %d compressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + continue; + } + } + + // Save into file's cache + Header->m_UncompressedSize = Converted.size(); + Header->m_CompressedSize = CompressedData.size(); + NewDataContents.append( CompressedData ); + } + + // Done converting + m_DataContents = NewDataContents; + m_ChunkVersion = 2; + SynchronizeFile(); + + LOGINFO("Updated \"%s\" version 1 to version 2", m_FileName.c_str() ); +} + + + + + +void cWSSCompact::cPAKFile::UpdateChunk2To3() +{ + int Offset = 0; + AString NewDataContents; + int ChunksConverted = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + sChunkHeader * Header = *itr; + + if( ChunksConverted % 32 == 0 ) + { + LOGINFO("Updating \"%s\" version 2 to version 3: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() ); + } + ChunksConverted++; + + AString Data; + int UncompressedSize = Header->m_UncompressedSize; + Data.assign(m_DataContents, Offset, Header->m_CompressedSize); + Offset += Header->m_CompressedSize; + + // Crude data integrity check: + const int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2 + if (UncompressedSize < ExpectedSize) + { + LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", + Header->m_ChunkX, Header->m_ChunkZ, + UncompressedSize, ExpectedSize + ); + Offset += Header->m_CompressedSize; + continue; + } + + // Decompress the data: + AString UncompressedData; + { + int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, UncompressedSize); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + } + + if (UncompressedSize != (int)UncompressedData.size()) + { + LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", + UncompressedSize, UncompressedData.size(), + Header->m_ChunkX, Header->m_ChunkZ + ); + Offset += Header->m_CompressedSize; + continue; + } + + char ConvertedData[ExpectedSize]; + memset(ConvertedData, 0, ExpectedSize); + + // Cannot use cChunk::MakeIndex because it might change again????????? + // For compatibility, use what we know is current + #define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) ) + #define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) ) + + unsigned int InChunkOffset = 0; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) // YZX Loop order is important, in 1.1 Y was first then Z then X + { + ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset]; + ++InChunkOffset; + } // for y, z, x + + + unsigned int index2 = 0; + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) + { + ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); + ++index2; + } + InChunkOffset += index2 / 2; + index2 = 0; + + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) + { + ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); + ++index2; + } + InChunkOffset += index2 / 2; + index2 = 0; + + for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) + { + ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4); + ++index2; + } + InChunkOffset += index2 / 2; + index2 = 0; + + AString Converted(ConvertedData, ExpectedSize); + + // Add JSON data afterwards + if (UncompressedData.size() > InChunkOffset) + { + Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end() ); + } + + // Re-compress data + AString CompressedData; + { + int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData); + if (errorcode != Z_OK) + { + LOGERROR("Error %d compressing data for chunk [%d, %d]", + errorcode, + Header->m_ChunkX, Header->m_ChunkZ + ); + continue; + } + } + + // Save into file's cache + Header->m_UncompressedSize = Converted.size(); + Header->m_CompressedSize = CompressedData.size(); + NewDataContents.append( CompressedData ); + } + + // Done converting + m_DataContents = NewDataContents; + m_ChunkVersion = 3; + SynchronizeFile(); + + LOGINFO("Updated \"%s\" version 2 to version 3", m_FileName.c_str() ); +} + + + + + +bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World) +{ + // Crude data integrity check: + if (a_UncompressedSize < cChunkDef::BlockDataSize) + { + LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing", + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, + a_UncompressedSize, cChunkDef::BlockDataSize + ); + EraseChunkData(a_Chunk); + return false; + } + + // Decompress the data: + AString UncompressedData; + int errorcode = UncompressString(a_Data.data(), a_Data.size(), UncompressedData, a_UncompressedSize); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d]", + errorcode, + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ + ); + return false; + } + + if (a_UncompressedSize != (int)UncompressedData.size()) + { + LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]", + a_UncompressedSize, UncompressedData.size(), + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ + ); + return false; + } + + cEntityList Entities; + cBlockEntityList BlockEntities; + bool IsLightValid = false; + + if (a_UncompressedSize > cChunkDef::BlockDataSize) + { + Json::Value root; // will contain the root value after parsing. + Json::Reader reader; + if ( !reader.parse( UncompressedData.data() + cChunkDef::BlockDataSize, root, false ) ) + { + LOGERROR("Failed to parse trailing JSON in chunk [%d, %d]!", + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ + ); + } + else + { + LoadEntitiesFromJson(root, Entities, BlockEntities, a_World); + IsLightValid = root.get("IsLightValid", false).asBool(); + } + } + + BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data(); + NIBBLETYPE * MetaData = (NIBBLETYPE *)(BlockData + cChunkDef::MetaOffset); + NIBBLETYPE * BlockLight = (NIBBLETYPE *)(BlockData + cChunkDef::LightOffset); + NIBBLETYPE * SkyLight = (NIBBLETYPE *)(BlockData + cChunkDef::SkyLightOffset); + + a_World->SetChunkData( + a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, + BlockData, MetaData, + IsLightValid ? BlockLight : NULL, + IsLightValid ? SkyLight : NULL, + NULL, NULL, + Entities, BlockEntities, + false + ); + + return true; +} + + + + + +bool cWSSCompact::cPAKFile::EraseChunkData(const cChunkCoords & a_Chunk) +{ + int ChunkX = a_Chunk.m_ChunkX; + int ChunkZ = a_Chunk.m_ChunkZ; + int Offset = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) + { + m_DataContents.erase(Offset, (*itr)->m_CompressedSize); + delete *itr; + itr = m_ChunkHeaders.erase(itr); + return true; + } + Offset += (*itr)->m_CompressedSize; + } + + return false; +} + + + + + +bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World) +{ + // Serialize the chunk: + cJsonChunkSerializer Serializer; + if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer)) + { + // Chunk not valid + LOG("cWSSCompact: Trying to save chunk [%d, %d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + return false; + } + + AString Data; + Data.assign((const char *)Serializer.GetBlockData(), cChunkDef::BlockDataSize); + if (Serializer.HasJsonData()) + { + AString JsonData; + Json::StyledWriter writer; + JsonData = writer.write(Serializer.GetRoot()); + Data.append(JsonData); + } + + // Compress the data: + AString CompressedData; + int errorcode = CompressString(Data.data(), Data.size(), CompressedData); + if ( errorcode != Z_OK ) + { + LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + return false; + } + + // Erase any existing data for the chunk: + EraseChunkData(a_Chunk); + + // Save the header: + sChunkHeader * Header = new sChunkHeader; + if (Header == NULL) + { + LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ); + return false; + } + Header->m_CompressedSize = (int)CompressedData.size(); + Header->m_ChunkX = a_Chunk.m_ChunkX; + Header->m_ChunkZ = a_Chunk.m_ChunkZ; + Header->m_UncompressedSize = (int)Data.size(); + m_ChunkHeaders.push_back(Header); + + m_DataContents.append(CompressedData.data(), CompressedData.size()); + + m_NumDirty++; + return true; +} + + + + + +#define WRITE(Var) \ + if (f.Write(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("cWSSCompact: ERROR writing %s to file \"%s\" (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ + return; \ + } + +void cWSSCompact::cPAKFile::SynchronizeFile(void) +{ + cFile f; + if (!f.Open(m_FileName, cFile::fmWrite)) + { + LOGERROR("Cannot open PAK file \"%s\" for writing", m_FileName.c_str()); + return; + } + + WRITE(m_PakVersion); + WRITE(m_ChunkVersion); + short NumChunks = (short)m_ChunkHeaders.size(); + WRITE(NumChunks); + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + WRITE(**itr); + } + if (f.Write(m_DataContents.data(), m_DataContents.size()) != (int)m_DataContents.size()) + { + LOGERROR("cWSSCompact: ERROR writing chunk contents to file \"%s\" (line %d); file offset %d", m_FileName.c_str(), __LINE__, f.Tell()); + return; + } + m_NumDirty = 0; +} + + + + diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h new file mode 100644 index 000000000..e6a013eaf --- /dev/null +++ b/src/WorldStorage/WSSCompact.h @@ -0,0 +1,144 @@ + +// WSSCompact.h + +// Interfaces to the cWSSCompact class representing the "Compact" storage schema (PAK-files) + + + + + +#pragma once +#ifndef WSSCOMPACT_H_INCLUDED +#define WSSCOMPACT_H_INCLUDED + +#include "WorldStorage.h" +#include "../Vector3i.h" + + + + + +/// Helper class for serializing a chunk into Json +class cJsonChunkSerializer : + public cChunkDataCollector +{ +public: + + cJsonChunkSerializer(void); + + Json::Value & GetRoot (void) {return m_Root; } + BLOCKTYPE * GetBlockData(void) {return (BLOCKTYPE *)m_BlockData; } + bool HasJsonData (void) const {return m_HasJsonData; } + +protected: + + // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array + + // Entities and BlockEntities are serialized to Json + Json::Value m_Root; + bool m_HasJsonData; + + // cChunkDataCollector overrides: + virtual void Entity (cEntity * a_Entity) override; + virtual void BlockEntity (cBlockEntity * a_Entity) override; + virtual bool LightIsValid (bool a_IsLightValid) override; +} ; + + + + + +class cWSSCompact : + public cWSSchema +{ +public: + cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {} + virtual ~cWSSCompact(); + +protected: + + struct sChunkHeader; + typedef std::vector<sChunkHeader *> sChunkHeaders; + + /// Implements a cache for a single PAK file; implements lazy-write in order to be able to write multiple chunks fast + class cPAKFile + { + public: + + cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ); + ~cPAKFile(); + + bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); + bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data); + bool EraseChunkData(const cChunkCoords & a_Chunk); + + bool SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World); + + int GetLayerX(void) const {return m_LayerX; } + int GetLayerZ(void) const {return m_LayerZ; } + + static const int PAK_VERSION = 1; +#if AXIS_ORDER == AXIS_ORDER_XZY + static const int CHUNK_VERSION = 3; +#elif AXIS_ORDER == AXIS_ORDER_YZX + static const int CHUNK_VERSION = 2; +#endif + protected: + + AString m_FileName; + int m_LayerX; + int m_LayerZ; + + sChunkHeaders m_ChunkHeaders; + AString m_DataContents; // Data contents of the file, cached + + int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file + + Vector3i m_ChunkSize; // Is related to m_ChunkVersion + char m_ChunkVersion; + char m_PakVersion; + + bool SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World); // Saves the chunk to m_DataContents, updates headers and m_NumDirty + void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty + + void UpdateChunk1To2(void); // Height from 128 to 256 + void UpdateChunk2To3(void); // Axis order from YZX to XZY + } ; + + typedef std::list<cPAKFile *> cPAKFiles; + + cCriticalSection m_CS; + cPAKFiles m_PAKFiles; // A MRU cache of PAK files + + /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache + cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk); + + /// Gets chunk data from the correct file; locks CS as needed + bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data); + + /// Sets chunk data to the correct file; locks CS as needed + bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data); + + /// Erases chunk data from the correct file; locks CS as needed + bool EraseChunkData(const cChunkCoords & a_Chunk); + + /// Loads the chunk from the data (no locking needed) + bool LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, const AString & a_Data, cWorld * a_World); + + void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World); + + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; + virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; + virtual const AString GetName(void) const override {return "compact"; } +} ; + + + + + +#endif // WSSCOMPACT_H_INCLUDED + + + + diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp new file mode 100644 index 000000000..f290ec128 --- /dev/null +++ b/src/WorldStorage/WorldStorage.cpp @@ -0,0 +1,409 @@ + +// WorldStorage.cpp + +// Implements the cWorldStorage class representing the chunk loading / saving thread + +// To add a new storage schema, implement a cWSSchema descendant and add it to cWorldStorage::InitSchemas() + +#include "Globals.h" +#include "WorldStorage.h" +#include "WSSCompact.h" +#include "WSSAnvil.h" +#include "../World.h" +#include "../Generating/ChunkGenerator.h" +#include "../Entities/Entity.h" +#include "../BlockEntities/BlockEntity.h" + + + + + +/// If a chunk with this Y coord is de-queued, it is a signal to emit the saved-all message (cWorldStorage::QueueSavedMessage()) +#define CHUNK_Y_MESSAGE 2 + + + + + +/// Example storage schema - forgets all chunks ;) +class cWSSForgetful : + public cWSSchema +{ +public: + cWSSForgetful(cWorld * a_World) : cWSSchema(a_World) {} + +protected: + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkCoords & a_Chunk) override {return false; } + virtual bool SaveChunk(const cChunkCoords & a_Chunk) override {return true; } + virtual const AString GetName(void) const override {return "forgetful"; } +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldStorage: + +cWorldStorage::cWorldStorage(void) : + super("cWorldStorage"), + m_World(NULL), + m_SaveSchema(NULL) +{ +} + + + + + +cWorldStorage::~cWorldStorage() +{ + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + delete *itr; + } // for itr - m_Schemas[] + m_LoadQueue.clear(); + m_SaveQueue.clear(); +} + + + + + +bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName) +{ + m_World = a_World; + m_StorageSchemaName = a_StorageSchemaName; + InitSchemas(); + + return super::Start(); +} + + + + + +void cWorldStorage::Stop(void) +{ + WaitForFinish(); +} + + + + + +void cWorldStorage::WaitForFinish(void) +{ + LOG("Waiting for the world storage to finish saving"); + + { + // Cancel all loading requests: + cCSLock Lock(m_CSQueues); + m_LoadQueue.clear(); + } + + // Wait for the saving to finish: + WaitForQueuesEmpty(); + + // Wait for the thread to finish: + m_ShouldTerminate = true; + m_Event.Set(); + m_evtRemoved.Set(); // Wake up anybody waiting in the WaitForQueuesEmpty() method + super::Wait(); + LOG("World storage thread finished"); +} + + + + + +void cWorldStorage::WaitForQueuesEmpty(void) +{ + cCSLock Lock(m_CSQueues); + while (!m_ShouldTerminate && (!m_LoadQueue.empty() || !m_SaveQueue.empty())) + { + cCSUnlock Unlock(Lock); + m_evtRemoved.Wait(); + } +} + + + + + +int cWorldStorage::GetLoadQueueLength(void) +{ + cCSLock Lock(m_CSQueues); + return (int)m_LoadQueue.size(); +} + + + + + +int cWorldStorage::GetSaveQueueLength(void) +{ + cCSLock Lock(m_CSQueues); + return (int)m_SaveQueue.size(); +} + + + + + +void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) +{ + // Queues the chunk for loading; if not loaded, the chunk will be generated + { + cCSLock Lock(m_CSQueues); + + // Check if already in the queue: + for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_Generate == a_Generate)) + { + return; + } + } + m_LoadQueue.push_back(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate)); + } + + m_Event.Set(); +} + + + + + +void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CSQueues); + m_SaveQueue.remove (cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); // Don't add twice + m_SaveQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + } + m_Event.Set(); +} + + + + + +void cWorldStorage::QueueSavedMessage(void) +{ + // Pushes a special coord pair into the queue, signalizing a message instead: + { + cCSLock Lock(m_CSQueues); + m_SaveQueue.push_back(cChunkCoords(0, CHUNK_Y_MESSAGE, 0)); + } + m_Event.Set(); +} + + + + + +void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSQueues); + for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr) + { + if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkY != a_ChunkY) || (itr->m_ChunkZ != a_ChunkZ)) + { + continue; + } + m_LoadQueue.erase(itr); + Lock.Unlock(); + m_evtRemoved.Set(); + return; + } // for itr - m_LoadQueue[] +} + + + + + +void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) +{ + { + cCSLock Lock(m_CSQueues); + m_SaveQueue.remove(a_Chunk); + } + m_evtRemoved.Set(); +} + + + + + +void cWorldStorage::InitSchemas(void) +{ + // The first schema added is considered the default + m_Schemas.push_back(new cWSSAnvil (m_World)); + m_Schemas.push_back(new cWSSCompact (m_World)); + m_Schemas.push_back(new cWSSForgetful(m_World)); + // Add new schemas here + + if (NoCaseCompare(m_StorageSchemaName, "default") == 0) + { + m_SaveSchema = m_Schemas.front(); + return; + } + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + if (NoCaseCompare((*itr)->GetName(), m_StorageSchemaName) == 0) + { + m_SaveSchema = *itr; + return; + } + } // for itr - m_Schemas[] + + // Unknown schema selected, let the admin know: + LOGWARNING("Unknown storage schema name \"%s\". Using default (\"%s\"). Available schemas:", + m_StorageSchemaName.c_str(), m_SaveSchema->GetName().c_str() + ); + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + LOGWARNING("\t\"%s\"", (*itr)->GetName().c_str()); + } + m_SaveSchema = m_Schemas.front(); +} + + + + + +void cWorldStorage::Execute(void) +{ + while (!m_ShouldTerminate) + { + m_Event.Wait(); + + // Process both queues until they are empty again: + bool HasMore; + do + { + HasMore = false; + if (m_ShouldTerminate) + { + return; + } + + HasMore = LoadOneChunk(); + HasMore = HasMore | SaveOneChunk(); + m_evtRemoved.Set(); + } while (HasMore); + } +} + + + + + +bool cWorldStorage::LoadOneChunk(void) +{ + sChunkLoad ToLoad(0, 0, 0, false); + bool HasMore; + bool ShouldLoad = false; + { + cCSLock Lock(m_CSQueues); + if (!m_LoadQueue.empty()) + { + ToLoad = m_LoadQueue.front(); + m_LoadQueue.pop_front(); + ShouldLoad = true; + } + HasMore = !m_LoadQueue.empty(); + } + + if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ)) + { + if (ToLoad.m_Generate) + { + // The chunk couldn't be loaded, generate it: + m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); + } + else + { + // TODO: Notify the world that the load has failed: + // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ); + } + } + return HasMore; +} + + + + + +bool cWorldStorage::SaveOneChunk(void) +{ + cChunkCoords Save(0, 0, 0); + bool HasMore; + bool ShouldSave = false; + { + cCSLock Lock(m_CSQueues); + if (!m_SaveQueue.empty()) + { + Save = m_SaveQueue.front(); + m_SaveQueue.pop_front(); + ShouldSave = true; + } + HasMore = !m_SaveQueue.empty(); + } + if (Save.m_ChunkY == CHUNK_Y_MESSAGE) + { + LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str()); + return HasMore; + } + if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkZ)) + { + m_World->MarkChunkSaving(Save.m_ChunkX, Save.m_ChunkZ); + if (m_SaveSchema->SaveChunk(Save)) + { + m_World->MarkChunkSaved(Save.m_ChunkX, Save.m_ChunkZ); + } + } + return HasMore; +} + + + + + +bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) + { + // Already loaded (can happen, since the queue is async) + return true; + } + + cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ); + + // First try the schema that is used for saving + if (m_SaveSchema->LoadChunk(Coords)) + { + return true; + } + + // If it didn't have the chunk, try all the other schemas: + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(Coords)) + { + return true; + } + } + + // Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true): + m_World->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); + + return false; +} + + + + + diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h new file mode 100644 index 000000000..b2c6dfb4b --- /dev/null +++ b/src/WorldStorage/WorldStorage.h @@ -0,0 +1,135 @@ + +// WorldStorage.h + +// Interfaces to the cWorldStorage class representing the chunk loading / saving thread +// This class decides which storage schema to use for saving; it queries all available schemas for loading +// Also declares the base class for all storage schemas, cWSSchema +// Helper serialization class cJsonChunkSerializer is declared as well + + + + + +#pragma once +#ifndef WORLDSTORAGE_H_INCLUDED +#define WORLDSTORAGE_H_INCLUDED + +#include "../ChunkDef.h" +#include "../OSSupport/IsThread.h" +#include "lib/jsoncpp/include/json/json.h" + + + + + +// fwd: +class cWorld; + + + + + +/// Interface that all the world storage schemas need to implement +class cWSSchema abstract +{ +public: + cWSSchema(cWorld * a_World) : m_World(a_World) {} + virtual ~cWSSchema() {} // Force the descendants' destructors to be virtual + + virtual bool LoadChunk(const cChunkCoords & a_Chunk) = 0; + virtual bool SaveChunk(const cChunkCoords & a_Chunk) = 0; + virtual const AString GetName(void) const = 0; + +protected: + + cWorld * m_World; +} ; + +typedef std::list<cWSSchema *> cWSSchemaList; + + + + + +/// The actual world storage class +class cWorldStorage : + public cIsThread +{ + typedef cIsThread super; + +public: + + cWorldStorage(void); + ~cWorldStorage(); + + void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true + void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Signals that a message should be output to the console when all the chunks have been saved + void QueueSavedMessage(void); + + /// Loads the chunk specified; returns true on success, false on failure + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void UnqueueSave(const cChunkCoords & a_Chunk); + + bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args + void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event + void WaitForFinish(void); + void WaitForQueuesEmpty(void); + + int GetLoadQueueLength(void); + int GetSaveQueueLength(void); + +protected: + + struct sChunkLoad + { + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + bool m_Generate; // If true, the chunk will be generated if it cannot be loaded + + sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {} + } ; + + typedef std::list<sChunkLoad> sChunkLoadQueue; + + cWorld * m_World; + AString m_StorageSchemaName; + + // Both queues are locked by the same CS + cCriticalSection m_CSQueues; + sChunkLoadQueue m_LoadQueue; + cChunkCoordsList m_SaveQueue; + + cEvent m_Event; // Set when there's any addition to the queues + cEvent m_evtRemoved; // Set when an item has been removed from the queue, either by the worker thread or the Unqueue methods + + /// All the storage schemas (all used for loading) + cWSSchemaList m_Schemas; + + /// The one storage schema used for saving + cWSSchema * m_SaveSchema; + + void InitSchemas(void); + + virtual void Execute(void) override; + + /// Loads one chunk from the queue (if any queued); returns true if there are more chunks in the load queue + bool LoadOneChunk(void); + + /// Saves one chunk from the queue (if any queued); returns true if there are more chunks in the save queue + bool SaveOneChunk(void); +} ; + + + + + +#endif // WORLDSTORAGE_H_INCLUDED + + + + diff --git a/src/XMLParser.h b/src/XMLParser.h new file mode 100644 index 000000000..f492d1a5d --- /dev/null +++ b/src/XMLParser.h @@ -0,0 +1,701 @@ + +// XMLParser.h + +// Interfaces to the CXMLParser class representing the base class for XML parsing + +// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions + + + + + +#pragma once + +#include "expat/expat.h" + + + + + +class CXMLParser +{ +public: + CXMLParser(void); + virtual ~CXMLParser(); + + // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush) + int Parse(const char * iData, size_t iLength, bool iIsFinal = false); + +private: + // LibExpat stuff: + XML_Parser mParser; + + static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes) + { + ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes); + } + + static void EndElementHandler (void * iContext, const XML_Char * iElement) + { + ((CXMLParser *)iContext)->OnEndElement(iElement); + } + + static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength) + { + ((CXMLParser *)iContext)->OnCharacters(iData, iLength); + } + +protected: + virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0; + virtual void OnEndElement (const XML_Char * iElement) = 0; + virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The following template has been modified from code available at +// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser +// It uses templates to remove the virtual function call penalty (both size and speed) for each callback + +/* Usage: +1, Declare a subclass: + class CMyParser : public CExpatImpl<CMyParser> +2, Declare handlers that you want in that subclass: + void CMyParser::OnEndElement(const XML_Char * iTagName); +3, Create an instance of your class: + CMyParser Parser; +4, Call Create(): + Parser.Create(NULL, NULL); +4, Call Parse(), repeatedly: + Parser.Parse(Buffer, Length); +*/ + +template <class _T> +class CExpatImpl +{ + +// @access Constructors and destructors +public: + + // @cmember General constructor + + CExpatImpl () + { + m_p = NULL; + } + + // @cmember Destructor + + ~CExpatImpl () + { + Destroy (); + } + +// @access Parser creation and deletion methods +public: + + // @cmember Create a parser + + bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL) + { + // Destroy the old parser + Destroy (); + + // If the encoding or seperator are empty, then NULL + if (pszEncoding != NULL && pszEncoding [0] == 0) + { + pszEncoding = NULL; + } + if (pszSep != NULL && pszSep [0] == 0) + { + pszSep = NULL; + } + + // Create the new parser + m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep); + if (m_p == NULL) + { + return false; + } + + // Invoke the post create routine + _T * pThis = static_cast <_T *> (this); + pThis ->OnPostCreate (); + + // Set the user data used in callbacks + XML_SetUserData (m_p, (void *) this); + return true; + } + + // @cmember Destroy the parser + + void Destroy (void) + { + if (m_p != NULL) + { + XML_ParserFree (m_p); + } + m_p = NULL; + } + + + // @cmember Parse a block of data + + bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true) + { + assert (m_p != NULL); + return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0; + } + + // @cmember Parse internal buffer + + bool ParseBuffer (int nLength, bool fIsFinal = true) + { + assert (m_p != NULL); + return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0; + } + + // @cmember Get the internal buffer + + void *GetBuffer (int nLength) + { + assert (m_p != NULL); + return XML_GetBuffer (m_p, nLength); + } + + +protected: + // Parser callback enable/disable methods: + + // @cmember Enable/Disable the start element handler + + void EnableStartElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL); + } + + // @cmember Enable/Disable the end element handler + + void EnableEndElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL); + } + + // @cmember Enable/Disable the element handlers + + void EnableElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartElementHandler (fEnable); + EnableEndElementHandler (fEnable); + } + + // @cmember Enable/Disable the character data handler + + void EnableCharacterDataHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL); + } + + // @cmember Enable/Disable the processing instruction handler + + void EnableProcessingInstructionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL); + } + + // @cmember Enable/Disable the comment handler + + void EnableCommentHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL); + } + + // @cmember Enable/Disable the start CDATA section handler + + void EnableStartCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL); + } + + // @cmember Enable/Disable the end CDATA section handler + + void EnableEndCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL); + } + + // @cmember Enable/Disable the CDATA section handlers + + void EnableCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartCdataSectionHandler (fEnable); + EnableEndCdataSectionHandler (fEnable); + } + + // @cmember Enable/Disable default handler + + void EnableDefaultHandler (bool fEnable = true, bool fExpand = true) + { + assert (m_p != NULL); + if (fExpand) + { + XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL); + } + else + XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL); + } + + // @cmember Enable/Disable external entity ref handler + + void EnableExternalEntityRefHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL); + } + + // @cmember Enable/Disable unknown encoding handler + + void EnableUnknownEncodingHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL); + } + + // @cmember Enable/Disable start namespace handler + + void EnableStartNamespaceDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL); + } + + // @cmember Enable/Disable end namespace handler + + void EnableEndNamespaceDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL); + } + + // @cmember Enable/Disable namespace handlers + + void EnableNamespaceDeclHandler (bool fEnable = true) + { + EnableStartNamespaceDeclHandler (fEnable); + EnableEndNamespaceDeclHandler (fEnable); + } + + // @cmember Enable/Disable the XML declaration handler + + void EnableXmlDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL); + } + + // @cmember Enable/Disable the start DOCTYPE declaration handler + + void EnableStartDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL); + } + + // @cmember Enable/Disable the end DOCTYPE declaration handler + + void EnableEndDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndDoctypeDeclHandler (m_p, + fEnable ? EndDoctypeDeclHandler : NULL); + } + + // @cmember Enable/Disable the DOCTYPE declaration handler + + void EnableDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartDoctypeDeclHandler (fEnable); + EnableEndDoctypeDeclHandler (fEnable); + } + +public: + // Parser error reporting methods + + // @cmember Get last error + + enum XML_Error GetErrorCode () + { + assert (m_p != NULL); + return XML_GetErrorCode (m_p); + } + + // @cmember Get the current byte index + + long GetCurrentByteIndex () + { + assert (m_p != NULL); + return XML_GetCurrentByteIndex (m_p); + } + + // @cmember Get the current line number + + int GetCurrentLineNumber () + { + assert (m_p != NULL); + return XML_GetCurrentLineNumber (m_p); + } + + // @cmember Get the current column number + + int GetCurrentColumnNumber () + { + assert (m_p != NULL); + return XML_GetCurrentColumnNumber (m_p); + } + + // @cmember Get the current byte count + + int GetCurrentByteCount () + { + assert (m_p != NULL); + return XML_GetCurrentByteCount (m_p); + } + + // @cmember Get the input context + + const char *GetInputContext (int *pnOffset, int *pnSize) + { + assert (m_p != NULL); + return XML_GetInputContext (m_p, pnOffset, pnSize); + } + + // @cmember Get last error string + + const XML_LChar *GetErrorString () + { + return XML_ErrorString (GetErrorCode ()); + } + + // @cmember Return the version string + + static const XML_LChar *GetExpatVersion () + { + return XML_ExpatVersion (); + } + + // @cmember Get the version information + + static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro) + { + XML_expat_version v = XML_ExpatVersionInfo (); + if (pnMajor) + *pnMajor = v .major; + if (pnMinor) + *pnMinor = v .minor; + if (pnMicro) + *pnMicro = v .micro; + } + + // @cmember Get last error string + + static const XML_LChar *GetErrorString (enum XML_Error nError) + { + return XML_ErrorString (nError); + } + + + // Public handler methods: + // The template parameter should provide their own implementation for those handlers that they want + + // @cmember Start element handler + + void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs) + { + return; + } + + // @cmember End element handler + + void OnEndElement (const XML_Char *pszName) + { + return; + } + + // @cmember Character data handler + + void OnCharacterData (const XML_Char *pszData, int nLength) + { + return; + } + + // @cmember Processing instruction handler + + void OnProcessingInstruction (const XML_Char *pszTarget, + const XML_Char *pszData) + { + return; + } + + // @cmember Comment handler + + void OnComment (const XML_Char *pszData) + { + return; + } + + // @cmember Start CDATA section handler + + void OnStartCdataSection () + { + return; + } + + // @cmember End CDATA section handler + + void OnEndCdataSection () + { + return; + } + + // @cmember Default handler + + void OnDefault (const XML_Char *pszData, int nLength) + { + return; + } + + // @cmember External entity ref handler + + bool OnExternalEntityRef (const XML_Char *pszContext, + const XML_Char *pszBase, const XML_Char *pszSystemID, + const XML_Char *pszPublicID) + { + return false; + } + + // @cmember Unknown encoding handler + + bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo) + { + return false; + } + + // @cmember Start namespace declaration handler + + void OnStartNamespaceDecl (const XML_Char *pszPrefix, + const XML_Char *pszURI) + { + return; + } + + // @cmember End namespace declaration handler + + void OnEndNamespaceDecl (const XML_Char *pszPrefix) + { + return; + } + + // @cmember XML declaration handler + + void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding, + bool fStandalone) + { + return; + } + + // @cmember Start DOCTYPE declaration handler + + void OnStartDoctypeDecl (const XML_Char *pszDoctypeName, + const XML_Char *pszSysID, const XML_Char *pszPubID, + bool fHasInternalSubset) + { + return; + } + + // @cmember End DOCTYPE declaration handler + + void OnEndDoctypeDecl () + { + return; + } + +// @access Protected methods +protected: + + // @cmember Handle any post creation + + void OnPostCreate () + { + } + +// @access Protected static methods +protected: + + // @cmember Start element handler wrapper + + static void __cdecl StartElementHandler (void *pUserData, + const XML_Char *pszName, const XML_Char **papszAttrs) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartElement (pszName, papszAttrs); + } + + // @cmember End element handler wrapper + + static void __cdecl EndElementHandler (void *pUserData, + const XML_Char *pszName) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndElement (pszName); + } + + // @cmember Character data handler wrapper + + static void __cdecl CharacterDataHandler (void *pUserData, + const XML_Char *pszData, int nLength) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnCharacterData (pszData, nLength); + } + + // @cmember Processing instruction handler wrapper + + static void __cdecl ProcessingInstructionHandler (void *pUserData, + const XML_Char *pszTarget, const XML_Char *pszData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnProcessingInstruction (pszTarget, pszData); + } + + // @cmember Comment handler wrapper + + static void __cdecl CommentHandler (void *pUserData, + const XML_Char *pszData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnComment (pszData); + } + + // @cmember Start CDATA section wrapper + + static void __cdecl StartCdataSectionHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartCdataSection (); + } + + // @cmember End CDATA section wrapper + + static void __cdecl EndCdataSectionHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndCdataSection (); + } + + // @cmember Default wrapper + + static void __cdecl DefaultHandler (void *pUserData, + const XML_Char *pszData, int nLength) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnDefault (pszData, nLength); + } + + // @cmember External entity ref wrapper + + static int __cdecl ExternalEntityRefHandler (void *pUserData, + const XML_Char *pszContext, const XML_Char *pszBase, + const XML_Char *pszSystemID, const XML_Char *pszPublicID) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + return pThis ->OnExternalEntityRef (pszContext, + pszBase, pszSystemID, pszPublicID) ? 1 : 0; + } + + // @cmember Unknown encoding wrapper + + static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0; + } + + // @cmember Start namespace decl wrapper + + static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartNamespaceDecl (pszPrefix, pszURI); + } + + // @cmember End namespace decl wrapper + + static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndNamespaceDecl (pszPrefix); + } + + // @cmember XML declaration wrapper + + static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0); + } + + // @cmember Start Doctype declaration wrapper + + static void __cdecl StartDoctypeDeclHandler ( + void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID, + const XML_Char *pszPubID, int nHasInternalSubset + ) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID, + pszPubID, nHasInternalSubset != 0); + } + + // @cmember End Doctype declaration wrapper + + static void __cdecl EndDoctypeDeclHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndDoctypeDecl (); + } + + +protected: + + XML_Parser m_p; + + /// Returns the value of the specified attribute, if found; NULL otherwise + static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind) + { + for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2) + { + if (strcmp(*Attr, iAttrToFind) == 0) + { + return *(Attr + 1); + } + } // for Attr - iAttrs[] + return NULL; + } +} ; + + + + diff --git a/src/lua5.1.dll b/src/lua5.1.dll Binary files differnew file mode 100644 index 000000000..515cf8b30 --- /dev/null +++ b/src/lua5.1.dll diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..1f6aad24f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,197 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Root.h" + +#include <exception> //std::exception +#include <csignal> //std::signal +#include <stdlib.h> //exit() + +#ifdef _MSC_VER + #include <dbghelp.h> +#endif // _MSC_VER + + + + + +/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window +#define ENABLE_LEAK_FINDER + + + + + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + #pragma warning(push) + #pragma warning(disable:4100) + #include "LeakFinder.h" + #pragma warning(pop) +#endif + + + + + + +void ShowCrashReport(int) +{ + std::signal(SIGSEGV, SIG_DFL); + + printf("\n\nMCServer has crashed!\n"); + + exit(-1); +} + + + + + +#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Windows 32-bit stuff: when the server crashes, create a "dump file" containing the callstack of each thread and some variables; let the user send us that crash file for analysis + +typedef BOOL (WINAPI *pMiniDumpWriteDump)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + PMINIDUMP_CALLBACK_INFORMATION CallbackParam +); + +pMiniDumpWriteDump g_WriteMiniDump; // The function in dbghlp DLL that creates dump files + +char g_DumpFileName[MAX_PATH]; // Filename of the dump file; hes to be created before the dump handler kicks in +char g_ExceptionStack[128 * 1024]; // Substitute stack, just in case the handler kicks in because of "insufficient stack space" +MINIDUMP_TYPE g_DumpFlags = MiniDumpNormal; // By default dump only the stack and some helpers + + + + + +/** This function gets called just before the "program executed an illegal instruction and will be terminated" or similar. +Its purpose is to create the crashdump using the dbghlp DLLs +*/ +LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo) +{ + char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)]; + char * oldStack; + + // Use the substitute stack: + // This code is the reason why we don't support 64-bit (yet) + _asm + { + mov oldStack, esp + mov esp, newStack + } + + MINIDUMP_EXCEPTION_INFORMATION ExcInformation; + ExcInformation.ThreadId = GetCurrentThreadId(); + ExcInformation.ExceptionPointers = a_ExceptionInfo; + ExcInformation.ClientPointers = 0; + + // Write the dump file: + HANDLE dumpFile = CreateFile(g_DumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + g_WriteMiniDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, g_DumpFlags, (a_ExceptionInfo) ? &ExcInformation : NULL, NULL, NULL); + CloseHandle(dumpFile); + + // Revert to old stack: + _asm + { + mov esp, oldStack + } + + return 0; +} + +#endif // _WIN32 && !_WIN64 + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// main: + +int main( int argc, char **argv ) +{ + (void)argc; + (void)argv; + + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + InitLeakFinder(); + #endif + + // Magic code to produce dump-files on Windows if the server crashes: + #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) + HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL"); + g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); + if (g_WriteMiniDump != NULL) + { + _snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId()); + SetUnhandledExceptionFilter(LastChanceExceptionFilter); + + // Parse arguments for minidump flags: + for (int i = 0; i < argc; i++) + { + if (_stricmp(argv[i], "/cdg") == 0) + { + // Add globals to the dump + g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs); + } + else if (_stricmp(argv[i], "/cdf") == 0) + { + // Add full memory to the dump (HUUUGE file) + g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory); + } + } // for i - argv[] + } + #endif // _WIN32 && !_WIN64 + // End of dump-file magic + + #if defined(_DEBUG) && defined(_MSC_VER) + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + + // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output) + // Only useful when the leak is in the same sequence all the time + // _CrtSetBreakAlloc(85950); + + #endif // _DEBUG && _MSC_VER + + #ifndef _DEBUG + std::signal(SIGSEGV, ShowCrashReport); + #endif + + // DEBUG: test the dumpfile creation: + // *((int *)0) = 0; + + #if !defined(ANDROID_NDK) + try + #endif + { + cRoot Root; + Root.Start(); + } + #if !defined(ANDROID_NDK) + catch( std::exception& e ) + { + LOGERROR("Standard exception: %s", e.what() ); + } + catch( ... ) + { + LOGERROR("Unknown exception!"); + } + #endif + + + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + DeinitLeakFinder(); + #endif + + return 0; +} + + + + diff --git a/src/tolua++.exe b/src/tolua++.exe Binary files differnew file mode 100644 index 000000000..e5cec6d78 --- /dev/null +++ b/src/tolua++.exe diff --git a/src/tolua++.h b/src/tolua++.h new file mode 100644 index 000000000..8da427fe3 --- /dev/null +++ b/src/tolua++.h @@ -0,0 +1,186 @@ +/* tolua +** Support code for Lua bindings. +** Written by Waldemar Celes +** TeCGraf/PUC-Rio +** Apr 2003 +** $Id: $ +*/ + +/* This code is free software; you can redistribute it and/or modify it. +** The software provided hereunder is on an "as is" basis, and +** the author has no obligation to provide maintenance, support, updates, +** enhancements, or modifications. +*/ + + +#ifndef TOLUA_H +#define TOLUA_H + +#ifndef TOLUA_API +#define TOLUA_API extern +#endif + +#define TOLUA_VERSION "tolua++-1.0.92" + +#ifdef __cplusplus +extern "C" { +#endif + +#define tolua_pushcppstring(x,y) tolua_pushstring(x,y.c_str()) +#define tolua_iscppstring tolua_isstring + +#define tolua_iscppstringarray tolua_isstringarray +#define tolua_pushfieldcppstring(L,lo,idx,s) tolua_pushfieldstring(L, lo, idx, s.c_str()) + +#ifndef TEMPLATE_BIND + #define TEMPLATE_BIND(p) +#endif + +#define TOLUA_TEMPLATE_BIND(p) + +#define TOLUA_PROTECTED_DESTRUCTOR +#define TOLUA_PROPERTY_TYPE(p) + +typedef int lua_Object; + +#include "lua/src/lua.h" +#include "lua/src/lauxlib.h" + +struct tolua_Error +{ + int index; + int array; + const char* type; +}; +typedef struct tolua_Error tolua_Error; + +#define TOLUA_NOPEER LUA_REGISTRYINDEX /* for lua 5.1 */ + +TOLUA_API const char* tolua_typename (lua_State* L, int lo); +TOLUA_API void tolua_error (lua_State* L, const char* msg, tolua_Error* err); +TOLUA_API int tolua_isnoobj (lua_State* L, int lo, tolua_Error* err); +TOLUA_API int tolua_isvalue (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isvaluenil (lua_State* L, int lo, tolua_Error* err); +TOLUA_API int tolua_isboolean (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isnumber (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isstring (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_istable (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isusertable (lua_State* L, int lo, const char* type, int def, tolua_Error* err); +TOLUA_API int tolua_isuserdata (lua_State* L, int lo, int def, tolua_Error* err); +TOLUA_API int tolua_isusertype (lua_State* L, int lo, const char* type, int def, tolua_Error* err); +TOLUA_API int tolua_isvaluearray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isbooleanarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isnumberarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isstringarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_istablearray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isuserdataarray + (lua_State* L, int lo, int dim, int def, tolua_Error* err); +TOLUA_API int tolua_isusertypearray + (lua_State* L, int lo, const char* type, int dim, int def, tolua_Error* err); + +TOLUA_API void tolua_open (lua_State* L); + +TOLUA_API void* tolua_copy (lua_State* L, void* value, unsigned int size); +TOLUA_API int tolua_register_gc (lua_State* L, int lo); +TOLUA_API int tolua_default_collect (lua_State* tolua_S); + +TOLUA_API void tolua_usertype (lua_State* L, const char* type); +TOLUA_API void tolua_beginmodule (lua_State* L, const char* name); +TOLUA_API void tolua_endmodule (lua_State* L); +TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar); +TOLUA_API void tolua_class (lua_State* L, const char* name, const char* base); +TOLUA_API void tolua_cclass (lua_State* L, const char* lname, const char* name, const char* base, lua_CFunction col); +TOLUA_API void tolua_function (lua_State* L, const char* name, lua_CFunction func); +TOLUA_API void tolua_constant (lua_State* L, const char* name, lua_Number value); +TOLUA_API void tolua_variable (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set); +TOLUA_API void tolua_array (lua_State* L,const char* name, lua_CFunction get, lua_CFunction set); + +/* TOLUA_API void tolua_set_call_event(lua_State* L, lua_CFunction func, char* type); */ +/* TOLUA_API void tolua_addbase(lua_State* L, char* name, char* base); */ + +TOLUA_API void tolua_pushvalue (lua_State* L, int lo); +TOLUA_API void tolua_pushboolean (lua_State* L, int value); +TOLUA_API void tolua_pushnumber (lua_State* L, lua_Number value); +TOLUA_API void tolua_pushstring (lua_State* L, const char* value); +TOLUA_API void tolua_pushuserdata (lua_State* L, void* value); +TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type); +TOLUA_API void tolua_pushusertype_and_takeownership(lua_State* L, void* value, const char* type); +TOLUA_API void tolua_pushfieldvalue (lua_State* L, int lo, int index, int v); +TOLUA_API void tolua_pushfieldboolean (lua_State* L, int lo, int index, int v); +TOLUA_API void tolua_pushfieldnumber (lua_State* L, int lo, int index, lua_Number v); +TOLUA_API void tolua_pushfieldstring (lua_State* L, int lo, int index, const char* v); +TOLUA_API void tolua_pushfielduserdata (lua_State* L, int lo, int index, void* v); +TOLUA_API void tolua_pushfieldusertype (lua_State* L, int lo, int index, void* v, const char* type); +TOLUA_API void tolua_pushfieldusertype_and_takeownership (lua_State* L, int lo, int index, void* v, const char* type); + +TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def); +TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def); +TOLUA_API void* tolua_touserdata (lua_State* L, int narg, void* def); +TOLUA_API void* tolua_tousertype (lua_State* L, int narg, void* def); +TOLUA_API int tolua_tovalue (lua_State* L, int narg, int def); +TOLUA_API int tolua_toboolean (lua_State* L, int narg, int def); +TOLUA_API lua_Number tolua_tofieldnumber (lua_State* L, int lo, int index, lua_Number def); +TOLUA_API const char* tolua_tofieldstring (lua_State* L, int lo, int index, const char* def); +TOLUA_API void* tolua_tofielduserdata (lua_State* L, int lo, int index, void* def); +TOLUA_API void* tolua_tofieldusertype (lua_State* L, int lo, int index, void* def); +TOLUA_API int tolua_tofieldvalue (lua_State* L, int lo, int index, int def); +TOLUA_API int tolua_getfieldboolean (lua_State* L, int lo, int index, int def); + +TOLUA_API void tolua_dobuffer(lua_State* L, char* B, unsigned int size, const char* name); + +TOLUA_API int class_gc_event (lua_State* L); + +#ifdef __cplusplus +static inline const char* tolua_tocppstring (lua_State* L, int narg, const char* def) { + + const char* s = tolua_tostring(L, narg, def); + return s?s:""; +}; + +static inline const char* tolua_tofieldcppstring (lua_State* L, int lo, int index, const char* def) { + + const char* s = tolua_tofieldstring(L, lo, index, def); + return s?s:""; +}; + +#else +#define tolua_tocppstring tolua_tostring +#define tolua_tofieldcppstring tolua_tofieldstring +#endif + +TOLUA_API int tolua_fast_isa(lua_State *L, int mt_indexa, int mt_indexb, int super_index); + +#ifndef Mtolua_new +#define Mtolua_new(EXP) new EXP +#endif + +#ifndef Mtolua_delete +#define Mtolua_delete(EXP) delete EXP +#endif + +#ifndef Mtolua_new_dim +#define Mtolua_new_dim(EXP, len) new EXP[len] +#endif + +#ifndef Mtolua_delete_dim +#define Mtolua_delete_dim(EXP) delete [] EXP +#endif + +#ifndef tolua_outside +#define tolua_outside +#endif + +#ifndef tolua_owned +#define tolua_owned +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tolua_base.h b/src/tolua_base.h new file mode 100644 index 000000000..4f1038c09 --- /dev/null +++ b/src/tolua_base.h @@ -0,0 +1,128 @@ +#ifndef TOLUA_BASE_H +#define TOLUA_BASE_H + +#pragma warning(disable:4800) // This file is ONLY included by Bindings.cpp and it throws lots of C4800 warnings + +#include "tolua++.h" + + + + + +class ToluaBase { + + int lua_instance; + +protected: + + lua_State* lua_state; + + void lua_stacktrace(lua_State* L) const + { + lua_Debug entry; + int depth = 0; + + while (lua_getstack(L, depth, &entry)) + { + lua_getinfo(L, "Sln", &entry); + + LOGERROR("%s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?"); + depth++; + } + } + + + bool report_errors(int status) const + { + if ( status!=0 ) + { + const char* s = lua_tostring(lua_state, -1); + LOGERROR("-- %s", s ); + //lua_pop(lua_state, 1); + LOGERROR("Stack:"); + lua_stacktrace( lua_state ); + return true; + } + return false; + } + + bool push_method(const char* name, lua_CFunction f) const { + + if (!lua_state) return false; + + lua_getref(lua_state, lua_instance); + lua_pushstring(lua_state, name); + //LOGINFO("1. push_method() Stack size: %i", lua_gettop( lua_state ) ); + lua_gettable(lua_state, -2); + //LOGINFO("2. push_method() Stack size: %i", lua_gettop( lua_state ) ); + + if (lua_isnil(lua_state, -1)) { + + // pop the table + lua_pop(lua_state, 2); + return false; + + } else { + + if (f) { + if (lua_iscfunction(lua_state, -1)) { + lua_pop(lua_state, 2); + return false; + }; + /* // not for now + lua_pushcfunction(lua_state, f); + if (lua_rawequal(lua_state, -1, -2)) { + + // avoid recursion, pop both functions and the table + lua_pop(lua_state, 3); + return false; + }; + + // pop f + lua_pop(lua_state, 1); + */ + }; + + // swap table with function + lua_insert(lua_state, -2); + }; + + return true; + }; + + void dbcall(lua_State* L, int nargs, int nresults) const { + + // using lua_call for now + int s = lua_pcall(L, nargs, nresults, 0); + report_errors( s ); + }; +public: + + int GetInstance() { return lua_instance; } + lua_State* GetLuaState() { return lua_state; } + + void tolua__set_instance(lua_State* L, lua_Object lo) { + + lua_state = L; + + lua_pushvalue(L, lo); + lua_instance = lua_ref(lua_state, 1); + }; + + ToluaBase() { + + lua_state = NULL; + }; + + ~ToluaBase() { + + if (lua_state) { + + lua_unref(lua_state, lua_instance); + }; + }; +}; + +#endif + + diff --git a/src/virtual_method_hooks.lua b/src/virtual_method_hooks.lua new file mode 100644 index 000000000..15ff1d7f8 --- /dev/null +++ b/src/virtual_method_hooks.lua @@ -0,0 +1,506 @@ +-- flags +local disable_virtual_hooks = true +local enable_pure_virtual = true +local default_private_access = false + +local access = {public = 0, protected = 1, private = 2} + +function preparse_hook(p) + + if default_private_access then + -- we need to make all structs 'public' by default + p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") + end +end + + +function parser_hook(s) + + local container = classContainer.curr -- get the current container + + if default_private_access then + if not container.curr_member_access and container.classtype == 'class' then + -- default access for classes is private + container.curr_member_access = access.private + end + end + + -- try labels (public, private, etc) + do + local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' + if b then + + -- found a label, get the new access value from the global 'access' table + if access[label] then + container.curr_member_access = access[label] + end -- else ? + + return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] + end + end + + + local ret = nil + + if disable_virtual_hooks then + + return ret + end + + local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") + local const + if b then + local ret = string.sub(s, e+1) + if string.find(ret, "^%s*const") then + const = "const" + ret = string.gsub(ret, "^%s*const", "") + end + local purev = false + if string.find(ret, "^%s*=%s*0") then + purev = true + ret = string.gsub(ret, "^%s*=%s*0", "") + end + ret = string.gsub(ret, "^%s*%b{}", "") + + local func = Function(decl, arg, const) + func.pure_virtual = purev + --func.access = access + func.original_sig = decl + + local curflags = classContainer.curr.flags + if not curflags.virtual_class then + + curflags.virtual_class = VirtualClass() + end + curflags.virtual_class:add(func) + curflags.pure_virtual = curflags.pure_virtual or purev + + return ret + end + + return ret +end + + +-- class VirtualClass +classVirtualClass = { + classtype = 'class', + name = '', + base = '', + type = '', + btype = '', + ctype = '', +} +classVirtualClass.__index = classVirtualClass +setmetatable(classVirtualClass,classClass) + +function classVirtualClass:add(f) + + local parent = classContainer.curr + pop() + + table.insert(self.methods, {f=f}) + + local name,sig + + -- doble negative means positive + if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then + + name = self.original_name + elseif f.name == 'delete' then + name = '~'..self.original_name + else + if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then + name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name + end + end + + if name then + sig = name..self:get_arg_list(f, true)..";\n" + push(self) + sig = preprocess(sig) + self:parse(sig) + pop() + end + + push(parent) +end + +function preprocess(sig) + + sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' + sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' + sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' + sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' + + return sig +end + +function classVirtualClass:get_arg_list(f, decl) + + local ret = "" + local sep = "" + local i=1 + while f.args[i] do + + local arg = f.args[i] + if decl then + local ptr + if arg.ret ~= '' then + ptr = arg.ret + else + ptr = arg.ptr + end + local def = "" + if arg.def and arg.def ~= "" then + + def = " = "..arg.def + end + ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def + else + ret = ret..sep..arg.name + end + + sep = "," + i = i+1 + end + + return "("..ret..")" +end + +function classVirtualClass:add_parent_virtual_methods(parent) + + parent = parent or _global_classes[self.flags.parent_object.btype] + + if not parent then return end + + if parent.flags.virtual_class then + + local vclass = parent.flags.virtual_class + for k,v in ipairs(vclass.methods) do + if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then + table.insert(self.methods, {f=v.f}) + end + end + end + + parent = _global_classes[parent.btype] + if parent then + self:add_parent_virtual_methods(parent) + end +end + +function classVirtualClass:has_method(f) + + for k,v in pairs(self.methods) do + -- just match name for now + if v.f.name == f.name then + return true + end + end + + return false +end + +function classVirtualClass:add_constructors() + + local i=1 + while self.flags.parent_object[i] do + + local v = self.flags.parent_object[i] + if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then + + self:add(v) + end + + i = i+1 + end + +end + +--[[ +function classVirtualClass:requirecollection(t) + + self:add_constructors() + local req = classClass.requirecollection(self, t) + if req then + output('class ',self.name,";") + end + return req +end +--]] + +function classVirtualClass:supcode() + + -- pure virtual classes can have no default constructors on gcc 4 + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') + end + + local ns + if self.prox.classtype == 'namespace' then + output('namespace ',self.prox.name, " {") + ns = true + end + + output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") + + output("public:\n") + + self:add_parent_virtual_methods() + + self:output_methods(self.btype) + self:output_parent_methods() + + self:add_constructors() + + -- no constructor for pure virtual classes + if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then + + self:output_constructors() + end + + output("};\n\n") + + if ns then + output("};") + end + + classClass.supcode(self) + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#endif // __GNUC__ >= 4\n') + end + + -- output collector for custom class if required + if self:requirecollection(_collect) and _collect[self.type] then + + output('\n') + output('/* function to release collected object via destructor */') + output('#ifdef __cplusplus\n') + --for i,v in pairs(collect) do + i,v = self.type, _collect[self.type] + output('\nstatic int '..v..' (lua_State* tolua_S)') + output('{') + output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') + output(' delete self;') + output(' return 0;') + output('}') + --end + output('#endif\n\n') + end + +end + +function classVirtualClass:register(pre) + + -- pure virtual classes can have no default constructors on gcc 4 + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') + end + + classClass.register(self, pre) + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#endif // __GNUC__ >= 4\n') + end +end + + +--function classVirtualClass:requirecollection(_c) +-- if self.flags.parent_object.flags.pure_virtual then +-- return false +-- end +-- return classClass.requirecollection(self, _c) +--end + +function classVirtualClass:output_parent_methods() + + for k,v in ipairs(self.methods) do + + if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then + + local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " + local parent_name = rettype..self.btype.."__"..v.f.name + + local par_list = self:get_arg_list(v.f, true) + local var_list = self:get_arg_list(v.f, false) + + -- the parent's virtual function + output("\t"..parent_name..par_list.." {") + + output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") + output("\t};") + end + end +end + +function classVirtualClass:output_methods(btype) + + for k,v in ipairs(self.methods) do + + if v.f.name ~= 'new' and v.f.name ~= 'delete' then + + self:output_method(v.f, btype) + end + end + output("\n") +end + +function classVirtualClass:output_constructors() + + for k,v in ipairs(self.methods) do + + if v.f.name == 'new' then + + local par_list = self:get_arg_list(v.f, true) + local var_list = self:get_arg_list(v.f, false) + + output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") + end + end +end + +function classVirtualClass:output_method(f, btype) + + if f.access == 2 then -- private + return + end + + local ptr + if f.ret ~= '' then + ptr = f.ret + else + ptr = f.ptr + end + + local rettype = f.mod.." "..f.type..f.ptr.." " + local par_list = self:get_arg_list(f, true) + local var_list = self:get_arg_list(f, false) + + if string.find(rettype, "%s*LuaQtGenericFlags%s*") then + + _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") + end + + -- the caller of the lua method + output("\t"..rettype.." "..f.name..par_list..f.const.." {") + local fn = f.cname + if f.access == 1 then + fn = "NULL" + end + output('\t\tif (push_method("',f.lname,'", ',fn,')) {') + + --if f.type ~= 'void' then + -- output("\t\t\tint top = lua_gettop(lua_state)-1;") + --end + + -- push the parameters + local argn = 0 + for i,arg in ipairs(f.args) do + if arg.type ~= 'void' then + local t,ct = isbasic(arg.type) + if t and t ~= '' then + if arg.ret == "*" then + t = 'userdata' + ct = 'void*' + end + output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); + else + local m = arg.ptr + if m and m~= "" then + if m == "*" then m = "" end + output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") + else + output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") + output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') + end + end + argn = argn+1 + end + end + + -- call the function + output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") + + -- return value + if f.type ~= 'void' then + output("1);") + + local t,ct = isbasic(f.type) + if t and t ~= '' then + --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") + output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") + else + + local mod = "" + if f.ptr ~= "*" then + mod = "*("..f.type.."*)" + end + + --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") + output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") + end + output("\t\t\tlua_pop(lua_state, 1);") + output("\t\t\treturn tolua_ret;") + else + output("0);") + end + + -- handle non-implemeted function + output("\t\t} else {") + + if f.pure_virtual then + + output('\t\t\tif (lua_state)') + --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') + output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') + output('\t\t\telse {') + output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') + output('\t\t\t\t::abort();') + output('\t\t\t};') + if( rettype == " std::string " ) then + output('\t\t\treturn "";') + else + output('\t\t\treturn (',rettype,')0;') + end + else + + output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') + end + + output("\t\t};") + + output("\t};") +end + +function VirtualClass() + + local parent = classContainer.curr + pop() + + local name = "Lua__"..parent.original_name + + local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) + setmetatable(c, classVirtualClass) + + local ft = getnamespace(c.parent)..c.original_name + append_global_type(ft, c) + + push(parent) + + c.flags.parent_object = parent + c.methods = {} + + push(c) + c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") + pop() + + return c +end + + + + + |