1
0

Merge pull request #48 from mc-server/BlockTracing

Block tracing
This commit is contained in:
madmaxoft 2013-08-07 06:46:43 -07:00
commit 3d027a8928
27 changed files with 2142 additions and 847 deletions

8
MCServer/.gitignore vendored
View File

@ -1,11 +1,8 @@
*.exe
ChunkWorx.ini
ChunkWorxSave.ini
*.ini
MCServer
banned.ini
logs
players
whitelist.ini
world*
API.txt
*.dat
@ -15,9 +12,6 @@ schematics
*.pdb
memdump.xml
*.grab
ProtectionAreas.ini
ProtectionAreas.sqlite
helgrind.log
settings.ini
webadmin.ini
motd.txt

View File

@ -23,17 +23,18 @@ function Initialize(Plugin)
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHAT);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
PluginManager:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "Shows a list of all the loaded entities");
PluginManager:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "Kills all the loaded entities");
PluginManager:BindCommand("/wool", "debuggers", HandleWoolCmd, "Sets all your armor to blue wool");
PluginManager:BindCommand("/testwnd", "debuggers", HandleTestWndCmd, "Opens up a window using plugin API");
PluginManager:BindCommand("/gc", "debuggers", HandleGCCmd, "Activates the Lua garbage collector");
PluginManager:BindCommand("/fast", "debuggers", HandleFastCmd, "Switches between fast and normal movement speed");
PluginManager:BindCommand("/dash", "debuggers", HandleDashCmd, "Switches between fast and normal sprinting speed");
PluginManager:BindCommand("/hunger", "debuggers", HandleHungerCmd, "Lists the current hunger-related variables");
PluginManager:BindCommand("/poison", "debuggers", HandlePoisonCmd, "Sets food-poisoning for 15 seconds");
PluginManager:BindCommand("/starve", "debuggers", HandleStarveCmd, "Sets the food level to zero");
PluginManager:BindCommand("/fl", "debuggers", HandleFoodLevelCmd, "Sets the food level to the given value");
PluginManager:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities");
PluginManager:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities");
PluginManager:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool");
PluginManager:BindCommand("/testwnd", "debuggers", HandleTestWndCmd, "- Opens up a window using plugin API");
PluginManager:BindCommand("/gc", "debuggers", HandleGCCmd, "- Activates the Lua garbage collector");
PluginManager:BindCommand("/fast", "debuggers", HandleFastCmd, "- Switches between fast and normal movement speed");
PluginManager:BindCommand("/dash", "debuggers", HandleDashCmd, "- Switches between fast and normal sprinting speed");
PluginManager:BindCommand("/hunger", "debuggers", HandleHungerCmd, "- Lists the current hunger-related variables");
PluginManager:BindCommand("/poison", "debuggers", HandlePoisonCmd, "- Sets food-poisoning for 15 seconds");
PluginManager:BindCommand("/starve", "debuggers", HandleStarveCmd, "- Sets the food level to zero");
PluginManager:BindCommand("/fl", "debuggers", HandleFoodLevelCmd, "- Sets the food level to the given value");
PluginManager:BindCommand("/spidey", "debuggers", HandleSpideyCmd, "- Shoots a line of web blocks until it hits non-air");
-- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
@ -707,3 +708,34 @@ end
function HandleSpideyCmd(a_Split, a_Player)
-- Place a line of cobwebs from the player's eyes until non-air block, in the line-of-sight of the player
local World = a_Player:GetWorld();
local Callbacks = {
OnNextBlock = function(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta)
if (a_BlockType ~= E_BLOCK_AIR) then
-- abort the trace
return true;
end
World:SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COBWEB, 0);
end
};
local EyePos = a_Player:GetEyePosition();
local LookVector = a_Player:GetLookVector();
LookVector:Normalize();
-- Start cca 2 blocks away from the eyes
local Start = EyePos + LookVector + LookVector;
local End = EyePos + LookVector * 50;
cLineBlockTracer.Trace(World, Callbacks, Start.x, Start.y, Start.z, End.x, End.y, End.z);
return true;
end

View File

