2013-07-30 16:26:06 +01:00
2013-09-07 18:33:15 +02:00
-- Global variables
2013-07-29 12:13:03 +01: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 22:48:13 +02:00
g_ShowFoodStats = false ; -- When true, each player's food stats are sent to them every 10 ticks
2013-07-29 12:13:03 +01:00
2013-07-30 16:26:06 +01:00
2013-07-29 12:13:03 +01:00
function Initialize ( Plugin )
Plugin : SetName ( "Debuggers" )
Plugin : SetVersion ( 1 )
2013-08-21 21:42:39 +02:00
--[[
-- Test multiple hook handlers:
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick1);
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2);
--]]
2014-02-15 16:27:40 +01: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 );
PM : AddHook ( cPluginManager.HOOK_PLUGIN_MESSAGE , OnPluginMessage );
2014-03-04 22:17:47 +01:00
PM : AddHook ( cPluginManager.HOOK_PLAYER_JOINED , OnPlayerJoined )
2014-02-15 16:27:40 +01:00
2014-03-28 13:34:32 +01:00
-- _X: Disabled so that the normal operation doesn't interfere with anything
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
2013-11-20 21:54:37 +01:00
PM : BindCommand ( "/le" , "debuggers" , HandleListEntitiesCmd , "- Shows a list of all the loaded entities" );
PM : BindCommand ( "/ke" , "debuggers" , HandleKillEntitiesCmd , "- Kills all the loaded entities" );
PM : BindCommand ( "/wool" , "debuggers" , HandleWoolCmd , "- Sets all your armor to blue wool" );
PM : BindCommand ( "/testwnd" , "debuggers" , HandleTestWndCmd , "- Opens up a window using plugin API" );
PM : BindCommand ( "/gc" , "debuggers" , HandleGCCmd , "- Activates the Lua garbage collector" );
PM : BindCommand ( "/fast" , "debuggers" , HandleFastCmd , "- Switches between fast and normal movement speed" );
PM : BindCommand ( "/dash" , "debuggers" , HandleDashCmd , "- Switches between fast and normal sprinting speed" );
PM : BindCommand ( "/hunger" , "debuggers" , HandleHungerCmd , "- Lists the current hunger-related variables" );
PM : BindCommand ( "/poison" , "debuggers" , HandlePoisonCmd , "- Sets food-poisoning for 15 seconds" );
PM : BindCommand ( "/starve" , "debuggers" , HandleStarveCmd , "- Sets the food level to zero" );
PM : BindCommand ( "/fl" , "debuggers" , HandleFoodLevelCmd , "- Sets the food level to the given value" );
PM : BindCommand ( "/spidey" , "debuggers" , HandleSpideyCmd , "- Shoots a line of web blocks until it hits non-air" );
PM : BindCommand ( "/ench" , "debuggers" , HandleEnchCmd , "- Provides an instant dummy enchantment window" );
PM : BindCommand ( "/fs" , "debuggers" , HandleFoodStatsCmd , "- Turns regular foodstats message on or off" );
PM : BindCommand ( "/arr" , "debuggers" , HandleArrowCmd , "- Creates an arrow going away from the player" );
PM : BindCommand ( "/fb" , "debuggers" , HandleFireballCmd , "- Creates a ghast fireball as if shot by the player" );
PM : BindCommand ( "/xpa" , "debuggers" , HandleAddExperience , "- Adds 200 experience to the player" );
PM : BindCommand ( "/xpr" , "debuggers" , HandleRemoveXp , "- Remove all xp" );
PM : BindCommand ( "/fill" , "debuggers" , HandleFill , "- Fills all block entities in current chunk with junk" );
2013-11-22 12:26:39 +01:00
PM : BindCommand ( "/fr" , "debuggers" , HandleFurnaceRecipe , "- Shows the furnace recipe for the currently held item" );
2013-11-22 16:50:03 +01:00
PM : BindCommand ( "/ff" , "debuggers" , HandleFurnaceFuel , "- Shows how long the currently held item would burn in a furnace" );
2014-01-19 23:49:19 +01:00
PM : BindCommand ( "/sched" , "debuggers" , HandleSched , "- Schedules a simple countdown using cWorld:ScheduleTask()" );
2014-02-09 20:40:20 +01:00
PM : BindCommand ( "/cs" , "debuggers" , HandleChunkStay , "- Tests the ChunkStay Lua integration for the specified chunk coords" );
2014-02-15 23:17:42 +01:00
PM : BindCommand ( "/compo" , "debuggers" , HandleCompo , "- Tests the cCompositeChat bindings" )
2014-03-15 08:36:49 +01:00
PM : BindCommand ( "/sb" , "debuggers" , HandleSetBiome , "- Sets the biome around you to the specified one" )
PM : BindCommand ( "/wesel" , "debuggers" , HandleWESel , "- Expands the current WE selection by 1 block in X/Z" )
2013-07-29 12:13:03 +01:00
2014-02-15 16:27:40 +01:00
Plugin : AddWebTab ( "Debuggers" , HandleRequest_Debuggers )
Plugin : AddWebTab ( "StressTest" , HandleRequest_StressTest )
2013-11-22 20:11:24 +01:00
2013-07-29 12:13:03 +01:00
-- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
LOG ( "Initialized " .. Plugin : GetName () .. " v." .. Plugin : GetVersion ())
2014-04-02 11:58:19 +02:00
-- TestBlockAreas()
-- TestSQLiteBindings()
-- TestExpatBindings()
-- TestPluginCalls()
2014-01-21 22:59:08 +01:00
2014-03-07 09:18:10 +01:00
TestBlockAreasString ()
2014-04-02 11:58:19 +02:00
TestStringBase64 ()
2014-03-31 22:51:14 +02: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 09:18:10 +01:00
2013-07-29 12:13:03 +01:00
return true
end ;
2013-07-30 16:26:06 +01:00
2014-01-21 22:59:08 +01: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.
-- Use the old, deprecated and unsafe method:
local Core = cPluginManager : Get (): GetPlugin ( "Core" )
if ( Core ~= nil ) then
LOGINFO ( "Calling Core::ReturnColorFromChar() the old-fashioned way..." )
local Gray = Core : Call ( "ReturnColorFromChar" , nil , "8" )
if ( Gray ~= cChatColor.Gray ) then
LOGWARNING ( "Call failed, exp " .. cChatColor.Gray .. ", got " .. ( Gray or "<nil>" ))
else
LOGINFO ( "Call succeeded" )
end
end
-- Use the new method:
LOGINFO ( "Calling Core::ReturnColorFromChar() the recommended way..." )
local Gray = cPluginManager : CallPlugin ( "Core" , "ReturnColorFromChar" , nil , "8" )
if ( Gray ~= cChatColor.Gray ) then
LOGWARNING ( "Call failed, exp " .. cChatColor.Gray .. ", got " .. ( Gray or "<nil>" ))
else
LOGINFO ( "Call succeeded" )
end
end
2013-07-30 16:26:06 +01:00
2013-07-29 12:13:03 +01: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 09:18:10 +01: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 13:54:32 +01:00
local f = io.open ( "schematics/StringTest.schematic" , "wb" )
2014-03-07 09:18:10 +01: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 13:54:32 +01:00
f = io.open ( "schematics/StringTest.schematic" , "rb" )
2014-03-07 09:18:10 +01:00
Data = f : read ( "*all" )
if not ( BA2 : LoadFromSchematicString ( Data )) then
LOG ( "Cannot load schematic from string" )
end
end
2014-04-02 11:58:19 +02: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
2013-07-29 12:13:03 +01: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
local Type , Meta , Valid = Player : GetWorld (): GetBlockTypeMeta ( BlockX , BlockY , BlockZ , Type , Meta );
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 );
Valid , Type , Meta = Player : GetWorld (): GetBlockTypeMeta ( X , Y , Z , Type , Meta );
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
LOG ( Receiver : GetClass () .. " was dealt " .. DamageTypeToString ( TDI.DamageType ) .. " damage: Raw " .. TDI.RawDamage .. ", Final " .. TDI.FinalDamage .. " (" .. ( TDI.RawDamage - TDI.FinalDamage ) .. " covered by armor)" );
return false ;
end
2013-08-21 21:42:39 +02: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 12:13:03 +01: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 22:48:13 +02:00
function OnWorldTick ( a_World , a_Dt )
2013-09-07 18:33:15 +02:00
-- Report food stats, if switched on:
2013-08-19 22:48:13 +02: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 12:13:03 +01:00
function OnChat ( a_Player , a_Message )
return false , "blabla " .. a_Message ;
end
2013-08-02 08:44:06 +02: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 12:13:03 +01:00
end
2013-12-29 12:51:58 +01:00
function OnPluginsLoaded ()
LOG ( "All plugins loaded" );
end
2013-11-15 09:49:40 +01: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 08:44:06 +02:00
end
2013-07-29 12:13:03 +01: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
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 12:03:34 +02: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 12:13:03 +01: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 13:44:57 +01:00
local WindowType = cWindow.wtHopper ;
2013-07-29 12:13:03 +01: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 22:48:13 +02: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 12:13:03 +01:00
return true ;
end
2013-08-07 14:34:56 +02: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 13:26:37 +02:00
function HandleEnchCmd ( a_Split , a_Player )
2013-10-28 13:44:57 +01:00
local Wnd = cLuaWindow ( cWindow.wtEnchantment , 1 , 1 , "Ench" );
2013-08-19 22:48:13 +02:00
a_Player : OpenWindow ( Wnd );
Wnd : SetProperty ( 0 , 10 );
Wnd : SetProperty ( 1 , 15 );
Wnd : SetProperty ( 2 , 25 );
2013-08-22 09:07:31 +02:00
return true ;
2013-08-19 22:48:13 +02:00
end
function HandleFoodStatsCmd ( a_Split , a_Player )
g_ShowFoodStats = not ( g_ShowFoodStats );
2013-08-22 09:07:31 +02: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 22:48:13 +02:00
end
2013-09-07 18:53:14 +02: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-15 08:35:02 +11:00
function HandleAddExperience ( a_Split , a_Player )
2013-11-16 22:05:34 +11:00
a_Player : DeltaExperience ( 200 );
2013-11-15 08:35:02 +11:00
return true ;
2013-11-16 00:07:42 +11:00
end
2013-11-16 02:23:50 +11:00
function HandleRemoveXp ( a_Split , a_Player )
2013-11-16 22:17:46 +11:00
a_Player : SetCurrentExperience ( 0 );
2013-11-16 02:23:50 +11:00
return true ;
end
2013-11-20 21:54:37 +01: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 12:26:39 +01:00
function HandleFurnaceRecipe ( a_Split , a_Player )
local HeldItem = a_Player : GetEquippedItem ();
2013-11-22 16:50:03 +01:00
local Out , NumTicks , In = cRoot : GetFurnaceRecipe ( HeldItem );
2013-11-22 12:26:39 +01: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 16:50:03 +01: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 20:11:24 +01:00
2014-01-19 23:49:19 +01: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
-- Note that we CANNOT us the a_Player in the callback - what if the player disconnected?
-- 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
2013-11-22 20:11:24 +01: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 15:46:45 +01:00
2014-02-15 16:27:40 +01: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 17:47:05 +01: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 14:26:44 +01: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 3D 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 15:46:45 +01:00
end
2014-02-09 20:40:20 +01:00
function HandleChunkStay ( a_Split , a_Player )
2014-02-11 08:54:29 +01: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 20:40:20 +01:00
if ( # a_Split ~= 3 ) then
2014-02-11 08:54:29 +01:00
a_Player : SendMessageInfo ( "Usage: /cs <ChunkX> <ChunkZ>" )
2014-02-09 20:40:20 +01: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 08:54:29 +01:00
a_Player : SendMessageFailure ( "Invalid chunk coords." )
2014-02-09 20:40:20 +01:00
return true
end
local World = a_Player : GetWorld ()
local PlayerID = a_Player : GetUniqueID ()
2014-02-11 08:54:29 +01:00
a_Player : SendMessageInfo ( "Loading chunks, stand by..." );
2014-02-09 20:40:20 +01:00
2014-02-10 22:47:32 +01:00
-- Set the wanted chunks:
local Chunks = {}
2014-02-09 20:40:20 +01:00
for z = - 1 , 1 do for x = - 1 , 1 do
2014-02-10 22:47:32 +01:00
table.insert ( Chunks , { ChunkX + x , ChunkZ + z })
2014-02-09 20:40:20 +01:00
end end
-- The function that is called when all chunks are available
2014-02-10 22:47:32 +01: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 20:40:20 +01:00
local OnAllChunksAvailable = function ()
2014-02-10 22:47:32 +01:00
LOGINFO ( "ChunkStay all chunks now available" )
2014-02-09 20:40:20 +01: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 08:54:29 +01:00
a_CallbackPlayer : SendMessageSuccess ( "ChunkStay fully available" )
2014-02-09 20:40:20 +01:00
end
)
end
-- This function will be called for each chunk that is made available
2014-02-10 22:47:32 +01:00
-- Note that the player needs to be referenced using their EntityID - in case they disconnect before the chunks load
2014-02-09 20:40:20 +01: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 08:54:29 +01:00
a_CallbackPlayer : SendMessageInfo ( "ChunkStay now has chunk [" .. a_ChunkX .. ", " .. a_ChunkZ .. "]" )
2014-02-09 20:40:20 +01:00
end
)
end
2014-02-10 22:47:32 +01:00
-- Process the ChunkStay:
World : ChunkStay ( Chunks , OnChunkAvailable , OnAllChunksAvailable )
2014-02-09 20:40:20 +01:00
return true
end
2014-02-15 23:17:42 +01: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 13:06:38 +01: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 22:17:47 +01:00
2014-03-15 08:36:49 +01: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:
local IsSuccess = cPluginManager : CallPlugin ( "WorldEdit" , "SetPlayerCuboidSelection" , a_Player , SelCuboid )
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 22:17:47 +01:00
function OnPlayerJoined ( a_Player )
-- Test composite chat chaining:
a_Player : SendMessage ( cCompositeChat ()
: AddTextPart ( "Hello, " )
: AddUrlPart ( a_Player : GetName (), "www.mc-server.org" , "u@2" )
: AddSuggestCommandPart ( ", and welcome." , "/help" , "u" )
: AddRunCommandPart ( " SetDay" , "/time set 0" )
)
end