2013-07-30 11:26:06 -04:00
2013-09-07 12:33:15 -04:00
-- Global variables
2013-07-29 07:13:03 -04:00
g_DropSpensersToActivate = { } ; -- A list of dispensers and droppers (as {World, X, Y Z} quadruplets) that are to be activated every tick
g_HungerReportTick = 10 ;
2013-08-19 16:48:13 -04:00
g_ShowFoodStats = false ; -- When true, each player's food stats are sent to them every 10 ticks
2013-07-29 07:13:03 -04:00
2013-07-30 11:26:06 -04:00
2015-04-11 04:06:08 -04:00
function Initialize ( a_Plugin )
2013-08-21 15:42:39 -04:00
--[[
-- Test multiple hook handlers:
cPluginManager.AddHook ( cPluginManager.HOOK_TICK , OnTick1 ) ;
cPluginManager.AddHook ( cPluginManager.HOOK_TICK , OnTick2 ) ;
--]]
2014-02-15 10:27:40 -05:00
local PM = cPluginManager ;
PM : AddHook ( cPluginManager.HOOK_PLAYER_USING_BLOCK , OnPlayerUsingBlock ) ;
PM : AddHook ( cPluginManager.HOOK_PLAYER_USING_ITEM , OnPlayerUsingItem ) ;
PM : AddHook ( cPluginManager.HOOK_TAKE_DAMAGE , OnTakeDamage ) ;
PM : AddHook ( cPluginManager.HOOK_TICK , OnTick ) ;
PM : AddHook ( cPluginManager.HOOK_CHAT , OnChat ) ;
PM : AddHook ( cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY , OnPlayerRightClickingEntity ) ;
PM : AddHook ( cPluginManager.HOOK_WORLD_TICK , OnWorldTick ) ;
PM : AddHook ( cPluginManager.HOOK_PLUGINS_LOADED , OnPluginsLoaded ) ;
2014-06-02 05:54:45 -04:00
PM : AddHook ( cPluginManager.HOOK_PLAYER_JOINED , OnPlayerJoined ) ;
PM : AddHook ( cPluginManager.HOOK_PROJECTILE_HIT_BLOCK , OnProjectileHitBlock ) ;
2014-07-10 12:19:05 -04:00
PM : AddHook ( cPluginManager.HOOK_CHUNK_UNLOADING , OnChunkUnloading ) ;
PM : AddHook ( cPluginManager.HOOK_WORLD_STARTED , OnWorldStarted ) ;
2014-08-27 08:41:29 -04:00
PM : AddHook ( cPluginManager.HOOK_PROJECTILE_HIT_BLOCK , OnProjectileHitBlock ) ;
2014-02-15 10:27:40 -05:00
2015-03-01 06:11:51 -05:00
-- _X: Disabled WECUI manipulation:
-- PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage);
2014-03-28 08:34:32 -04:00
-- _X: Disabled so that the normal operation doesn't interfere with anything
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
2014-12-11 15:08:35 -05:00
-- Load the InfoReg shared library:
dofile ( cPluginManager : GetPluginsPath ( ) .. " /InfoReg.lua " )
-- Bind all the commands:
RegisterPluginInfoCommands ( ) ;
-- Bind all the console commands:
RegisterPluginInfoConsoleCommands ( ) ;
2014-10-13 08:49:18 -04:00
2015-04-11 04:06:08 -04:00
a_Plugin : AddWebTab ( " Debuggers " , HandleRequest_Debuggers )
a_Plugin : AddWebTab ( " StressTest " , HandleRequest_StressTest )
2013-11-22 14:11:24 -05:00
2013-07-29 07:13:03 -04:00
-- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
2014-04-02 05:58:19 -04:00
-- TestBlockAreas()
-- TestSQLiteBindings()
-- TestExpatBindings()
2015-05-07 17:02:18 -04:00
TestPluginCalls ( )
2014-01-21 16:59:08 -05:00
2014-03-07 03:18:10 -05:00
TestBlockAreasString ( )
2014-04-02 05:58:19 -04:00
TestStringBase64 ( )
2014-08-09 16:55:38 -04:00
-- TestUUIDFromName()
-- TestRankMgr()
2015-04-10 15:40:45 -04:00
TestFileExt ( )
2015-04-11 04:06:08 -04:00
TestFileLastMod ( )
2015-04-23 13:41:01 -04:00
TestPluginInterface ( )
2015-04-11 04:06:08 -04:00
local LastSelfMod = cFile : GetLastModificationTime ( a_Plugin : GetLocalFolder ( ) .. " /Debuggers.lua " )
LOG ( " Debuggers.lua last modified on " .. os.date ( " %Y-%m-%dT%H:%M:%S " , LastSelfMod ) )
2014-03-31 16:51:14 -04:00
--[[
-- Test cCompositeChat usage in console-logging:
LOGINFO ( cCompositeChat ( " This is a simple message with some @2 color formatting @4 and http://links.to . " )
: AddSuggestCommandPart ( " (Suggested command) " , " cmd " )
: AddRunCommandPart ( " (Run command) " , " cmd " )
: SetMessageType ( mtInfo )
)
--]]
2014-03-07 03:18:10 -05:00
2015-04-23 13:41:01 -04:00
-- Test the crash in #1889:
cPluginManager : AddHook ( cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY ,
function ( a_CBPlayer , a_CBEntity )
a_CBPlayer : GetWorld ( ) : DoWithEntityByID ( -- This will crash the server in #1889
a_CBEntity : GetUniqueID ( ) ,
function ( Entity )
2015-04-23 16:20:31 -04:00
LOG ( " RightClicking an entity, crash #1889 fixed. Entity is a " .. tolua.type ( Entity ) )
2015-04-23 13:41:01 -04:00
end
)
end
)
2013-07-29 07:13:03 -04:00
return true
end ;
2013-07-30 11:26:06 -04:00
2015-04-23 13:41:01 -04:00
function TestPluginInterface ( )
cPluginManager : DoWithPlugin ( " Core " ,
function ( a_CBPlugin )
if ( a_CBPlugin : GetStatus ( ) == cPluginManager.psLoaded ) then
LOG ( " Core plugin was found, version " .. a_CBPlugin : GetVersion ( ) )
else
LOG ( " Core plugin is not loaded " )
end
end
)
cPluginManager : ForEachPlugin (
function ( a_CBPlugin )
LOG ( " Plugin in " .. a_CBPlugin : GetFolderName ( ) .. " has an API name of " .. a_CBPlugin : GetName ( ) .. " and status " .. a_CBPlugin : GetStatus ( ) )
end
)
end
2015-04-10 15:40:45 -04:00
function TestFileExt ( )
assert ( cFile : ChangeFileExt ( " fileless_dir/ " , " new " ) == " fileless_dir/ " )
2015-04-11 11:42:32 -04:00
assert ( cFile : ChangeFileExt ( " fileless_dir/ " , " .new " ) == " fileless_dir/ " )
2015-04-10 15:40:45 -04:00
assert ( cFile : ChangeFileExt ( " pathless_file.ext " , " new " ) == " pathless_file.new " )
2015-04-11 11:42:32 -04:00
assert ( cFile : ChangeFileExt ( " pathless_file.ext " , " .new " ) == " pathless_file.new " )
2015-04-10 15:40:45 -04:00
assert ( cFile : ChangeFileExt ( " path/to/file.ext " , " new " ) == " path/to/file.new " )
2015-04-11 11:42:32 -04:00
assert ( cFile : ChangeFileExt ( " path/to/file.ext " , " .new " ) == " path/to/file.new " )
2015-04-10 15:40:45 -04:00
assert ( cFile : ChangeFileExt ( " path/to.dir/file " , " new " ) == " path/to.dir/file.new " )
2015-04-11 11:42:32 -04:00
assert ( cFile : ChangeFileExt ( " path/to.dir/file " , " .new " ) == " path/to.dir/file.new " )
2015-04-10 15:40:45 -04:00
assert ( cFile : ChangeFileExt ( " path/to.dir/file.ext " , " new " ) == " path/to.dir/file.new " )
2015-04-11 11:42:32 -04:00
assert ( cFile : ChangeFileExt ( " path/to.dir/file.ext " , " .new " ) == " path/to.dir/file.new " )
assert ( cFile : ChangeFileExt ( " path/to.dir/file.longext " , " new " ) == " path/to.dir/file.new " )
assert ( cFile : ChangeFileExt ( " path/to.dir/file.longext " , " .new " ) == " path/to.dir/file.new " )
assert ( cFile : ChangeFileExt ( " path/to.dir/file. " , " new " ) == " path/to.dir/file.new " )
assert ( cFile : ChangeFileExt ( " path/to.dir/file. " , " .new " ) == " path/to.dir/file.new " )
2015-04-10 15:40:45 -04:00
end
2015-04-11 04:06:08 -04:00
function TestFileLastMod ( )
local f = assert ( io.open ( " test.txt " , " w " ) )
f : write ( " test " )
f : close ( )
local filetime = cFile : GetLastModificationTime ( " test.txt " )
local ostime = os.time ( )
LOG ( " file time: " .. filetime .. " , OS time: " .. ostime .. " , difference: " .. ostime - filetime )
end
2014-01-21 16:59:08 -05:00
function TestPluginCalls ( )
-- In order to test the inter-plugin communication, we're going to call Core's ReturnColorFromChar() function
-- It is a rather simple function that doesn't need any tables as its params and returns a value, too
-- Note the signature: function ReturnColorFromChar( Split, char ) ... return cChatColog.Gray ... end
-- The Split parameter should be a table, but it is not used in that function anyway,
-- so we can get away with passing nil to it.
2015-05-07 17:02:18 -04:00
LOG ( " Debuggers: Calling NoSuchPlugin.FnName()... " )
cPluginManager : CallPlugin ( " NoSuchPlugin " , " FnName " , " SomeParam " )
LOG ( " Debuggers: Calling Core.NoSuchFunction()... " )
cPluginManager : CallPlugin ( " Core " , " NoSuchFunction " , " SomeParam " )
LOG ( " Debuggers: Calling Core.ReturnColorFromChar(..., \" 8 \" )... " )
local Gray = cPluginManager : CallPlugin ( " Core " , " ReturnColorFromChar " , " split " , " 8 " )
2014-01-21 16:59:08 -05:00
if ( Gray ~= cChatColor.Gray ) then
2015-05-07 17:02:18 -04:00
LOGWARNING ( " Debuggers: Call failed, exp " .. cChatColor.Gray .. " , got " .. ( Gray or " <nil> " ) )
2014-01-21 16:59:08 -05:00
else
2015-05-07 17:02:18 -04:00
LOG ( " Debuggers: Call succeeded " )
2014-01-21 16:59:08 -05:00
end
2015-05-07 17:02:18 -04:00
LOG ( " Debuggers: Inter-plugin calls done. " )
2014-01-21 16:59:08 -05:00
end
2013-07-30 11:26:06 -04:00
2013-07-29 07:13:03 -04:00
function TestBlockAreas ( )
LOG ( " Testing block areas... " ) ;
-- Debug block area merging:
local BA1 = cBlockArea ( ) ;
local BA2 = cBlockArea ( ) ;
if ( BA1 : LoadFromSchematicFile ( " schematics/test.schematic " ) ) then
if ( BA2 : LoadFromSchematicFile ( " schematics/fountain.schematic " ) ) then
BA2 : SetRelBlockType ( 0 , 0 , 0 , E_BLOCK_LAPIS_BLOCK ) ;
BA2 : SetRelBlockType ( 1 , 0 , 0 , E_BLOCK_LAPIS_BLOCK ) ;
BA2 : SetRelBlockType ( 2 , 0 , 0 , E_BLOCK_LAPIS_BLOCK ) ;
BA1 : Merge ( BA2 , 1 , 10 , 1 , cBlockArea.msImprint ) ;
BA1 : SaveToSchematicFile ( " schematics/merge.schematic " ) ;
end
else
BA1 : Create ( 16 , 16 , 16 ) ;
end
-- Debug block area cuboid filling:
BA1 : FillRelCuboid ( 2 , 9 , 2 , 8 , 2 , 8 , cBlockArea.baTypes , E_BLOCK_GOLD_BLOCK ) ;
BA1 : RelLine ( 2 , 2 , 2 , 9 , 8 , 8 , cBlockArea.baTypes or cBlockArea.baMetas , E_BLOCK_SAPLING , E_META_SAPLING_BIRCH ) ;
BA1 : SaveToSchematicFile ( " schematics/fillrel.schematic " ) ;
-- Debug block area mirroring:
if ( BA1 : LoadFromSchematicFile ( " schematics/lt.schematic " ) ) then
BA1 : MirrorXYNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/lt_XY.schematic " ) ;
BA1 : MirrorXYNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/lt_XY2.schematic " ) ;
BA1 : MirrorXZNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/lt_XZ.schematic " ) ;
BA1 : MirrorXZNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/lt_XZ2.schematic " ) ;
BA1 : MirrorYZNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/lt_YZ.schematic " ) ;
BA1 : MirrorYZNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/lt_YZ2.schematic " ) ;
end
-- Debug block area rotation:
if ( BA1 : LoadFromSchematicFile ( " schematics/rot.schematic " ) ) then
BA1 : RotateCWNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rot1.schematic " ) ;
BA1 : RotateCWNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rot2.schematic " ) ;
BA1 : RotateCWNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rot3.schematic " ) ;
BA1 : RotateCWNoMeta ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rot4.schematic " ) ;
end
-- Debug block area rotation:
if ( BA1 : LoadFromSchematicFile ( " schematics/rotm.schematic " ) ) then
BA1 : RotateCCW ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rotm1.schematic " ) ;
BA1 : RotateCCW ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rotm2.schematic " ) ;
BA1 : RotateCCW ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rotm3.schematic " ) ;
BA1 : RotateCCW ( ) ;
BA1 : SaveToSchematicFile ( " schematics/rotm4.schematic " ) ;
end
-- Debug block area mirroring:
if ( BA1 : LoadFromSchematicFile ( " schematics/ltm.schematic " ) ) then
BA1 : MirrorXY ( ) ;
BA1 : SaveToSchematicFile ( " schematics/ltm_XY.schematic " ) ;
BA1 : MirrorXY ( ) ;
BA1 : SaveToSchematicFile ( " schematics/ltm_XY2.schematic " ) ;
BA1 : MirrorXZ ( ) ;
BA1 : SaveToSchematicFile ( " schematics/ltm_XZ.schematic " ) ;
BA1 : MirrorXZ ( ) ;
BA1 : SaveToSchematicFile ( " schematics/ltm_XZ2.schematic " ) ;
BA1 : MirrorYZ ( ) ;
BA1 : SaveToSchematicFile ( " schematics/ltm_YZ.schematic " ) ;
BA1 : MirrorYZ ( ) ;
BA1 : SaveToSchematicFile ( " schematics/ltm_YZ2.schematic " ) ;
end
LOG ( " Block areas test ended " ) ;
end
2014-03-07 03:18:10 -05:00
function TestBlockAreasString ( )
-- Write one area to string, then to file:
local BA1 = cBlockArea ( )
BA1 : Create ( 5 , 5 , 5 , cBlockArea.baTypes + cBlockArea.baMetas )
BA1 : Fill ( cBlockArea.baTypes , E_BLOCK_DIAMOND_BLOCK )
BA1 : FillRelCuboid ( 1 , 3 , 1 , 3 , 1 , 3 , cBlockArea.baTypes , E_BLOCK_GOLD_BLOCK )
local Data = BA1 : SaveToSchematicString ( )
if ( ( type ( Data ) ~= " string " ) or ( Data == " " ) ) then
LOG ( " Cannot save schematic to string " )
return
end
cFile : CreateFolder ( " schematics " )
2014-03-18 08:54:32 -04:00
local f = io.open ( " schematics/StringTest.schematic " , " wb " )
2014-03-07 03:18:10 -05:00
f : write ( Data )
f : close ( )
-- Load a second area from that file:
local BA2 = cBlockArea ( )
if not ( BA2 : LoadFromSchematicFile ( " schematics/StringTest.schematic " ) ) then
LOG ( " Cannot read schematic from string test file " )
return
end
BA2 : Clear ( )
-- Load another area from a string in that file:
2014-03-18 08:54:32 -04:00
f = io.open ( " schematics/StringTest.schematic " , " rb " )
2014-03-07 03:18:10 -05:00
Data = f : read ( " *all " )
if not ( BA2 : LoadFromSchematicString ( Data ) ) then
LOG ( " Cannot load schematic from string " )
end
end
2014-04-02 05:58:19 -04:00
function TestStringBase64 ( )
-- Create a binary string:
local s = " "
for i = 0 , 255 do
s = s .. string.char ( i )
end
-- Roundtrip through Base64:
local Base64 = Base64Encode ( s )
local UnBase64 = Base64Decode ( Base64 )
assert ( UnBase64 == s )
end
2014-07-28 11:14:23 -04:00
function TestUUIDFromName ( )
LOG ( " Testing UUID-from-Name resolution... " )
-- Test by querying a few existing names, along with a non-existent one:
local PlayerNames =
{
" xoft " ,
" aloe_vera " ,
" nonexistent_player " ,
}
-- WARNING: Blocking operation! DO NOT USE IN TICK THREAD!
2014-07-30 07:53:23 -04:00
local UUIDs = cMojangAPI : GetUUIDsFromPlayerNames ( PlayerNames )
2014-07-28 11:14:23 -04:00
-- Log the results:
for _ , name in ipairs ( PlayerNames ) do
local UUID = UUIDs [ name ]
if ( UUID == nil ) then
LOG ( " UUID( " .. name .. " ) not found. " )
else
LOG ( " UUID( " .. name .. " ) = \" " .. UUID .. " \" " )
end
end
2014-07-30 07:53:23 -04:00
-- Test once more with the same players, valid-only. This should go directly from cache, so fast.
LOG ( " Testing again with the same valid players... " )
local ValidPlayerNames =
{
" xoft " ,
" aloe_vera " ,
}
UUIDs = cMojangAPI : GetUUIDsFromPlayerNames ( ValidPlayerNames ) ;
2014-07-31 04:02:50 -04:00
-- Log the results:
for _ , name in ipairs ( ValidPlayerNames ) do
local UUID = UUIDs [ name ]
if ( UUID == nil ) then
LOG ( " UUID( " .. name .. " ) not found. " )
else
LOG ( " UUID( " .. name .. " ) = \" " .. UUID .. " \" " )
end
end
-- Test yet again, cache-only:
LOG ( " Testing once more, cache only... " )
local PlayerNames3 =
{
" xoft " ,
" aloe_vera " ,
" notch " , -- Valid player name, but not cached (most likely :)
}
UUIDs = cMojangAPI : GetUUIDsFromPlayerNames ( PlayerNames3 , true )
2014-07-30 07:53:23 -04:00
2014-07-31 04:02:50 -04:00
-- Log the results:
for _ , name in ipairs ( PlayerNames3 ) do
local UUID = UUIDs [ name ]
if ( UUID == nil ) then
LOG ( " UUID( " .. name .. " ) not found. " )
else
LOG ( " UUID( " .. name .. " ) = \" " .. UUID .. " \" " )
end
end
LOG ( " UUID-from-Name resolution tests finished. " )
2014-08-03 15:32:53 -04:00
LOG ( " Performing a Name-from-UUID test... " )
-- local NameToTest = "aloe_vera"
local NameToTest = " xoft "
local Name = cMojangAPI : GetPlayerNameFromUUID ( UUIDs [ NameToTest ] )
LOG ( " Name( " .. UUIDs [ NameToTest ] .. " ) = ' " .. Name .. " ', expected ' " .. NameToTest .. " '. " )
LOG ( " Name-from-UUID test finished. " )
2014-07-28 11:14:23 -04:00
end
2014-08-08 17:13:43 -04:00
function TestRankMgr ( )
LOG ( " Testing the rank manager " )
cRankManager : AddRank ( " LuaRank " )
cRankManager : AddGroup ( " LuaTestGroup " )
cRankManager : AddGroupToRank ( " LuaTestGroup " , " LuaRank " )
cRankManager : AddPermissionToGroup ( " luaperm " , " LuaTestGroup " )
end
2013-07-29 07:13:03 -04:00
function TestSQLiteBindings ( )
LOG ( " Testing SQLite bindings... " ) ;
-- Debug SQLite binding
local TestDB , ErrCode , ErrMsg = sqlite3.open ( " test.sqlite " ) ;
if ( TestDB ~= nil ) then
local function ShowRow ( UserData , NumCols , Values , Names )
assert ( UserData == ' UserData ' ) ;
LOG ( " New row " ) ;
for i = 1 , NumCols do
LOG ( " " .. Names [ i ] .. " = " .. Values [ i ] ) ;
end
return 0 ;
end
local sql = [ = [
CREATE TABLE numbers ( num1 , num2 , str ) ;
INSERT INTO numbers VALUES ( 1 , 11 , " ABC " ) ;
INSERT INTO numbers VALUES ( 2 , 22 , " DEF " ) ;
INSERT INTO numbers VALUES ( 3 , 33 , " UVW " ) ;
INSERT INTO numbers VALUES ( 4 , 44 , " XYZ " ) ;
SELECT * FROM numbers ;
] = ]
local Res = TestDB : exec ( sql , ShowRow , ' UserData ' ) ;
if ( Res ~= sqlite3.OK ) then
LOG ( " TestDB:exec() failed: " .. Res .. " ( " .. TestDB : errmsg ( ) .. " ) " ) ;
end ;
TestDB : close ( ) ;
else
-- This happens if for example SQLite cannot open the file (eg. a folder with the same name exists)
LOG ( " SQLite3 failed to open DB! ( " .. ErrCode .. " , " .. ErrMsg .. " ) " ) ;
end
LOG ( " SQLite bindings test ended " ) ;
end
function TestExpatBindings ( )
LOG ( " Testing Expat bindings... " ) ;
-- Debug LuaExpat bindings:
local count = 0
callbacks = {
StartElement = function ( parser , name )
LOG ( " + " .. string.rep ( " " , count ) .. name ) ;
count = count + 1 ;
end ,
EndElement = function ( parser , name )
count = count - 1 ;
LOG ( " - " .. string.rep ( " " , count ) .. name ) ;
end
}
local p = lxp.new ( callbacks ) ;
p : parse ( " <elem1> \n next line \n another line " ) ;
p : parse ( " text \n " ) ;
p : parse ( " <elem2/> \n " ) ;
p : parse ( " more text " ) ;
p : parse ( " </elem1> " ) ;
p : parse ( " \n " ) ;
p : parse ( ) ; -- finishes the document
p : close ( ) ; -- closes the parser
LOG ( " Expat bindings test ended " ) ;
end
function OnUsingBlazeRod ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ )
-- Magic rod of query: show block types and metas for both neighbors of the pointed face
2014-06-24 01:05:49 -04:00
local Valid , Type , Meta = Player : GetWorld ( ) : GetBlockTypeMeta ( BlockX , BlockY , BlockZ ) ;
2013-07-29 07:13:03 -04:00
if ( Type == E_BLOCK_AIR ) then
Player : SendMessage ( cChatColor.LightGray .. " Block { " .. BlockX .. " , " .. BlockY .. " , " .. BlockZ .. " }: air: " .. Meta ) ;
else
local TempItem = cItem ( Type , 1 , Meta ) ;
Player : SendMessage ( cChatColor.LightGray .. " Block { " .. BlockX .. " , " .. BlockY .. " , " .. BlockZ .. " }: " .. ItemToFullString ( TempItem ) .. " ( " .. Type .. " : " .. Meta .. " ) " ) ;
end
local X , Y , Z = AddFaceDirection ( BlockX , BlockY , BlockZ , BlockFace ) ;
2014-06-24 01:05:49 -04:00
Valid , Type , Meta = Player : GetWorld ( ) : GetBlockTypeMeta ( X , Y , Z ) ;
2013-07-29 07:13:03 -04:00
if ( Type == E_BLOCK_AIR ) then
Player : SendMessage ( cChatColor.LightGray .. " Block { " .. X .. " , " .. Y .. " , " .. Z .. " }: air: " .. Meta ) ;
else
local TempItem = cItem ( Type , 1 , Meta ) ;
Player : SendMessage ( cChatColor.LightGray .. " Block { " .. X .. " , " .. Y .. " , " .. Z .. " }: " .. ItemToFullString ( TempItem ) .. " ( " .. Type .. " : " .. Meta .. " ) " ) ;
end
return false ;
end
function OnUsingDiamond ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ )
-- Rclk with a diamond to test block area cropping and expanding
local Area = cBlockArea ( ) ;
Area : Read ( Player : GetWorld ( ) ,
BlockX - 19 , BlockX + 19 ,
BlockY - 7 , BlockY + 7 ,
BlockZ - 19 , BlockZ + 19
) ;
LOG ( " Size before cropping: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " crop0.dat " ) ;
Area : Crop ( 2 , 3 , 0 , 0 , 0 , 0 ) ;
LOG ( " Size after cropping 1: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " crop1.dat " ) ;
Area : Crop ( 2 , 3 , 0 , 0 , 0 , 0 ) ;
LOG ( " Size after cropping 2: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " crop2.dat " ) ;
Area : Expand ( 2 , 3 , 0 , 0 , 0 , 0 ) ;
LOG ( " Size after expanding 1: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " expand1.dat " ) ;
Area : Expand ( 3 , 2 , 1 , 1 , 0 , 0 ) ;
LOG ( " Size after expanding 2: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " expand2.dat " ) ;
Area : Crop ( 0 , 0 , 0 , 0 , 3 , 2 ) ;
LOG ( " Size after cropping 3: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " crop3.dat " ) ;
Area : Crop ( 0 , 0 , 3 , 2 , 0 , 0 ) ;
LOG ( " Size after cropping 4: " .. Area : GetSizeX ( ) .. " x " .. Area : GetSizeY ( ) .. " x " .. Area : GetSizeZ ( ) ) ;
Area : DumpToRawFile ( " crop4.dat " ) ;
LOG ( " Crop test done " ) ;
Player : SendMessage ( " Crop / expand test done. " ) ;
return false ;
end
function OnUsingEyeOfEnder ( Player , BlockX , BlockY , BlockZ )
-- Rclk with an eye of ender places a predefined schematic at the cursor
local Area = cBlockArea ( ) ;
if not ( Area : LoadFromSchematicFile ( " schematics/test.schematic " ) ) then
LOG ( " Loading failed " ) ;
return false ;
end
LOG ( " Schematic loaded, placing now. " ) ;
Area : Write ( Player : GetWorld ( ) , BlockX , BlockY , BlockZ ) ;
LOG ( " Done. " ) ;
return false ;
end
function OnUsingEnderPearl ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ )
-- Rclk with an ender pearl saves a predefined area around the cursor into a .schematic file. Also tests area copying
local Area = cBlockArea ( ) ;
if not ( Area : Read ( Player : GetWorld ( ) ,
BlockX - 8 , BlockX + 8 , BlockY - 8 , BlockY + 8 , BlockZ - 8 , BlockZ + 8 )
) then
LOG ( " LUA: Area couldn't be read " ) ;
return false ;
end
LOG ( " LUA: Area read, copying now. " ) ;
local Area2 = cBlockArea ( ) ;
Area2 : CopyFrom ( Area ) ;
LOG ( " LUA: Copied, now saving. " ) ;
if not ( Area2 : SaveToSchematicFile ( " schematics/test.schematic " ) ) then
LOG ( " LUA: Cannot save schematic file. " ) ;
return false ;
end
LOG ( " LUA: Done. " ) ;
return false ;
end
function OnUsingRedstoneTorch ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ )
-- Redstone torch activates a rapid dispenser / dropper discharge (at every tick):
local BlockType = Player : GetWorld ( ) : GetBlock ( BlockX , BlockY , BlockZ ) ;
if ( BlockType == E_BLOCK_DISPENSER ) then
table.insert ( g_DropSpensersToActivate , { World = Player : GetWorld ( ) , x = BlockX , y = BlockY , z = BlockZ } ) ;
Player : SendMessage ( " Dispenser at { " .. BlockX .. " , " .. BlockY .. " , " .. BlockZ .. " } discharging " ) ;
return true ;
elseif ( BlockType == E_BLOCK_DROPPER ) then
table.insert ( g_DropSpensersToActivate , { World = Player : GetWorld ( ) , x = BlockX , y = BlockY , z = BlockZ } ) ;
Player : SendMessage ( " Dropper at { " .. BlockX .. " , " .. BlockY .. " , " .. BlockZ .. " } discharging " ) ;
return true ;
else
Player : SendMessage ( " Neither a dispenser nor a dropper at { " .. BlockX .. " , " .. BlockY .. " , " .. BlockZ .. " }: " .. BlockType ) ;
end
return false ;
end
function OnPlayerUsingItem ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ )
-- dont check if the direction is in the air
if ( BlockFace == BLOCK_FACE_NONE ) then
return false
end
local HeldItem = Player : GetEquippedItem ( ) ;
local HeldItemType = HeldItem.m_ItemType ;
if ( HeldItemType == E_ITEM_STICK ) then
-- Magic sTick of ticking: set the pointed block for ticking at the next tick
Player : SendMessage ( cChatColor.LightGray .. " Setting next block tick to { " .. BlockX .. " , " .. BlockY .. " , " .. BlockZ .. " } " )
Player : GetWorld ( ) : SetNextBlockTick ( BlockX , BlockY , BlockZ ) ;
return true
elseif ( HeldItemType == E_ITEM_BLAZE_ROD ) then
return OnUsingBlazeRod ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ ) ;
elseif ( HeldItemType == E_ITEM_DIAMOND ) then
return OnUsingDiamond ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ ) ;
elseif ( HeldItemType == E_ITEM_EYE_OF_ENDER ) then
return OnUsingEyeOfEnder ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ ) ;
elseif ( HeldItemType == E_ITEM_ENDER_PEARL ) then
return OnUsingEnderPearl ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ ) ;
end
return false ;
end
function OnPlayerUsingBlock ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ , BlockType , BlockMeta )
-- dont check if the direction is in the air
if ( BlockFace == BLOCK_FACE_NONE ) then
return false
end
local HeldItem = Player : GetEquippedItem ( ) ;
local HeldItemType = HeldItem.m_ItemType ;
if ( HeldItemType == E_BLOCK_REDSTONE_TORCH_ON ) then
return OnUsingRedstoneTorch ( Player , BlockX , BlockY , BlockZ , BlockFace , CursorX , CursorY , CursorZ ) ;
end
end
function OnTakeDamage ( Receiver , TDI )
-- Receiver is cPawn
-- TDI is TakeDamageInfo
2014-07-18 17:11:59 -04:00
-- LOG(Receiver:GetClass() .. " was dealt " .. DamageTypeToString(TDI.DamageType) .. " damage: Raw " .. TDI.RawDamage .. ", Final " .. TDI.FinalDamage .. " (" .. (TDI.RawDamage - TDI.FinalDamage) .. " covered by armor)");
2013-07-29 07:13:03 -04:00
return false ;
end
2013-08-21 15:42:39 -04:00
function OnTick1 ( )
-- For testing multiple hook handlers per plugin
LOGINFO ( " Tick1 " ) ;
end
function OnTick2 ( )
-- For testing multiple hook handlers per plugin
LOGINFO ( " Tick2 " ) ;
end
2013-07-29 07:13:03 -04:00
--- When set to a positive number, the following OnTick() will perform GC and decrease until 0 again
GCOnTick = 0 ;
function OnTick ( )
-- Activate all dropspensers in the g_DropSpensersToActivate list:
local ActivateDrSp = function ( DropSpenser )
if ( DropSpenser : GetContents ( ) : GetFirstUsedSlot ( ) == - 1 ) then
return true ;
end
DropSpenser : Activate ( ) ;
return false ;
end
-- Walk the list backwards, because we're removing some items
local idx = # g_DropSpensersToActivate ;
for i = idx , 1 , - 1 do
local DrSp = g_DropSpensersToActivate [ i ] ;
if not ( DrSp.World : DoWithDropSpenserAt ( DrSp.x , DrSp.y , DrSp.z , ActivateDrSp ) ) then
table.remove ( g_DropSpensersToActivate , i ) ;
end
end
-- If GCOnTick > 0, do a garbage-collect and decrease by one
if ( GCOnTick > 0 ) then
collectgarbage ( ) ;
GCOnTick = GCOnTick - 1 ;
end
return false ;
end
2013-08-19 16:48:13 -04:00
function OnWorldTick ( a_World , a_Dt )
2013-09-07 12:33:15 -04:00
-- Report food stats, if switched on:
2013-08-19 16:48:13 -04:00
local Tick = a_World : GetWorldAge ( ) ;
if ( not ( g_ShowFoodStats ) or ( math.mod ( Tick , 10 ) ~= 0 ) ) then
return false ;
end
a_World : ForEachPlayer (
function ( a_Player )
a_Player : SendMessage (
tostring ( Tick / 10 ) ..
" > FS: fl " .. a_Player : GetFoodLevel ( ) ..
" ; sat " .. a_Player : GetFoodSaturationLevel ( ) ..
" ; exh " .. a_Player : GetFoodExhaustionLevel ( )
) ;
end
) ;
end
2013-07-29 07:13:03 -04:00
function OnChat ( a_Player , a_Message )
return false , " blabla " .. a_Message ;
end
2013-08-02 02:44:06 -04:00
function OnPlayerRightClickingEntity ( a_Player , a_Entity )
LOG ( " Player " .. a_Player : GetName ( ) .. " right-clicking entity ID " .. a_Entity : GetUniqueID ( ) .. " , a " .. a_Entity : GetClass ( ) ) ;
return false ;
2013-07-29 07:13:03 -04:00
end
2013-12-29 06:51:58 -05:00
function OnPluginsLoaded ( )
LOG ( " All plugins loaded " ) ;
end
2013-11-15 03:49:40 -05:00
function OnChunkGenerated ( a_World , a_ChunkX , a_ChunkZ , a_ChunkDesc )
-- Get the topmost block coord:
local Height = a_ChunkDesc : GetHeight ( 0 , 0 ) ;
-- Create a sign there:
a_ChunkDesc : SetBlockTypeMeta ( 0 , Height + 1 , 0 , E_BLOCK_SIGN_POST , 0 ) ;
local BlockEntity = a_ChunkDesc : GetBlockEntity ( 0 , Height + 1 , 0 ) ;
if ( BlockEntity ~= nil ) then
local SignEntity = tolua.cast ( BlockEntity , " cSignEntity " ) ;
SignEntity : SetLines ( " Chunk: " , tonumber ( a_ChunkX ) .. " , " .. tonumber ( a_ChunkZ ) , " " , " (Debuggers) " ) ;
end
-- Update the heightmap:
a_ChunkDesc : SetHeight ( 0 , 0 , Height + 1 ) ;
2013-08-02 02:44:06 -04:00
end
2013-07-29 07:13:03 -04:00
-- Function "round" copied from http://lua-users.org/wiki/SimpleRound
function round ( num , idp )
local mult = 10 ^ ( idp or 0 )
if num >= 0 then return math.floor ( num * mult + 0.5 ) / mult
else return math.ceil ( num * mult - 0.5 ) / mult end
end
2014-09-23 08:39:49 -04:00
function HandleNickCmd ( Split , Player )
2014-09-02 13:13:36 -04:00
if ( Split [ 2 ] == nil ) then
2014-09-23 08:39:49 -04:00
Player : SendMessage ( " Usage: /nick [CustomName] " ) ;
2014-09-02 13:13:36 -04:00
return true ;
end
2014-09-23 08:39:49 -04:00
Player : SetCustomName ( Split [ 2 ] ) ;
2014-09-02 13:13:36 -04:00
Player : SendMessageSuccess ( " Custom name setted to " .. Player : GetCustomName ( ) .. " ! " )
return true
end
2013-07-29 07:13:03 -04:00
function HandleListEntitiesCmd ( Split , Player )
local NumEntities = 0 ;
local ListEntity = function ( Entity )
if ( Entity : IsDestroyed ( ) ) then
-- The entity has already been destroyed, don't list it
return false ;
end ;
2013-08-19 06:03:34 -04:00
local cls = Entity : GetClass ( ) ;
Player : SendMessage ( " " .. Entity : GetUniqueID ( ) .. " : " .. cls .. " { " .. round ( Entity : GetPosX ( ) , 2 ) .. " , " .. round ( Entity : GetPosY ( ) , 2 ) .. " , " .. round ( Entity : GetPosZ ( ) , 2 ) .. " } " ) ;
if ( cls == " cPickup " ) then
local Pickup = Entity ;
tolua.cast ( Pickup , " cPickup " ) ;
Player : SendMessage ( " Age: " .. Pickup : GetAge ( ) .. " , IsCollected: " .. tostring ( Pickup : IsCollected ( ) ) ) ;
end
2013-07-29 07:13:03 -04:00
NumEntities = NumEntities + 1 ;
end
Player : SendMessage ( " Listing all entities... " ) ;
Player : GetWorld ( ) : ForEachEntity ( ListEntity ) ;
Player : SendMessage ( " List finished, " .. NumEntities .. " entities listed " ) ;
return true ;
end
function HandleKillEntitiesCmd ( Split , Player )
local NumEntities = 0 ;
local KillEntity = function ( Entity )
-- kill everything except for players:
if ( Entity : GetEntityType ( ) ~= cEntity.etPlayer ) then
Entity : Destroy ( ) ;
NumEntities = NumEntities + 1 ;
end ;
end
Player : SendMessage ( " Killing all entities... " ) ;
Player : GetWorld ( ) : ForEachEntity ( KillEntity ) ;
Player : SendMessage ( " Killed " .. NumEntities .. " entities. " ) ;
return true ;
end
function HandleWoolCmd ( Split , Player )
local Wool = cItem ( E_BLOCK_WOOL , 1 , E_META_WOOL_BLUE ) ;
Player : GetInventory ( ) : SetArmorSlot ( 0 , Wool ) ;
Player : GetInventory ( ) : SetArmorSlot ( 1 , Wool ) ;
Player : GetInventory ( ) : SetArmorSlot ( 2 , Wool ) ;
Player : GetInventory ( ) : SetArmorSlot ( 3 , Wool ) ;
Player : SendMessage ( " You have been bluewooled :) " ) ;
return true ;
end
function HandleTestWndCmd ( a_Split , a_Player )
2013-10-28 08:44:57 -04:00
local WindowType = cWindow.wtHopper ;
2013-07-29 07:13:03 -04:00
local WindowSizeX = 5 ;
local WindowSizeY = 1 ;
if ( # a_Split == 4 ) then
WindowType = tonumber ( a_Split [ 2 ] ) ;
WindowSizeX = tonumber ( a_Split [ 3 ] ) ;
WindowSizeY = tonumber ( a_Split [ 4 ] ) ;
elseif ( # a_Split ~= 1 ) then
a_Player : SendMessage ( " Usage: /testwnd [WindowType WindowSizeX WindowSizeY] " ) ;
return true ;
end
-- Test out the OnClosing callback's ability to refuse to close the window
local attempt = 1 ;
local OnClosing = function ( Window , Player , CanRefuse )
Player : SendMessage ( " Window closing attempt # " .. attempt .. " ; CanRefuse = " .. tostring ( CanRefuse ) ) ;
attempt = attempt + 1 ;
return CanRefuse and ( attempt <= 3 ) ; -- refuse twice, then allow, unless CanRefuse is set to true
end
-- Log the slot changes
local OnSlotChanged = function ( Window , SlotNum )
LOG ( " Window \" " .. Window : GetWindowTitle ( ) .. " \" slot " .. SlotNum .. " changed. " ) ;
end
local Window = cLuaWindow ( WindowType , WindowSizeX , WindowSizeY , " TestWnd " ) ;
local Item2 = cItem ( E_ITEM_DIAMOND_SWORD , 1 , 0 , " 1=1 " ) ;
local Item3 = cItem ( E_ITEM_DIAMOND_SHOVEL ) ;
Item3.m_Enchantments : SetLevel ( cEnchantments.enchUnbreaking , 4 ) ;
local Item4 = cItem ( Item3 ) ; -- Copy
Item4.m_Enchantments : SetLevel ( cEnchantments.enchEfficiency , 3 ) ; -- Add enchantment
Item4.m_Enchantments : SetLevel ( cEnchantments.enchUnbreaking , 5 ) ; -- Overwrite existing level
local Item5 = cItem ( E_ITEM_DIAMOND_CHESTPLATE , 1 , 0 , " thorns=1;unbreaking=3 " ) ;
Window : SetSlot ( a_Player , 0 , cItem ( E_ITEM_DIAMOND , 64 ) ) ;
Window : SetSlot ( a_Player , 1 , Item2 ) ;
Window : SetSlot ( a_Player , 2 , Item3 ) ;
Window : SetSlot ( a_Player , 3 , Item4 ) ;
Window : SetSlot ( a_Player , 4 , Item5 ) ;
Window : SetOnClosing ( OnClosing ) ;
Window : SetOnSlotChanged ( OnSlotChanged ) ;
a_Player : OpenWindow ( Window ) ;
-- To make sure that the object has the correct life-management in Lua,
-- let's garbage-collect in the following few ticks
GCOnTick = 10 ;
return true ;
end
function HandleGCCmd ( a_Split , a_Player )
collectgarbage ( ) ;
return true ;
end
function HandleFastCmd ( a_Split , a_Player )
if ( a_Player : GetNormalMaxSpeed ( ) <= 0.11 ) then
-- The player has normal speed, set double speed:
a_Player : SetNormalMaxSpeed ( 0.2 ) ;
a_Player : SendMessage ( " You are now fast " ) ;
else
-- The player has fast speed, set normal speed:
a_Player : SetNormalMaxSpeed ( 0.1 ) ;
a_Player : SendMessage ( " Back to normal speed " ) ;
end
return true ;
end
function HandleDashCmd ( a_Split , a_Player )
if ( a_Player : GetSprintingMaxSpeed ( ) <= 0.14 ) then
-- The player has normal sprinting speed, set double Sprintingspeed:
a_Player : SetSprintingMaxSpeed ( 0.4 ) ;
a_Player : SendMessage ( " You can now sprint very fast " ) ;
else
-- The player has fast sprinting speed, set normal sprinting speed:
a_Player : SetSprintingMaxSpeed ( 0.13 ) ;
a_Player : SendMessage ( " Back to normal sprinting " ) ;
end
return true ;
end ;
function HandleHungerCmd ( a_Split , a_Player )
a_Player : SendMessage ( " FoodLevel: " .. a_Player : GetFoodLevel ( ) ) ;
a_Player : SendMessage ( " FoodSaturationLevel: " .. a_Player : GetFoodSaturationLevel ( ) ) ;
a_Player : SendMessage ( " FoodTickTimer: " .. a_Player : GetFoodTickTimer ( ) ) ;
a_Player : SendMessage ( " FoodExhaustionLevel: " .. a_Player : GetFoodExhaustionLevel ( ) ) ;
a_Player : SendMessage ( " FoodPoisonedTicksRemaining: " .. a_Player : GetFoodPoisonedTicksRemaining ( ) ) ;
return true ;
end
function HandlePoisonCmd ( a_Split , a_Player )
a_Player : FoodPoison ( 15 * 20 ) ;
return true ;
end
function HandleStarveCmd ( a_Split , a_Player )
a_Player : SetFoodLevel ( 0 ) ;
a_Player : SendMessage ( " You are now starving " ) ;
return true ;
end
function HandleFoodLevelCmd ( a_Split , a_Player )
if ( # a_Split ~= 2 ) then
a_Player : SendMessage ( " Missing an argument: the food level to set " ) ;
return true ;
end
a_Player : SetFoodLevel ( tonumber ( a_Split [ 2 ] ) ) ;
2013-08-19 16:48:13 -04:00
a_Player : SetFoodSaturationLevel ( 5 ) ;
a_Player : SetFoodExhaustionLevel ( 0 ) ;
a_Player : SendMessage (
" Food level set to " .. a_Player : GetFoodLevel ( ) ..
" , saturation reset to " .. a_Player : GetFoodSaturationLevel ( ) ..
" and exhaustion reset to " .. a_Player : GetFoodExhaustionLevel ( )
) ;
2013-07-29 07:13:03 -04:00
return true ;
end
2013-08-07 08:34:56 -04:00
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
2013-08-18 07:26:37 -04:00
function HandleEnchCmd ( a_Split , a_Player )
2013-10-28 08:44:57 -04:00
local Wnd = cLuaWindow ( cWindow.wtEnchantment , 1 , 1 , " Ench " ) ;
2013-08-19 16:48:13 -04:00
a_Player : OpenWindow ( Wnd ) ;
Wnd : SetProperty ( 0 , 10 ) ;
Wnd : SetProperty ( 1 , 15 ) ;
Wnd : SetProperty ( 2 , 25 ) ;
2013-08-22 03:07:31 -04:00
return true ;
2013-08-19 16:48:13 -04:00
end
function HandleFoodStatsCmd ( a_Split , a_Player )
g_ShowFoodStats = not ( g_ShowFoodStats ) ;
2013-08-22 03:07:31 -04:00
return true ;
end
function HandleArrowCmd ( a_Split , a_Player )
local World = a_Player : GetWorld ( ) ;
local Pos = a_Player : GetEyePosition ( ) ;
local Speed = a_Player : GetLookVector ( ) ;
Speed : Normalize ( ) ;
Pos = Pos + Speed ;
World : CreateProjectile ( Pos.x , Pos.y , Pos.z , cProjectileEntity.pkArrow , a_Player , Speed * 10 ) ;
return true ;
2013-08-19 16:48:13 -04:00
end
2013-09-07 12:53:14 -04:00
function HandleFireballCmd ( a_Split , a_Player )
local World = a_Player : GetWorld ( ) ;
local Pos = a_Player : GetEyePosition ( ) ;
local Speed = a_Player : GetLookVector ( ) ;
Speed : Normalize ( ) ;
Pos = Pos + Speed * 2 ;
World : CreateProjectile ( Pos.x , Pos.y , Pos.z , cProjectileEntity.pkGhastFireball , a_Player , Speed * 10 ) ;
return true ;
end
2013-11-14 16:35:02 -05:00
function HandleAddExperience ( a_Split , a_Player )
2013-11-16 06:05:34 -05:00
a_Player : DeltaExperience ( 200 ) ;
2013-11-14 16:35:02 -05:00
return true ;
2013-11-15 08:07:42 -05:00
end
2013-11-15 10:23:50 -05:00
function HandleRemoveXp ( a_Split , a_Player )
2013-11-16 06:17:46 -05:00
a_Player : SetCurrentExperience ( 0 ) ;
2013-11-15 10:23:50 -05:00
return true ;
end
2013-11-20 15:54:37 -05:00
function HandleFill ( a_Split , a_Player )
local World = a_Player : GetWorld ( ) ;
local ChunkX = a_Player : GetChunkX ( ) ;
local ChunkZ = a_Player : GetChunkZ ( ) ;
World : ForEachBlockEntityInChunk ( ChunkX , ChunkZ ,
function ( a_BlockEntity )
local BlockType = a_BlockEntity : GetBlockType ( ) ;
if (
( BlockType == E_BLOCK_CHEST ) or
( BlockType == E_BLOCK_DISPENSER ) or
( BlockType == E_BLOCK_DROPPER ) or
( BlockType == E_BLOCK_FURNACE ) or
( BlockType == E_BLOCK_HOPPER )
) then
-- This block entity has items (inherits from cBlockEntityWithItems), fill it:
-- Note that we're not touching lit furnaces, don't wanna mess them up
local EntityWithItems = tolua.cast ( a_BlockEntity , " cBlockEntityWithItems " ) ;
local ItemGrid = EntityWithItems : GetContents ( ) ;
local NumSlots = ItemGrid : GetNumSlots ( ) ;
local ItemToSet = cItem ( E_ITEM_GOLD_NUGGET ) ;
for i = 0 , NumSlots - 1 do
if ( ItemGrid : GetSlot ( i ) : IsEmpty ( ) ) then
ItemGrid : SetSlot ( i , ItemToSet ) ;
end
end
end
end
) ;
return true ;
end
2013-11-22 06:26:39 -05:00
function HandleFurnaceRecipe ( a_Split , a_Player )
local HeldItem = a_Player : GetEquippedItem ( ) ;
2013-11-22 10:50:03 -05:00
local Out , NumTicks , In = cRoot : GetFurnaceRecipe ( HeldItem ) ;
2013-11-22 06:26:39 -05:00
if ( Out ~= nil ) then
a_Player : SendMessage (
" Furnace turns " .. ItemToFullString ( In ) ..
" to " .. ItemToFullString ( Out ) ..
" in " .. NumTicks .. " ticks ( " ..
tostring ( NumTicks / 20 ) .. " seconds). "
) ;
else
a_Player : SendMessage ( " There is no furnace recipe that would smelt " .. ItemToString ( HeldItem ) ) ;
end
return true ;
end
2013-11-22 10:50:03 -05:00
function HandleFurnaceFuel ( a_Split , a_Player )
local HeldItem = a_Player : GetEquippedItem ( ) ;
local NumTicks = cRoot : GetFurnaceFuelBurnTime ( HeldItem ) ;
if ( NumTicks > 0 ) then
a_Player : SendMessage (
ItemToFullString ( HeldItem ) .. " would power a furnace for " .. NumTicks ..
" ticks ( " .. tostring ( NumTicks / 20 ) .. " seconds). "
) ;
else
a_Player : SendMessage ( ItemToString ( HeldItem ) .. " will not power furnaces. " ) ;
end
return true ;
end
2013-11-22 14:11:24 -05:00
2014-01-19 17:49:19 -05:00
function HandleSched ( a_Split , a_Player )
local World = a_Player : GetWorld ( )
-- Schedule a broadcast of a countdown message:
for i = 1 , 10 do
World : ScheduleTask ( i * 20 ,
function ( a_World )
a_World : BroadcastChat ( " Countdown: " .. 11 - i )
end
)
end
-- Schedule a broadcast of the final message and a note to the originating player
2015-01-27 07:28:01 -05:00
-- Note that we CANNOT use the a_Player in the callback - what if the player disconnected?
2014-01-19 17:49:19 -05:00
-- Therefore we store the player's EntityID
local PlayerID = a_Player : GetUniqueID ( )
World : ScheduleTask ( 220 ,
function ( a_World )
a_World : BroadcastChat ( " Countdown: BOOM " )
a_World : DoWithEntityByID ( PlayerID ,
function ( a_Entity )
if ( a_Entity : IsPlayer ( ) ) then
-- Although unlikely, it is possible that this player is not the originating player
-- However, I leave this as an excercise to you to fix this "bug"
local Player = tolua.cast ( a_Entity , " cPlayer " )
Player : SendMessage ( " Countdown finished " )
end
end
)
end
)
return true
end
2014-07-18 17:11:59 -04:00
function HandleRMItem ( a_Split , a_Player )
2014-07-18 17:51:15 -04:00
-- Check params:
if ( a_Split [ 2 ] == nil ) then
2014-07-18 17:11:59 -04:00
a_Player : SendMessage ( " Usage: /rmitem <Item> [Count] " )
return true
end
2014-07-18 17:51:15 -04:00
-- Parse the item type:
2014-07-18 17:11:59 -04:00
local Item = cItem ( )
if ( not StringToItem ( a_Split [ 2 ] , Item ) ) then
a_Player : SendMessageFailure ( a_Split [ 2 ] .. " isn't a valid item " )
return true
end
2014-07-18 17:51:15 -04:00
-- Parse the optional item count
if ( a_Split [ 3 ] ~= nil ) then
2014-07-18 17:11:59 -04:00
local Count = tonumber ( a_Split [ 3 ] )
if ( Count == nil ) then
a_Player : SendMessageFailure ( a_Split [ 3 ] .. " isn't a valid number " )
return true
end
Item.m_ItemCount = Count
end
2014-07-18 17:51:15 -04:00
-- Remove the item:
local NumRemovedItems = a_Player : GetInventory ( ) : RemoveItem ( Item )
a_Player : SendMessageSuccess ( " Removed " .. NumRemovedItems .. " Items! " )
2014-07-18 17:11:59 -04:00
return true
end
2013-11-22 14:11:24 -05:00
function HandleRequest_Debuggers ( a_Request )
local FolderContents = cFile : GetFolderContents ( " ./ " ) ;
return " <p>The following objects have been returned by cFile:GetFolderContents():<ul><li> " .. table.concat ( FolderContents , " </li><li> " ) .. " </li></ul></p> " ;
end
2014-01-05 09:46:45 -05:00
2014-02-15 10:27:40 -05:00
local g_Counter = 0
local g_JavaScript =
[ [
< script >
function createXHR ( )
{
var request = false ;
try {
request = new ActiveXObject ( ' Msxml2.XMLHTTP ' ) ;
}
catch ( err2 )
{
try
{
request = new ActiveXObject ( ' Microsoft.XMLHTTP ' ) ;
}
catch ( err3 )
{
try
{
request = new XMLHttpRequest ( ) ;
}
catch ( err1 )
{
request = false ;
}
}
}
return request ;
}
function RefreshCounter ( )
{
var xhr = createXHR ( ) ;
xhr.onreadystatechange = function ( )
{
if ( xhr.readyState == 4 )
{
document.getElementById ( " cnt " ) . innerHTML = xhr.responseText ;
}
} ;
xhr.open ( " POST " , " /~webadmin/Debuggers/StressTest " , true ) ;
xhr.setRequestHeader ( ' Content-Type ' , ' application/x-www-form-urlencoded ' ) ;
xhr.send ( " counter=true " ) ;
}
setInterval ( RefreshCounter , 10 )
</ script >
] ]
function HandleRequest_StressTest ( a_Request )
if ( a_Request.PostParams [ " counter " ] ) then
g_Counter = g_Counter + 1
return tostring ( g_Counter )
end
return g_JavaScript .. " <p>The counter below should be reloading as fast as possible</p><div id='cnt'>0</div> "
end
2014-01-07 11:47:05 -05:00
function OnPluginMessage ( a_Client , a_Channel , a_Message )
LOGINFO ( " Received a plugin message from client " .. a_Client : GetUsername ( ) .. " : channel ' " .. a_Channel .. " ', message ' " .. a_Message .. " ' " ) ;
2014-01-09 08:26:44 -05:00
if ( a_Channel == " REGISTER " ) then
if ( a_Message : find ( " WECUI " ) ) then
-- The client has WorldEditCUI mod installed, test the comm by sending a few WECUI messages:
--[[
WECUI messages have the following generic format :
< shape >|< params >
If shape is p ( cuboid selection ) , the params are sent individually for each corner click and have the following format :
< point - index >|< x >|< y >|< z >|< volume >
point - index is 0 or 1 ( lclk / rclk )
volume is the 3 D volume of the current cuboid selected ( all three coords ' deltas multiplied), including the edge blocks; -1 if N/A
--]]
-- Select a 51 * 51 * 51 block cuboid:
a_Client : SendPluginMessage ( " WECUI " , " p|0|50|50|50|-1 " ) ;
a_Client : SendPluginMessage ( " WECUI " , " p|1|100|100|100|132651 " ) ; -- 132651 = 51 * 51 * 51
end
end
2014-01-05 09:46:45 -05:00
end
2014-02-09 14:40:20 -05:00
function HandleChunkStay ( a_Split , a_Player )
2014-02-11 02:54:29 -05:00
-- As an example of using ChunkStay, this call will load 3x3 chunks around the specified chunk coords,
-- then build an obsidian pillar in the middle of each one.
-- Once complete, the player will be teleported to the middle pillar
2014-02-09 14:40:20 -05:00
if ( # a_Split ~= 3 ) then
2014-02-11 02:54:29 -05:00
a_Player : SendMessageInfo ( " Usage: /cs <ChunkX> <ChunkZ> " )
2014-02-09 14:40:20 -05:00
return true
end
local ChunkX = tonumber ( a_Split [ 2 ] )
local ChunkZ = tonumber ( a_Split [ 3 ] )
if ( ( ChunkX == nil ) or ( ChunkZ == nil ) ) then
2014-02-11 02:54:29 -05:00
a_Player : SendMessageFailure ( " Invalid chunk coords. " )
2014-02-09 14:40:20 -05:00
return true
end
local World = a_Player : GetWorld ( )
local PlayerID = a_Player : GetUniqueID ( )
2014-02-11 02:54:29 -05:00
a_Player : SendMessageInfo ( " Loading chunks, stand by... " ) ;
2014-02-09 14:40:20 -05:00
2014-02-10 16:47:32 -05:00
-- Set the wanted chunks:
local Chunks = { }
2014-02-09 14:40:20 -05:00
for z = - 1 , 1 do for x = - 1 , 1 do
2014-02-10 16:47:32 -05:00
table.insert ( Chunks , { ChunkX + x , ChunkZ + z } )
2014-02-09 14:40:20 -05:00
end end
-- The function that is called when all chunks are available
2014-02-10 16:47:32 -05:00
-- Will perform the actual action with all those chunks
-- Note that the player needs to be referenced using their EntityID - in case they disconnect before the chunks load
2014-02-09 14:40:20 -05:00
local OnAllChunksAvailable = function ( )
2014-02-10 16:47:32 -05:00
LOGINFO ( " ChunkStay all chunks now available " )
2014-02-09 14:40:20 -05:00
-- Build something on the neighboring chunks, to verify:
for z = - 1 , 1 do for x = - 1 , 1 do
local BlockX = ( ChunkX + x ) * 16 + 8
local BlockZ = ( ChunkZ + z ) * 16 + 8
for y = 20 , 80 do
World : SetBlock ( BlockX , y , BlockZ , E_BLOCK_OBSIDIAN , 0 )
end
end end
-- Teleport the player there for visual inspection:
World : DoWithEntityByID ( PlayerID ,
function ( a_CallbackPlayer )
a_CallbackPlayer : TeleportToCoords ( ChunkX * 16 + 8 , 85 , ChunkZ * 16 + 8 )
2014-02-11 02:54:29 -05:00
a_CallbackPlayer : SendMessageSuccess ( " ChunkStay fully available " )
2014-02-09 14:40:20 -05:00
end
)
end
-- This function will be called for each chunk that is made available
2014-02-10 16:47:32 -05:00
-- Note that the player needs to be referenced using their EntityID - in case they disconnect before the chunks load
2014-02-09 14:40:20 -05:00
local OnChunkAvailable = function ( a_ChunkX , a_ChunkZ )
LOGINFO ( " ChunkStay now has chunk [ " .. a_ChunkX .. " , " .. a_ChunkZ .. " ] " )
World : DoWithEntityByID ( PlayerID ,
function ( a_CallbackPlayer )
2014-02-11 02:54:29 -05:00
a_CallbackPlayer : SendMessageInfo ( " ChunkStay now has chunk [ " .. a_ChunkX .. " , " .. a_ChunkZ .. " ] " )
2014-02-09 14:40:20 -05:00
end
)
end
2014-02-10 16:47:32 -05:00
-- Process the ChunkStay:
World : ChunkStay ( Chunks , OnChunkAvailable , OnAllChunksAvailable )
2014-02-09 14:40:20 -05:00
return true
end
2014-02-15 17:17:42 -05:00
function HandleCompo ( a_Split , a_Player )
-- Send one composite message to self:
local msg = cCompositeChat ( )
msg : AddTextPart ( " Hello! " , " b@e " ) -- bold yellow
msg : AddUrlPart ( " MCServer " , " http://mc-server.org " )
msg : AddTextPart ( " rules! " )
msg : AddRunCommandPart ( " Set morning " , " /time set 0 " )
a_Player : SendMessage ( msg )
-- Broadcast another one to the world:
local msg2 = cCompositeChat ( )
msg2 : AddSuggestCommandPart ( a_Player : GetName ( ) , " /tell " .. a_Player : GetName ( ) .. " " )
msg2 : AddTextPart ( " knows how to use cCompositeChat! " ) ;
a_Player : GetWorld ( ) : BroadcastChat ( msg2 )
return true
end
2014-02-18 07:06:38 -05:00
function HandleSetBiome ( a_Split , a_Player )
local Biome = biJungle
local Size = 20
local SplitSize = # a_Split
if ( SplitSize > 3 ) then
a_Player : SendMessage ( " Too many parameters. Usage: " .. a_Split [ 1 ] .. " <BiomeType> " )
return true
end
if ( SplitSize >= 2 ) then
Biome = StringToBiome ( a_Split [ 2 ] )
if ( Biome == biInvalidBiome ) then
a_Player : SendMessage ( " Unknown biome: ' " .. a_Split [ 2 ] .. " '. Command ignored. " )
return true
end
end
if ( SplitSize >= 3 ) then
Size = tostring ( a_Split [ 3 ] )
if ( Size == nil ) then
a_Player : SendMessage ( " Unknown size: ' " .. a_Split [ 3 ] .. " '. Command ignored. " )
return true
end
end
local BlockX = math.floor ( a_Player : GetPosX ( ) )
local BlockZ = math.floor ( a_Player : GetPosZ ( ) )
a_Player : GetWorld ( ) : SetAreaBiome ( BlockX - Size , BlockX + Size , BlockZ - Size , BlockZ + Size , Biome )
a_Player : SendMessage (
" Blocks { " .. ( BlockX - Size ) .. " , " .. ( BlockZ - Size ) ..
" } - { " .. ( BlockX + Size ) .. " , " .. ( BlockZ + Size ) ..
" } set to biome # " .. tostring ( Biome ) .. " . "
)
return true
end
2014-03-04 16:17:47 -05:00
2014-03-15 03:36:49 -04:00
function HandleWESel ( a_Split , a_Player )
-- Check if the selection is a cuboid:
local IsCuboid = cPluginManager : CallPlugin ( " WorldEdit " , " IsPlayerSelectionCuboid " )
if ( IsCuboid == nil ) then
a_Player : SendMessage ( cCompositeChat ( ) : SetMessageType ( mtFailure ) : AddTextPart ( " Cannot adjust selection, WorldEdit is not loaded " ) )
return true
elseif ( IsCuboid == false ) then
a_Player : SendMessage ( cCompositeChat ( ) : SetMessageType ( mtFailure ) : AddTextPart ( " Cannot adjust selection, the selection is not a cuboid " ) )
return true
end
-- Get the selection:
local SelCuboid = cCuboid ( )
local IsSuccess = cPluginManager : CallPlugin ( " WorldEdit " , " GetPlayerCuboidSelection " , a_Player , SelCuboid )
if not ( IsSuccess ) then
a_Player : SendMessage ( cCompositeChat ( ) : SetMessageType ( mtFailure ) : AddTextPart ( " Cannot adjust selection, WorldEdit reported failure while getting current selection " ) )
return true
end
-- Adjust the selection:
local NumBlocks = tonumber ( a_Split [ 2 ] or " 1 " ) or 1
SelCuboid : Expand ( NumBlocks , NumBlocks , 0 , 0 , NumBlocks , NumBlocks )
-- Set the selection:
2015-02-21 03:41:14 -05:00
IsSuccess = cPluginManager : CallPlugin ( " WorldEdit " , " SetPlayerCuboidSelection " , a_Player , SelCuboid )
2014-03-15 03:36:49 -04:00
if not ( IsSuccess ) then
a_Player : SendMessage ( cCompositeChat ( ) : SetMessageType ( mtFailure ) : AddTextPart ( " Cannot adjust selection, WorldEdit reported failure while setting new selection " ) )
return true
end
a_Player : SendMessage ( cCompositeChat ( ) : SetMessageType ( mtInformation ) : AddTextPart ( " Successfully adjusted the selection by " .. NumBlocks .. " block(s) " ) )
return true
end
2014-03-04 16:17:47 -05:00
function OnPlayerJoined ( a_Player )
-- Test composite chat chaining:
a_Player : SendMessage ( cCompositeChat ( )
: AddTextPart ( " Hello, " )
2014-09-17 14:28:30 -04:00
: AddUrlPart ( a_Player : GetName ( ) , " http://www.mc-server.org " , " u@2 " )
2014-03-04 16:17:47 -05:00
: AddSuggestCommandPart ( " , and welcome. " , " /help " , " u " )
: AddRunCommandPart ( " SetDay " , " /time set 0 " )
)
end
2014-06-02 05:54:45 -04:00
function OnProjectileHitBlock ( a_Projectile , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_BlockHitPos )
2014-07-10 12:19:05 -04:00
-- Test projectile hooks by setting the blocks they hit on fire:
2014-06-02 05:54:45 -04:00
local BlockX , BlockY , BlockZ = AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace )
local World = a_Projectile : GetWorld ( )
World : SetBlock ( BlockX , BlockY , BlockZ , E_BLOCK_FIRE , 0 )
end
2014-07-10 12:19:05 -04:00
function OnChunkUnloading ( a_World , a_ChunkX , a_ChunkZ )
-- Do not let chunk [0, 0] unload, so that it continues ticking [cWorld:SetChunkAlwaysTicked() test]
if ( ( a_ChunkX == 0 ) and ( a_ChunkZ == 0 ) ) then
return true
end
end
function OnWorldStarted ( a_World )
-- Make the chunk [0, 0] in every world keep ticking [cWorld:SetChunkAlwaysTicked() test]
a_World : ChunkStay ( { { 0 , 0 } } , nil ,
function ( )
-- The chunk is loaded, make it always tick:
a_World : SetChunkAlwaysTicked ( 0 , 0 , true )
end
)
end
2014-08-27 08:41:29 -04:00
function OnProjectileHitBlock ( a_ProjectileEntity , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_BlockHitPos )
-- This simple test is for testing issue #1326 - simply declaring this hook would crash the server upon call
LOG ( " Projectile hit block " )
LOG ( " Projectile EntityID: " .. a_ProjectileEntity : GetUniqueID ( ) )
LOG ( " Block: { " .. a_BlockX .. " , " .. a_BlockY .. " , " .. a_BlockZ .. " }, face " .. a_BlockFace )
LOG ( " HitPos: { " .. a_BlockHitPos.x .. " , " .. a_BlockHitPos.y .. " , " .. a_BlockHitPos.z .. " } " )
end
2014-09-03 11:02:16 -04:00
local PossibleItems =
{
cItem ( E_ITEM_DIAMOND ) ,
cItem ( E_ITEM_GOLD ) ,
cItem ( E_ITEM_IRON ) ,
2014-09-03 17:05:49 -04:00
cItem ( E_ITEM_DYE , 1 , E_META_DYE_BLUE ) , -- Lapis lazuli
2014-09-03 11:02:16 -04:00
cItem ( E_ITEM_COAL ) ,
}
function HandlePickups ( a_Split , a_Player )
local PlayerX = a_Player : GetPosX ( )
local PlayerY = a_Player : GetPosY ( )
local PlayerZ = a_Player : GetPosZ ( )
local World = a_Player : GetWorld ( )
2014-09-03 17:05:49 -04:00
local Range = 12
2014-09-03 11:02:16 -04:00
for x = 0 , Range do for z = 0 , Range do
2014-09-03 17:05:49 -04:00
local px = PlayerX + x - Range / 2
local pz = PlayerZ + z - Range / 2
2014-09-03 11:02:16 -04:00
local Items = cItems ( )
Items : Add ( PossibleItems [ math.random ( # PossibleItems ) ] )
2014-09-03 17:05:49 -04:00
World : SpawnItemPickups ( Items , px , PlayerY , pz , 0 )
2014-09-03 11:02:16 -04:00
end end -- for z, for x
2014-09-03 17:05:49 -04:00
return true
2014-09-03 11:02:16 -04:00
end
2015-10-21 14:59:54 -04:00
function HandlePlugMsg ( a_Split , a_Player )
local ch = a_Player : GetClientHandle ( )
ch : SendPluginMessage ( " TestCh " , " some \0 string \1 with \2 funny \3 chars " )
return true
end
2014-09-03 11:02:16 -04:00
function HandlePoof ( a_Split , a_Player )
2014-09-03 17:05:49 -04:00
local PlayerPos = Vector3d ( a_Player : GetPosition ( ) ) -- Create a copy of the position
PlayerPos.y = PlayerPos.y - 1
local Box = cBoundingBox ( PlayerPos , 4 , 2 )
local NumEntities = 0
2014-09-03 11:02:16 -04:00
a_Player : GetWorld ( ) : ForEachEntityInBox ( Box ,
function ( a_Entity )
2014-09-03 17:05:49 -04:00
if not ( a_Entity : IsPlayer ( ) ) then
local AddSpeed = a_Entity : GetPosition ( ) - PlayerPos -- Speed away from the player
a_Entity : AddSpeed ( AddSpeed * 32 / ( AddSpeed : SqrLength ( ) + 1 ) ) -- The further away, the less speed to add
NumEntities = NumEntities + 1
end
2014-09-03 11:02:16 -04:00
end
)
2014-09-03 17:05:49 -04:00
a_Player : SendMessage ( " Poof! ( " .. NumEntities .. " entities) " )
return true
2014-09-03 11:02:16 -04:00
end
2014-10-13 08:49:18 -04:00
2015-02-21 03:41:14 -05:00
-- List of hashing functions to test:
local HashFunctions =
{
{ " md5 " , md5 } ,
{ " cCryptoHash.md5 " , cCryptoHash.md5 } ,
{ " cCryptoHash.md5HexString " , cCryptoHash.md5HexString } ,
{ " cCryptoHash.sha1 " , cCryptoHash.sha1 } ,
{ " cCryptoHash.sha1HexString " , cCryptoHash.sha1HexString } ,
}
-- List of strings to try hashing:
local HashExamples =
{
" " ,
" \0 " ,
" test " ,
}
function HandleConsoleHash ( a_Split )
for _ , str in ipairs ( HashExamples ) do
LOG ( " Hashing string \" " .. str .. " \" : " )
for _ , hash in ipairs ( HashFunctions ) do
if not ( hash [ 2 ] ) then
LOG ( " Hash function " .. hash [ 1 ] .. " doesn't exist in the API! " )
else
LOG ( hash [ 1 ] .. " () = " .. hash [ 2 ] ( str ) )
end
end -- for hash - HashFunctions[]
end -- for str - HashExamples[]
return true
2014-10-13 08:49:18 -04:00
end
2014-12-11 15:21:41 -05:00
2015-03-18 11:07:15 -04:00
--- Monitors the state of the "inh" entity-spawning hook
-- if false, the hook is installed before the "inh" command processing
local isInhHookInstalled = false
function HandleConsoleInh ( a_Split , a_FullCmd )
-- Check the param:
local kindStr = a_Split [ 2 ] or " pkArrow "
local kind = cProjectileEntity [ kindStr ]
if ( kind == nil ) then
return true , " There's no projectile kind ' " .. kindStr .. " '. "
end
-- Get the world to test in:
local world = cRoot : Get ( ) : GetDefaultWorld ( )
if ( world == nil ) then
return true , " Cannot test inheritance, no default world "
end
-- Install the hook, if needed:
if not ( isInhHookInstalled ) then
cPluginManager : AddHook ( cPluginManager.HOOK_SPAWNING_ENTITY ,
function ( a_CBWorld , a_CBEntity )
LOG ( " New entity is spawning: " )
LOG ( " Lua type: ' " .. type ( a_CBEntity ) .. " ' " )
LOG ( " ToLua type: ' " .. tolua.type ( a_CBEntity ) .. " ' " )
LOG ( " GetEntityType(): ' " .. a_CBEntity : GetEntityType ( ) .. " ' " )
LOG ( " GetClass(): ' " .. a_CBEntity : GetClass ( ) .. " ' " )
end
)
isInhHookInstalled = true
end
-- Create the projectile:
LOG ( " Creating a " .. kindStr .. " projectile in world " .. world : GetName ( ) .. " ... " )
local msg
world : ChunkStay ( { { 0 , 0 } } ,
nil ,
function ( )
-- Create a projectile at {8, 100, 8}:
local entityID = world : CreateProjectile ( 8 , 100 , 8 , kind , nil , nil )
if ( entityID < 0 ) then
msg = " Cannot test inheritance, projectile creation failed. "
return
end
LOG ( " Entity created, ID # " .. entityID )
-- Call a function on the newly created entity:
local hasExecutedCallback = false
world : DoWithEntityByID (
entityID ,
function ( a_CBEntity )
LOG ( " Projectile created and found using the DoWithEntityByID() callback " )
LOG ( " Lua type: ' " .. type ( a_CBEntity ) .. " ' " )
LOG ( " ToLua type: ' " .. tolua.type ( a_CBEntity ) .. " ' " )
LOG ( " GetEntityType(): ' " .. a_CBEntity : GetEntityType ( ) .. " ' " )
LOG ( " GetClass(): ' " .. a_CBEntity : GetClass ( ) .. " ' " )
hasExecutedCallback = true
end
)
if not ( hasExecutedCallback ) then
msg = " The callback failed to execute "
return
end
msg = " Inheritance test finished "
end
)
return true , msg
end
2014-12-11 15:21:41 -05:00
function HandleConsoleLoadChunk ( a_Split )
-- Check params:
local numParams = # a_Split
if ( numParams ~= 3 ) and ( numParams ~= 4 ) then
return true , " Usage: " .. a_Split [ 1 ] .. " <ChunkX> <ChunkZ> [<WorldName>] "
end
-- Get the chunk coords:
local chunkX = tonumber ( a_Split [ 2 ] )
if ( chunkX == nil ) then
return true , " Not a number: ' " .. a_Split [ 2 ] .. " ' "
end
local chunkZ = tonumber ( a_Split [ 3 ] )
if ( chunkZ == nil ) then
return true , " Not a number: ' " .. a_Split [ 3 ] .. " ' "
end
-- Get the world:
local world
if ( a_Split [ 4 ] == nil ) then
world = cRoot : Get ( ) : GetDefaultWorld ( )
else
world = cRoot : Get ( ) : GetWorld ( a_Split [ 4 ] )
if ( world == nil ) then
return true , " There's no world named ' " .. a_Split [ 4 ] .. " '. "
end
end
-- Queue a ChunkStay for the chunk, log a message when the chunk is loaded:
world : ChunkStay ( { { chunkX , chunkZ } } , nil ,
function ( )
LOG ( " Chunk [ " .. chunkX .. " , " .. chunkZ .. " ] is loaded " )
end
)
return true
end
function HandleConsolePrepareChunk ( a_Split )
-- Check params:
local numParams = # a_Split
if ( numParams ~= 3 ) and ( numParams ~= 4 ) then
return true , " Usage: " .. a_Split [ 1 ] .. " <ChunkX> <ChunkZ> [<WorldName>] "
end
-- Get the chunk coords:
local chunkX = tonumber ( a_Split [ 2 ] )
if ( chunkX == nil ) then
return true , " Not a number: ' " .. a_Split [ 2 ] .. " ' "
end
local chunkZ = tonumber ( a_Split [ 3 ] )
if ( chunkZ == nil ) then
return true , " Not a number: ' " .. a_Split [ 3 ] .. " ' "
end
-- Get the world:
local world
if ( a_Split [ 4 ] == nil ) then
world = cRoot : Get ( ) : GetDefaultWorld ( )
else
world = cRoot : Get ( ) : GetWorld ( a_Split [ 4 ] )
if ( world == nil ) then
return true , " There's no world named ' " .. a_Split [ 4 ] .. " '. "
end
end
2014-12-12 07:32:52 -05:00
-- Queue the chunk for preparing, log a message when prepared:
world : PrepareChunk ( chunkX , chunkZ ,
function ( a_CBChunkX , a_CBChunkZ )
LOG ( " Chunk [ " .. chunkX .. " , " .. chunkZ .. " ] has been prepared " )
end
)
2014-12-11 15:21:41 -05:00
return true
end
2015-02-21 03:41:14 -05:00
function HandleConsoleSchedule ( a_Split )
local prev = os.clock ( )
LOG ( " Scheduling a task for 2 seconds in the future (current os.clock is " .. prev .. " ) " )
cRoot : Get ( ) : GetDefaultWorld ( ) : ScheduleTask ( 40 ,
function ( )
local current = os.clock ( )
local diff = current - prev
LOG ( " Scheduled function is called. Current os.clock is " .. current .. " , difference is " .. diff .. " ) " )
end
)
return true , " Task scheduled "
end
2015-03-18 08:50:28 -04:00
2015-06-04 16:22:43 -04:00
--- Returns the square of the distance from the specified point to the specified line
local function SqDistPtFromLine ( x , y , x1 , y1 , x2 , y2 )
local dx = x - x1
local dy = y - y1
local px = x2 - x1
local py = y2 - y1
local ss = px * dx + py * dy
local ds = px * px + py * py
if ( ss < 0 ) then
-- Return sqdistance from point 1
return dx * dx + dy * dy
end
if ( ss > ds ) then
-- Return sqdistance from point 2
return ( ( x2 - x ) * ( x2 - x ) + ( y2 - y ) * ( y2 - y ) )
end
-- Return sqdistance from the line
if ( ( px * px + py * py ) == 0 ) then
return dx * dx + dy * dy
else
return ( py * dx - px * dy ) * ( py * dx - px * dy ) / ( px * px + py * py )
end
end
function HandleConsoleTestTracer ( a_Split , a_EntireCmd )
-- Check required params:
if not ( a_Split [ 7 ] ) then
return true , " Usage: " .. a_Split [ 1 ] .. " <x1> <y1> <z1> <x2> <y2> <z2> [<WorldName>] "
end
local Coords = { }
for i = 1 , 6 do
local v = tonumber ( a_Split [ i + 1 ] )
if not ( v ) then
return true , " Parameter " .. ( i + 1 ) .. " ( " .. tostring ( a_Split [ i + 1 ] ) .. " ) not a number "
end
Coords [ i ] = v
end
-- Get the world in which to test:
local World
if ( a_Split [ 8 ] ) then
World = cRoot : GetWorld ( a_Split [ 2 ] )
else
World = cRoot : Get ( ) : GetDefaultWorld ( )
end
if not ( World ) then
return true , " No such world "
end
-- Define the callbacks to use for tracing:
local Callbacks =
{
OnNextBlock = function ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta , a_EntryFace )
LOG ( string.format ( " {%d, %d, %d}: %s " , a_BlockX , a_BlockY , a_BlockZ , ItemToString ( cItem ( a_BlockType , 1 , a_BlockMeta ) ) ) )
end ,
OnNextBlockNoData = function ( a_BlockX , a_BlockY , a_BlockZ , a_EntryFace )
LOG ( string.format ( " {%d, %d, %d} (no data) " , a_BlockX , a_BlockY , a_BlockZ ) )
end ,
OnNoChunk = function ( )
LOG ( " Chunk not loaded " )
end ,
OnNoMoreHits = function ( )
LOG ( " Trace finished " )
end ,
OnOutOfWorld = function ( )
LOG ( " Out of world " )
end ,
OnIntoWorld = function ( )
LOG ( " Into world " )
end ,
}
-- Approximate the chunks needed for the trace by iterating over all chunks and measuring their center's distance from the traced line
local Chunks = { }
local sx = math.floor ( Coords [ 1 ] / 16 )
local sz = math.floor ( Coords [ 3 ] / 16 )
local ex = math.floor ( Coords [ 4 ] / 16 )
local ez = math.floor ( Coords [ 6 ] / 16 )
local sgnx = ( sx < ex ) and 1 or - 1
local sgnz = ( sz < ez ) and 1 or - 1
for z = sz , ez , sgnz do
local ChunkCenterZ = z * 16 + 8
for x = sx , ex , sgnx do
local ChunkCenterX = x * 16 + 8
local sqdist = SqDistPtFromLine ( ChunkCenterX , ChunkCenterZ , Coords [ 1 ] , Coords [ 3 ] , Coords [ 4 ] , Coords [ 6 ] )
if ( sqdist <= 128 ) then
table.insert ( Chunks , { x , z } )
end
end
end
-- Load the chunks and do the trace once loaded:
World : ChunkStay ( Chunks ,
nil ,
function ( )
cLineBlockTracer : Trace ( World , Callbacks , Coords [ 1 ] , Coords [ 2 ] , Coords [ 3 ] , Coords [ 4 ] , Coords [ 5 ] , Coords [ 6 ] )
end
)
return true
end
2015-03-18 08:50:28 -04:00
function HandleConsoleBBox ( a_Split )
local bbox = cBoundingBox ( 0 , 10 , 0 , 10 , 0 , 10 )
local v1 = Vector3d ( 1 , 1 , 1 )
local v2 = Vector3d ( 5 , 5 , 5 )
local v3 = Vector3d ( 11 , 11 , 11 )
if ( bbox : IsInside ( v1 ) ) then
LOG ( " v1 is inside bbox " )
else
LOG ( " v1 is not inside bbox " )
end
if ( bbox : IsInside ( v2 ) ) then
LOG ( " v2 is inside bbox " )
else
LOG ( " v2 is not inside bbox " )
end
if ( bbox : IsInside ( v3 ) ) then
LOG ( " v3 is inside bbox " )
else
LOG ( " v3 is not inside bbox " )
end
if ( bbox : IsInside ( v1 , v2 ) ) then
LOG ( " v1*v2 is inside bbox " )
else
LOG ( " v1*v2 is not inside bbox " )
end
if ( bbox : IsInside ( v2 , v1 ) ) then
LOG ( " v2*v1 is inside bbox " )
else
LOG ( " v2*v1 is not inside bbox " )
end
if ( bbox : IsInside ( v1 , v3 ) ) then
LOG ( " v1*v3 is inside bbox " )
else
LOG ( " v1*v3 is not inside bbox " )
end
if ( bbox : IsInside ( v2 , v3 ) ) then
LOG ( " v2*v3 is inside bbox " )
else
LOG ( " v2*v3 is not inside bbox " )
end
return true
end