@ -4,6 +4,10 @@ function Output(String)
table.insert(SiteContent, String)
end
function GetTableSize(Table)
local Size = 0
for key,value in pairs(Table) do
@ -12,6 +16,10 @@ function GetTableSize(Table)
return Size
end
function GetDefaultPage()
local PM = cRoot:Get():GetPluginManager()
@ -42,11 +50,15 @@ function GetDefaultPage()
return Content, SubTitle
end
function ShowPage(WebAdmin, TemplateRequest)
SiteContent = {}
local BaseURL = WebAdmin:GetBaseURL(TemplateRequest.Request.Path)
local Title = "MCServer"
local MemoryUsage = WebAdmin:GetMemoryUsage()
local MemoryUsage = cWebAdmin:GetMemoryUsage()
local NumChunks = cRoot:Get():GetTotalChunkCount()
local PluginPage = WebAdmin:GetPage(TemplateRequest.Request)
local PageContent = PluginPage.Content
@ -408,6 +420,7 @@ function ShowPage(WebAdmin, TemplateRequest)
<div id="sidebar">
<ul class="sideNav">
]])
local AllPlugins = WebAdmin:GetPlugins()
for key,value in pairs(AllPlugins) do
@ -421,6 +434,7 @@ function ShowPage(WebAdmin, TemplateRequest)
end
end
end
Output([[
</ul>

View File

@ -306,6 +306,10 @@
RelativePath="..\source\BlockID.h"
>
</File>
<File
RelativePath="..\source\BlockTracer.h"
>
</File>
<File
RelativePath="..\source\ByteBuffer.cpp"
>
@ -545,6 +549,14 @@
RelativePath="..\source\LinearUpscale.h"
>
</File>
<File
RelativePath="..\source\LineBlockTracer.cpp"
>
</File>
<File
RelativePath="..\source\LineBlockTracer.h"
>
</File>
<File
RelativePath="..\source\Log.cpp"
>
@ -1485,6 +1497,14 @@
RelativePath="..\source\LuaScript.h"
>
</File>
<File
RelativePath="..\source\LuaState.cpp"
>
</File>
<File
RelativePath="..\source\LuaState.h"
>
</File>
<File
RelativePath="..\source\LuaWindow.cpp"
>

View File

@ -154,7 +154,12 @@ public:
return SetValueI( keyname, valuename, int(value), create);
}
bool SetValueF( const std::string & keyname, const std::string & valuename, const double value, const bool create = true);
// tolua_end
bool SetValueV( const std::string & keyname, const std::string & valuename, char *format, ...);
// tolua_begin
// Deletes specified value.
// Returns true if value existed and deleted, false otherwise.

View File

@ -63,3 +63,11 @@ $cfile "LuaWindow.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;

View File

@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 08/02/13 08:41:18.
** Generated automatically by tolua++-1.0.92 on 08/07/13 12:06:03.
*/
#ifndef __cplusplus
@ -202,8 +202,11 @@ static int tolua_collect_Vector3d (lua_State* tolua_S)
static void tolua_reg_types (lua_State* tolua_S)
{
tolua_usertype(tolua_S,"TakeDamageInfo");
tolua_usertype(tolua_S,"cCraftingRecipe");
tolua_usertype(tolua_S,"cPlugin_NewLua");
tolua_usertype(tolua_S,"cCraftingGrid");
tolua_usertype(tolua_S,"cCraftingRecipe");
tolua_usertype(tolua_S,"cPlugin");
tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cStringMap");
tolua_usertype(tolua_S,"cItemGrid");
tolua_usertype(tolua_S,"cBlockArea");
@ -211,42 +214,42 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cLuaWindow");
tolua_usertype(tolua_S,"cInventory");
tolua_usertype(tolua_S,"cRoot");
tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cCraftingGrid");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cPickup");
tolua_usertype(tolua_S,"cItems");
tolua_usertype(tolua_S,"cCuboid");
tolua_usertype(tolua_S,"std::vector<cIniFile::key>");
tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cPickup");
tolua_usertype(tolua_S,"std::vector<std::string>");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cClientHandle");
tolua_usertype(tolua_S,"cChunkDesc");
tolua_usertype(tolua_S,"cFurnaceRecipe");
tolua_usertype(tolua_S,"cCuboid");
tolua_usertype(tolua_S,"cChatColor");
tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cEntity");
tolua_usertype(tolua_S,"cChatColor");
tolua_usertype(tolua_S,"cWorld");
tolua_usertype(tolua_S,"Lua__cPickup");
tolua_usertype(tolua_S,"Lua__cWebPlugin");
tolua_usertype(tolua_S,"cPlugin");
tolua_usertype(tolua_S,"cCraftingRecipes");
tolua_usertype(tolua_S,"cEntity");
tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"Vector3f");
tolua_usertype(tolua_S,"Lua__cPickup");
tolua_usertype(tolua_S,"cWebPlugin");
tolua_usertype(tolua_S,"cDropSpenserEntity");
tolua_usertype(tolua_S,"Lua__cPlayer");
tolua_usertype(tolua_S,"cWebPlugin");
tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"cChestEntity");
tolua_usertype(tolua_S,"cDispenserEntity");
tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"sWebAdminPage");
tolua_usertype(tolua_S,"cBlockEntity");
tolua_usertype(tolua_S,"cCriticalSection");
tolua_usertype(tolua_S,"HTTPTemplateRequest");
tolua_usertype(tolua_S,"sWebAdminPage");
tolua_usertype(tolua_S,"HTTPRequest");
tolua_usertype(tolua_S,"HTTPFormData");
tolua_usertype(tolua_S,"cFurnaceEntity");
tolua_usertype(tolua_S,"cPluginManager");
tolua_usertype(tolua_S,"cDropperEntity");
tolua_usertype(tolua_S,"cLineBlockTracer");
tolua_usertype(tolua_S,"cPluginManager");
tolua_usertype(tolua_S,"cIniFile");
tolua_usertype(tolua_S,"cWorld");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"cListeners");
tolua_usertype(tolua_S,"cPawn");
tolua_usertype(tolua_S,"cPlayer");
@ -254,7 +257,7 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cBlockEntityWindowOwner");
tolua_usertype(tolua_S,"cItemGrid::cListener");
tolua_usertype(tolua_S,"cServer");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"cItems");
tolua_usertype(tolua_S,"Lua__cEntity");
tolua_usertype(tolua_S,"Vector3d");
}
@ -267,16 +270,14 @@ static int tolua_AllToLua_cIniFile_new00(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) ||
!tolua_iscppstring(tolua_S,2,1,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
!tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
const std::string iniPath = ((const std::string) tolua_tocppstring(tolua_S,2,""));
{
cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)(iniPath));
cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)());
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
}
}
@ -297,16 +298,14 @@ static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertable(tolua_S,1,"cIniFile",0,&tolua_err) ||
!tolua_iscppstring(tolua_S,2,1,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
!tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
const std::string iniPath = ((const std::string) tolua_tocppstring(tolua_S,2,""));
{
cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)(iniPath));
cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)());
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
tolua_register_gc(tolua_S,lua_gettop(tolua_S));
}
@ -320,6 +319,59 @@ static int tolua_AllToLua_cIniFile_new00_local(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
/* method: new of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new01
static int tolua_AllToLua_cIniFile_new01(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
!tolua_isusertable(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
{
const std::string a_Path = ((const std::string) tolua_tocppstring(tolua_S,2,0));
{
cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)(a_Path));
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
tolua_pushcppstring(tolua_S,(const char*)a_Path);
}
}
return 2;
tolua_lerror:
return tolua_AllToLua_cIniFile_new00(tolua_S);
}
#endif //#ifndef TOLUA_DISABLE
/* method: new_local of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_new01_local
static int tolua_AllToLua_cIniFile_new01_local(lua_State* tolua_S)
{
tolua_Error tolua_err;
if (
!tolua_isusertable(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
{
const std::string a_Path = ((const std::string) tolua_tocppstring(tolua_S,2,0));
{
cIniFile* tolua_ret = (cIniFile*) Mtolua_new((cIniFile)(a_Path));
tolua_pushusertype(tolua_S,(void*)tolua_ret,"cIniFile");
tolua_register_gc(tolua_S,lua_gettop(tolua_S));
tolua_pushcppstring(tolua_S,(const char*)a_Path);
}
}
return 2;
tolua_lerror:
return tolua_AllToLua_cIniFile_new00_local(tolua_S);
}
#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)
@ -433,7 +485,7 @@ static int tolua_AllToLua_cIniFile_Path01(lua_State* tolua_S)
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Path'", NULL);
#endif
{
std::string tolua_ret = (std::string) self->Path();
const std::string tolua_ret = (const std::string) self->Path();
tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
@ -485,18 +537,20 @@ static int tolua_AllToLua_cIniFile_ReadFile00(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
!tolua_isboolean(tolua_S,2,1,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0);
bool a_AllowExampleRedirect = ((bool) tolua_toboolean(tolua_S,2,true));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ReadFile'", NULL);
#endif
{
bool tolua_ret = (bool) self->ReadFile();
bool tolua_ret = (bool) self->ReadFile(a_AllowExampleRedirect);
tolua_pushboolean(tolua_S,(bool)tolua_ret);
}
}
@ -516,14 +570,14 @@ static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S)
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cIniFile",0,&tolua_err) ||
!tolua_isusertype(tolua_S,1,"const 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);
const cIniFile* self = (const cIniFile*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'WriteFile'", NULL);
#endif
@ -541,37 +595,6 @@ static int tolua_AllToLua_cIniFile_WriteFile00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
/* method: Erase of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Erase00
static int tolua_AllToLua_cIniFile_Erase00(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 'Erase'", NULL);
#endif
{
self->Erase();
}
}
return 0;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'Erase'.",&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)
@ -634,6 +657,37 @@ static int tolua_AllToLua_cIniFile_Reset00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
/* method: Erase of class cIniFile */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cIniFile_Erase00
static int tolua_AllToLua_cIniFile_Erase00(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 'Erase'", NULL);
#endif
{
self->Erase();
}
}
return 0;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'Erase'.",&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)
@ -28476,6 +28530,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
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,"new",tolua_AllToLua_cIniFile_new01);
tolua_function(tolua_S,"new_local",tolua_AllToLua_cIniFile_new01_local);
tolua_function(tolua_S,".call",tolua_AllToLua_cIniFile_new01_local);
tolua_function(tolua_S,"CaseSensitive",tolua_AllToLua_cIniFile_CaseSensitive00);
tolua_function(tolua_S,"CaseInsensitive",tolua_AllToLua_cIniFile_CaseInsensitive00);
tolua_function(tolua_S,"Path",tolua_AllToLua_cIniFile_Path00);
@ -28483,9 +28540,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"SetPath",tolua_AllToLua_cIniFile_SetPath00);
tolua_function(tolua_S,"ReadFile",tolua_AllToLua_cIniFile_ReadFile00);
tolua_function(tolua_S,"WriteFile",tolua_AllToLua_cIniFile_WriteFile00);
tolua_function(tolua_S,"Erase",tolua_AllToLua_cIniFile_Erase00);
tolua_function(tolua_S,"Clear",tolua_AllToLua_cIniFile_Clear00);
tolua_function(tolua_S,"Reset",tolua_AllToLua_cIniFile_Reset00);
tolua_function(tolua_S,"Erase",tolua_AllToLua_cIniFile_Erase00);
tolua_function(tolua_S,"FindKey",tolua_AllToLua_cIniFile_FindKey00);
tolua_function(tolua_S,"FindValue",tolua_AllToLua_cIniFile_FindValue00);
tolua_function(tolua_S,"NumKeys",tolua_AllToLua_cIniFile_NumKeys00);
@ -28699,6 +28756,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_BLOCK_QUARTZ_BLOCK",E_BLOCK_QUARTZ_BLOCK);
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_CARPET",E_BLOCK_CARPET);
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_ITEM_EMPTY",E_ITEM_EMPTY);
@ -30283,6 +30341,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00);
tolua_variable(tolua_S,"__cItemGrid__cListener__",tolua_get_cLuaWindow___cItemGrid__cListener__,NULL);
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;
}

View File

@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 08/02/13 08:41:19.
** Generated automatically by tolua++-1.0.92 on 08/07/13 12:06:04.
*/
/* Exported function */

View File

@ -123,7 +123,7 @@ void cChestEntity::UsedBy(cPlayer * a_Player)
// 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_PosY, m_PosZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ);
m_World->MarkChunkDirty(ChunkX, ChunkZ);
}

View File

@ -125,7 +125,7 @@ void cHopperEntity::UsedBy(cPlayer * a_Player)
// 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_PosY, m_PosZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ);
m_World->MarkChunkDirty(ChunkX, ChunkZ);
}

104
source/BlockTracer.h Normal file
View File

@ -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) = 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) { 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;
} ;

View File

@ -137,7 +137,7 @@ public:
/// 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_Y, a_Z, a_ChunkX, 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;
@ -145,9 +145,8 @@ public:
/// Converts absolute block coords to chunk coords:
inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkZ )
inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ)
{
(void)a_Y;
a_ChunkX = a_X / Width;
if ((a_X < 0) && (a_X % Width != 0))
{

View File

@ -345,7 +345,7 @@ void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, c
x = a_BlockX;
y = a_BlockY;
z = a_BlockZ;
cChunkDef::BlockToChunk(x, y, z, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if (Chunk == NULL)
{
@ -364,7 +364,7 @@ void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_blockX, a_blockY, a_blockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if (Chunk == NULL)
{
@ -382,7 +382,7 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c
{
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
@ -592,7 +592,7 @@ void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, in
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcY / 8, a_SrcZ / 8, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if (Chunk == NULL)
{
@ -611,7 +611,7 @@ void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_S
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcY / 8, a_SrcZ / 8, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if (Chunk == NULL)
{
@ -645,7 +645,7 @@ void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, c
{
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if (Chunk == NULL)
{
@ -664,7 +664,7 @@ void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_Bl
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if (Chunk == NULL)
{
@ -682,7 +682,7 @@ void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClien
{
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
@ -700,7 +700,7 @@ void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, i
// a_Player rclked block entity at the coords specified, handle it
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
@ -713,11 +713,26 @@ void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, i
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_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
@ -735,8 +750,8 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M
{
cSimulatorManager * SimMgr = m_World->GetSimulatorManager();
int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ;
cChunkDef::BlockToChunk(a_MinBlockX, ZERO_CHUNK_Y, a_MinBlockZ, MinChunkX, MinChunkZ);
cChunkDef::BlockToChunk(a_MaxBlockX, ZERO_CHUNK_Y, a_MaxBlockZ, 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);
@ -1903,7 +1918,7 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt
{
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{

View File

@ -35,6 +35,7 @@ typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cDropperEntity> cDropperCallback;
typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
typedef cItemCallback<cChunk> cChunkCallback;
@ -79,6 +80,9 @@ public:
/// 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);

View File

@ -55,7 +55,10 @@ public:
{
if (!IsValidItem(m_ItemType))
{
LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType);
if (m_ItemType != E_BLOCK_AIR)
{
LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType);
}
Empty();
}
}

261
source/LineBlockTracer.cpp Normal file
View File

@ -0,0 +1,261 @@
// 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;
// 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; break;
case dirY: m_CurrentY += m_DirY; break;
case dirZ: m_CurrentZ += m_DirZ; 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))
{
// The callback terminated the trace
return false;
}
}
else
{
if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ))
{
// 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;
}
}
}

84
source/LineBlockTracer.h Normal file
View File

@ -0,0 +1,84 @@
// 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;
/// 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;
} ;

View File

@ -6,34 +6,14 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "LuaScript.h"
extern "C"
{
#include "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);
}
cLuaScript::cLuaScript()
: m_LuaState(NULL)
: m_LuaState("cLuaScript")
{
}
@ -42,166 +22,45 @@ cLuaScript::cLuaScript()
cLuaScript::~cLuaScript()
{
if( m_LuaState )
{
lua_close( m_LuaState );
m_LuaState = 0;
}
}
void cLuaScript::Initialize()
{
// Check to see if this script has not been initialized before
ASSERT(!m_LuaState);
ASSERT(!m_LuaState.IsValid());
// Create a Lua state and bind all libraries to it
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_LuaState.Create();
}
bool cLuaScript::LoadFile( const char* a_FilePath )
bool cLuaScript::LoadFile(const char * a_FilePath)
{
// Make sure the plugin is initialized
ASSERT(m_LuaState);
ASSERT(m_LuaState.IsValid());
// Load the file into the Lua state
int s = luaL_loadfile(m_LuaState, a_FilePath );
if (ReportErrors(s))
{
return false;
}
return true;
return m_LuaState.LoadFile(a_FilePath);
}
bool cLuaScript::Execute()
bool cLuaScript::CallShowPage(cWebAdmin & a_WebAdmin, HTTPTemplateRequest & a_Request, AString & a_ReturnedString)
{
// Make sure we got a Lua state
ASSERT(m_LuaState);
// Execute the script as it is right now
int s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
if( ReportErrors( s ) )
ASSERT(m_LuaState.IsValid());
m_LuaState.PushFunction("ShowPage");
m_LuaState.PushUserType(&a_WebAdmin, "cWebAdmin");
m_LuaState.PushUserType(&a_Request, "HTTPTemplateRequest");
if (!m_LuaState.CallFunction(1))
{
return false;
}
return true;
}
bool cLuaScript::ReportErrors( int a_Status )
{
if (a_Status == 0)
{
// No error to report
return false;
}
// Status was set to error so get the error from the Lua state and log it
LOGERROR("LUA: %s", lua_tostring(m_LuaState, -1));
lua_pop(m_LuaState, 1);
// Return true to indicate that an error was returned
return true;
}
bool cLuaScript::LuaPushFunction( const char * a_FunctionName, bool a_bLogError /*= true*/ )
{
ASSERT(m_LuaState);
// Find and push the function on the Lua stack
lua_getglobal(m_LuaState, a_FunctionName);
// Make sure we found a function
if (!lua_isfunction(m_LuaState, -1))
{
if (a_bLogError)
{
LOGWARN("LUA: Could not find function %s()", a_FunctionName);
}
// Pop the pushed 'object' back
lua_pop(m_LuaState, 1);
return false;
}
// Successfully pushed a function to the Lua stack
return true;
}
bool cLuaScript::LuaCallFunction( int a_NumArgs, int a_NumResults, const char * a_FunctionName )
{
ASSERT(m_LuaState);
// Make sure there's a lua function on the stack
ASSERT(lua_isfunction(m_LuaState, -a_NumArgs - 1));
// Call the desired function
int s = lua_pcall(m_LuaState, a_NumArgs, a_NumResults, 0);
// Check for errors
if (ReportErrors(s))
{
LOGWARN("LUA: Error calling function %s()", a_FunctionName);
return false;
}
// Successfully executed function
return true;
}
bool cLuaScript::CallFunction( const char* a_Function, AString& ReturnedString )
{
// Make sure we have the required things to call a function
ASSERT(m_LuaState);
ASSERT(a_Function);
// Push the desired function on the stack
if (!LuaPushFunction(a_Function))
{
return false;
}
if (!LuaCallFunction(0, 1, a_Function))
{
return false;
}
if (lua_isstring(m_LuaState, -1))
{
ReturnedString = tolua_tostring(m_LuaState, -1, "");
a_ReturnedString.assign(tolua_tostring(m_LuaState, -1, ""));
}
lua_pop(m_LuaState, 1);
return true;
@ -210,69 +69,3 @@ bool cLuaScript::CallFunction( const char* a_Function, AString& ReturnedString )
bool cLuaScript::CallFunction( const char* a_Function, const sLuaUsertype& a_UserType, AString& ReturnedString )
{
// Make sure we have the required things to call a function
ASSERT(m_LuaState);
ASSERT(a_Function);
// Push the desired function on the stack
if (!LuaPushFunction(a_Function))
{
return false;
}
tolua_pushusertype(m_LuaState, a_UserType.Object, a_UserType.ClassName);
if (!LuaCallFunction(1, 1, a_Function))
{
return false;
}
if (lua_isstring(m_LuaState, -1))
{
ReturnedString = tolua_tostring(m_LuaState, -1, "");
}
lua_pop(m_LuaState, 1);
return true;
}
bool cLuaScript::CallFunction( const char* a_Function, const sLuaUsertype& a_UserType1, const sLuaUsertype& a_UserType2, AString& ReturnedString )
{
// Make sure we have the required things to call a function
ASSERT(m_LuaState);
ASSERT(a_Function);
// Push the desired function on the stack
if (!LuaPushFunction(a_Function))
{
return false;
}
tolua_pushusertype(m_LuaState, a_UserType1.Object, a_UserType1.ClassName);
tolua_pushusertype(m_LuaState, a_UserType2.Object, a_UserType2.ClassName);
if (!LuaCallFunction(2, 1, a_Function))
{
return false;
}
if (lua_isstring(m_LuaState, -1))
{
ReturnedString = tolua_tostring(m_LuaState, -1, "");
}
lua_pop(m_LuaState, 1);
return true;
}

View File

@ -9,19 +9,15 @@
#pragma once
struct lua_State;
#include "LuaState.h"
struct sLuaUsertype
{
sLuaUsertype(void* a_pObject, const char* a_pClassName) : Object(a_pObject), ClassName(a_pClassName) {}
//
void* Object;
const char* ClassName;
} ;
// fwd:
class cWebAdmin;
struct HTTPTemplateRequest;
@ -30,32 +26,18 @@ struct sLuaUsertype
class cLuaScript
{
public:
cLuaScript();
~cLuaScript();
cLuaScript(void);
/// Prepares a Lua state
void Initialize();
void Initialize();
/// Load a Lua script on the given path
bool LoadFile(const char* a_FilePath);
bool LoadFile(const char * a_FilePath);
/// Execute the loaded Lua script
bool Execute();
/// Call a function on the Lua script. Put all overloads here
bool CallFunction(const char* a_Function, AString& ReturnedString);
bool CallFunction(const char* a_Function, const sLuaUsertype& a_UserType, AString& ReturnedString);
bool CallFunction(const char* a_Function, const sLuaUsertype& a_UserType1, const sLuaUsertype& a_UserType2, AString& ReturnedString);
bool CallShowPage(cWebAdmin & a_WebAdmin, HTTPTemplateRequest & a_Request, AString & a_ReturnedString);
protected:
/// Reports an error in the log if a_Status is flagged as an error. Returns true when a_Status is flagged as error, returns false when no error occured.
bool ReportErrors(int a_Status);
/// Helper functions for calling functions in Lua
bool LuaPushFunction(const char * a_FunctionName, bool a_bLogError = true);
bool LuaCallFunction(int a_NumArgs, int a_NumResults, const char * a_FunctionName ); // a_FunctionName is only used for error messages, nothing else
private:
lua_State* m_LuaState;
cLuaState m_LuaState;
} ;

646
source/LuaState.cpp Normal file
View File

@ -0,0 +1,646 @@
// 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 "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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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::PushFunction(const char * a_FunctionName, bool a_ShouldLogFailure /* = true */)
{
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))
{
if (a_ShouldLogFailure)
{
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::PushFunctionFromRegistry(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::PushStringVector(const AStringVector & a_Vector)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
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());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Object, a_Type);
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushNumber(int a_Value)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushnumber(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushNumber(double a_Value)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushnumber(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushString(const char * a_Value)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushstring(m_LuaState, a_Value);
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushBool(bool a_Value)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushboolean(m_LuaState, a_Value ? 1 : 0);
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cWorld * a_World)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_World, "cWorld");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cPlayer * a_Player)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Player, "cPlayer");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cEntity * a_Entity)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Entity, "cEntity");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cItem * a_Item)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Item, "cItem");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cItems * a_Items)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Items, "cItems");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cClientHandle * a_Client)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Client, "cClientHandle");
m_NumCurrentFunctionArgs += 1;
}
void cLuaState::PushObject(cPickup * a_Pickup)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
tolua_pushusertype(m_LuaState, a_Pickup, "cPickup");
m_NumCurrentFunctionArgs += 1;
}
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::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::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;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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);
}
}

198
source/LuaState.h Normal file
View File

@ -0,0 +1,198 @@
// 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 "lauxlib.h"
}
class cWorld;
class cPlayer;
class cEntity;
class cItem;
class cItems;
class cClientHandle;
class cPickup;
/// 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) { return m_Ref; }
protected:
cLuaState & m_LuaState;
int m_Ref;
} ;
/** 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);
/** Pushes the function of the specified name onto the stack.
Returns true if successful.
If a_ShouldLogFail is true, logs a warning on failure (incl. m_SubsystemName)
*/
bool PushFunction(const char * a_FunctionName, bool a_ShouldLogFailure = true);
/** 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 PushFunctionFromRegistry(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 string vector, as a table, onto the stack
void PushStringVector(const AStringVector & a_Vector);
/// Pushes a usertype of the specified class type onto the stack
void PushUserType(void * a_Object, const char * a_Type);
/// Pushes an integer onto the stack
void PushNumber(int a_Value);
/// Pushes a double onto the stack
void PushNumber(double a_Value);
/// Pushes a string onto the stack
void PushString(const char * a_Value);
/// Pushes a bool onto the stack
void PushBool(bool a_Value);
// Special family of functions that do PushUserType internally, but require one less parameter
void PushObject(cWorld * a_World);
void PushObject(cPlayer * a_Player);
void PushObject(cEntity * a_Entity);
void PushObject(cItem * a_Item);
void PushObject(cItems * a_Items);
void PushObject(cClientHandle * a_ClientHandle);
void PushObject(cPickup * a_Pickup);
/**
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 usertype; also logs warning if not
bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are a table; also logs warning if not
bool CheckParamTable(int a_StartParam, int a_EndParam = -1);
/// Returns true if the specified parameters on the stack are a number; also logs warning if not
bool CheckParamNumber(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);
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;
} ;

View File

@ -19,13 +19,8 @@
#include "BlockEntities/FurnaceEntity.h"
#include "md5/md5.h"
#include "LuaWindow.h"
// fwd: LuaCommandBinder.cpp
bool report_errors(lua_State* lua, int status);
#include "LuaState.h"
#include "LineBlockTracer.h"
@ -83,23 +78,14 @@ int lua_do_error(lua_State* L, const char * a_pFormat, ...)
* Lua bound functions with special return types
**/
static int tolua_StringSplit(lua_State* tolua_S)
static int tolua_StringSplit(lua_State * tolua_S)
{
std::string str = ((std::string) tolua_tocppstring(tolua_S,1,0));
std::string delim = ((std::string) tolua_tocppstring(tolua_S,2,0));
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 );
lua_createtable(tolua_S, Split.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
std::vector<std::string>::const_iterator iter = Split.begin();
while(iter != Split.end()) {
tolua_pushstring( tolua_S, (*iter).c_str() );
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
}
AStringVector Split = StringSplit(str, delim);
LuaState.PushStringVector(Split);
return 1;
}
@ -157,7 +143,8 @@ cPlugin_NewLua * GetLuaPlugin(lua_State * L)
lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
if (!lua_islightuserdata(L, -1))
{
LOGERROR("%s: cannot get plugin instance, what have you done to my Lua state?", __FUNCTION__);
LOGWARNING("%s: cannot get plugin instance, what have you done to my Lua state?", __FUNCTION__);
lua_pop(L, 1);
return NULL;
}
cPlugin_NewLua * Plugin = (cPlugin_NewLua *)lua_topointer(L, -1);
@ -233,7 +220,7 @@ static int tolua_DoWith(lua_State* tolua_S)
}
int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; // Abort enumeration
}
@ -323,7 +310,7 @@ static int tolua_DoWithID(lua_State* tolua_S)
}
int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; // Abort enumeration
}
@ -354,9 +341,11 @@ static int tolua_DoWithID(lua_State* tolua_S)
template< class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &) >
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' */
@ -418,7 +407,7 @@ static int tolua_DoWithXYZ(lua_State* tolua_S)
}
int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; // Abort enumeration
}
@ -511,7 +500,7 @@ static int tolua_ForEachInChunk(lua_State* tolua_S)
}
int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; /* Abort enumeration */
}
@ -602,7 +591,7 @@ static int tolua_ForEach(lua_State * tolua_S)
}
int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; /* Abort enumeration */
}
@ -813,7 +802,7 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
tolua_pushcppstring(LuaState, a_HelpString);
int s = lua_pcall(LuaState, 3, 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; /* Abort enumeration */
}
@ -887,7 +876,7 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
tolua_pushcppstring(LuaState, a_HelpString);
int s = lua_pcall(LuaState, 2, 1, 0);
if (report_errors(LuaState, s))
if (cLuaState::ReportErrors(LuaState, s))
{
return true; /* Abort enumeration */
}
@ -1307,8 +1296,8 @@ static int tolua_cPlugin_Call(lua_State* tolua_S)
return 0;
}
int s = lua_pcall(targetState, top-2, LUA_MULTRET, 0);
if( report_errors( targetState, s ) )
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;
@ -1497,9 +1486,174 @@ tolua_lerror:
/// 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) override
{
if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock"))
{
// No such function in the table, skip the callback
return false;
}
m_LuaState.PushNumber(a_BlockX);
m_LuaState.PushNumber(a_BlockY);
m_LuaState.PushNumber(a_BlockZ);
m_LuaState.PushNumber(a_BlockType);
m_LuaState.PushNumber(a_BlockMeta);
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) override
{
if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData"))
{
// No such function in the table, skip the callback
return false;
}
m_LuaState.PushNumber(a_BlockX);
m_LuaState.PushNumber(a_BlockY);
m_LuaState.PushNumber(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 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.PushNumber(a_BlockX);
m_LuaState.PushNumber(a_BlockY);
m_LuaState.PushNumber(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.PushNumber(a_BlockX);
m_LuaState.PushNumber(a_BlockY);
m_LuaState.PushNumber(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;
}
void ManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S,NULL);
tolua_beginmodule(tolua_S, NULL);
tolua_function(tolua_S, "StringSplit", tolua_StringSplit);
tolua_function(tolua_S, "LOG", tolua_LOG);
tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO);
@ -1507,6 +1661,10 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR);
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>);

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
#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"
@ -11,9 +12,6 @@
// fwd: Lua
typedef struct lua_State lua_State;
// fwd: UI/Window.h
class cWindow;
@ -99,7 +97,7 @@ public:
/// 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);
lua_State * GetLuaState(void) { return m_LuaState; }
cLuaState & GetLuaState(void) { return m_LuaState; }
cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; }
@ -114,7 +112,7 @@ public:
protected:
cCriticalSection m_CriticalSection;
lua_State * m_LuaState;
cLuaState m_LuaState;
/// Maps command name into Lua function reference
typedef std::map<AString, int> CommandMap;
@ -122,9 +120,6 @@ protected:
CommandMap m_Commands;
CommandMap m_ConsoleCommands;
bool PushFunction(const char * a_FunctionName, bool a_bLogError = true);
bool CallFunction(int a_NumArgs, int a_NumResults, const char * a_FunctionName ); // a_FunctionName is only used for error messages, nothing else
/// Returns the name of Lua function that should handle the specified hook
const char * GetHookFnName(cPluginManager::PluginHook a_Hook);
} ; // tolua_export

View File

@ -178,7 +178,7 @@ void cWebAdmin::Request_Handler(webserver::http_request* r)
if (!bDontShowTemplate)
{
// New Lua web template
bLuaTemplateSuccessful = WebAdmin->m_pTemplate->CallFunction("ShowPage", sLuaUsertype(WebAdmin, "cWebAdmin"), sLuaUsertype(&TemplateRequest, "HTTPTemplateRequest"), Template);
bLuaTemplateSuccessful = WebAdmin->m_pTemplate->CallShowPage(*WebAdmin, TemplateRequest, Template);
}
if (!bLuaTemplateSuccessful)
@ -274,14 +274,14 @@ bool cWebAdmin::Init( int a_Port )
m_Port = a_Port;
m_IniFile = new cIniFile("webadmin.ini");
if( m_IniFile->ReadFile() )
if (m_IniFile->ReadFile())
{
m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080 );
m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080);
}
// Initialize the WebAdmin template script and load the file
m_pTemplate->Initialize();
if (!m_pTemplate->LoadFile( FILE_IO_PREFIX "webadmin/template.lua") || !m_pTemplate->Execute())
if (!m_pTemplate->LoadFile(FILE_IO_PREFIX "webadmin/template.lua"))
{
LOGWARN("Could not load WebAdmin template.");
}

View File

@ -58,6 +58,9 @@
#include "tolua++.h"
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
#include "LineBlockTracer.h"
#ifndef _WIN32
#include <stdlib.h>
#endif
@ -441,6 +444,60 @@ void cWorld::InitializeSpawn(void)
// 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
}
@ -831,6 +888,15 @@ bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_
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)

View File

@ -404,6 +404,9 @@ public:
/// 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); }
/// 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 GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, char a_SaplingMeta); // tolua_export