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 ( )
2016-08-23 07:20:43 -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
2016-01-01 15:05:09 -05:00
msg : AddUrlPart ( " Cuberite " , " http://cuberite.org " )
2014-02-15 17:17:42 -05:00
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, " )
2016-01-29 14:47:00 -05:00
: AddUrlPart ( a_Player : GetName ( ) , " http://cuberite.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 ( )
2016-03-01 11:25:45 -05:00
LOG ( " Scheduling a task for 5 seconds in the future (current os.clock is " .. prev .. " ) " )
cRoot : Get ( ) : GetDefaultWorld ( ) : ScheduleTask ( 5 * 20 ,
2015-02-21 03:41:14 -05:00
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
2016-06-05 12:23:16 -04:00
function HandleConsoleTestBbox ( a_Split , a_EntireCmd )
-- Test bbox intersection:
local bbox1 = cBoundingBox ( 0 , 5 , 0 , 5 , 0 , 5 )
local bbox2 = cBoundingBox ( bbox1 ) -- Make a copy
bbox2 : Move ( 20 , 20 , 20 )
local bbox3 = cBoundingBox ( bbox1 ) -- Make a copy
bbox3 : Move ( 2 , 2 , 2 )
local doesIntersect , intersection = bbox1 : Intersect ( bbox2 )
LOG ( " Bbox 2 intersection: " .. tostring ( doesIntersect ) )
LOG ( " Intersection type: " .. type ( intersection ) .. " / " .. tolua.type ( intersection ) )
if ( intersection ) then
LOG ( " { " .. intersection : GetMinX ( ) .. " , " .. intersection : GetMinY ( ) .. " , " .. intersection : GetMinZ ( ) .. " } " )
LOG ( " { " .. intersection : GetMaxX ( ) .. " , " .. intersection : GetMaxY ( ) .. " , " .. intersection : GetMaxZ ( ) .. " } " )
end
doesIntersect , intersection = bbox1 : Intersect ( bbox3 )
LOG ( " Bbox 3 intersection: " .. tostring ( doesIntersect ) )
LOG ( " Intersection type: " .. type ( intersection ) .. " / " .. tolua.type ( intersection ) )
if ( intersection ) then
LOG ( " { " .. intersection : GetMinX ( ) .. " , " .. intersection : GetMinY ( ) .. " , " .. intersection : GetMinZ ( ) .. " } " )
LOG ( " { " .. intersection : GetMaxX ( ) .. " , " .. intersection : GetMaxY ( ) .. " , " .. intersection : GetMaxZ ( ) .. " } " )
end
-- Test line intersection:
local lines =
{
{ Vector3d ( 5 , 0 , 5 ) , Vector3d ( 5 , 1 , 5 ) } ,
{ Vector3d ( 0 , 0 , 0 ) , Vector3d ( 0 , 1 , 0 ) } ,
}
for idx , line in ipairs ( lines ) do
local doesIntersect , coeff , face = bbox2 : CalcLineIntersection ( line [ 1 ] , line [ 2 ] )
LOG ( " Line " .. idx .. " intersection: " .. tostring ( doesIntersect ) )
LOG ( " Coeff: " .. tostring ( coeff ) )
LOG ( " Face: " .. tostring ( face ) )
local doesIntersect2 , coeff2 , face2 = cBoundingBox : CalcLineIntersection ( bbox2 : GetMin ( ) , bbox2 : GetMax ( ) , line [ 1 ] , line [ 2 ] )
assert ( doesIntersect == doesIntersect2 )
assert ( coeff == coeff2 )
assert ( face == face2 )
end
return true
end
2016-05-30 19:01:55 -04:00
function HandleConsoleTestCall ( a_Split , a_EntireCmd )
LOG ( " Testing inter-plugin calls " )
LOG ( " Note: These will fail if the Core plugin is not enabled " )
-- Test calling the HandleConsoleWeather handler:
local pm = cPluginManager
LOG ( " Calling Core's HandleConsoleWeather " )
local isSuccess = pm : CallPlugin ( " Core " , " HandleConsoleWeather " ,
{
" /weather " ,
" rain " ,
}
)
if ( type ( isSuccess ) == " boolean " ) then
LOG ( " Success " )
else
LOG ( " FAILED " )
end
-- Test injecting some code:
LOG ( " Injecting code into the Core plugin " )
isSuccess = pm : CallPlugin ( " Core " , " dofile " , pm : GetCurrentPlugin ( ) : GetLocalFolder ( ) .. " /Inject.lua " )
if ( type ( isSuccess ) == " boolean " ) then
LOG ( " Success " )
else
LOG ( " FAILED " )
end
-- Test the full capabilities of the table-passing API, using the injected function:
LOG ( " Calling injected code " )
isSuccess = pm : CallPlugin ( " Core " , " injectedPrintParams " ,
{
" test " ,
nil ,
{
" test " ,
" test "
} ,
[ 10 ] = " test " ,
[ " test " ] = " test " ,
[ { " test " } ] = " test " ,
[ true ] = " test " ,
}
)
if ( type ( isSuccess ) == " boolean " ) then
LOG ( " Success " )
else
LOG ( " FAILED " )
end
return true
end
2015-12-23 05:08:39 -05:00
function HandleConsoleTestJson ( a_Split , a_EntireCmd )
LOG ( " Testing Json parsing... " )
2016-07-10 02:16:44 -04:00
local t1 = cJson : Parse ( [[{"a": 1, "b": "2", "c": [3, "4", 5], "d": true }]] )
2015-12-23 05:08:39 -05:00
assert ( t1.a == 1 )
assert ( t1.b == " 2 " )
assert ( t1.c [ 1 ] == 3 )
assert ( t1.c [ 2 ] == " 4 " )
assert ( t1.c [ 3 ] == 5 )
2016-07-10 02:16:44 -04:00
assert ( t1.d == true )
LOG ( " Json parsing example 1 successful " )
2015-12-23 05:08:39 -05:00
local t2 , msg = cJson : Parse ( [[{"some": invalid, json}]] )
assert ( t2 == nil )
assert ( type ( msg ) == " string " )
2016-07-10 02:16:44 -04:00
LOG ( " Json parsing an invalid string: Error message returned: " .. msg )
2015-12-23 05:08:39 -05:00
LOG ( " Json parsing test succeeded " )
LOG ( " Testing Json serializing... " )
2016-07-10 02:16:44 -04:00
local s1 = cJson : Serialize ( { a = 1 , b = " 2 " , c = { 3 , " 4 " , 5 } , d = true } , { indentation = " " } )
2015-12-23 05:08:39 -05:00
LOG ( " Serialization result: " .. ( s1 or " <nil> " ) )
LOG ( " Json serializing test succeeded " )
return true
end
2015-06-04 16:22:43 -04:00
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
2016-08-23 07:20:43 -04:00
function HandleConsoleTestUrlClient ( a_Split , a_EntireCmd )
local url = a_Split [ 2 ] or " https://github.com "
local isSuccess , msg = cUrlClient : Get ( url ,
function ( a_Body , a_SecondParam )
if not ( a_Body ) then
-- An error has occurred, a_SecondParam is the error message
LOG ( " Error while retrieving URL \" " .. url .. " \" : " .. ( a_SecondParam or " <no message> " ) )
return
end
-- Body received, a_SecondParam is the HTTP headers dictionary-table
assert ( type ( a_Body ) == " string " )
assert ( type ( a_SecondParam ) == " table " )
LOG ( " URL body received, length is " .. string.len ( a_Body ) .. " bytes and there are these headers: " )
for k , v in pairs ( a_SecondParam ) do
LOG ( " \" " .. k .. " \" : \" " .. v .. " \" " )
end
LOG ( " (headers list finished) " )
end
)
if not ( isSuccess ) then
LOG ( " cUrlClient request failed: " .. ( msg or " <no message> " ) )
end
return true
end
2015-12-25 12:50:25 -05:00
function HandleConsoleTestUrlParser ( a_Split , a_EntireCmd )
LOG ( " Testing cUrlParser... " )
local UrlsToTest =
{
" invalid URL " ,
" https://github.com " ,
" ftp://anonymous:user@example.com@ftp.cuberite.org:9921/releases/2015/2015-12-25.zip " ,
" ftp://anonymous:user:name:with:colons@example.com@ftp.cuberite.org:9921 " ,
" http://google.com/ " ,
" http://google.com/?q=cuberite " ,
" http://google.com/search?q=cuberite " ,
" http://google.com/some/search?q=cuberite#results " ,
" http://google.com/?q=cuberite#results " ,
" http://google.com/#results " ,
" ftp://cuberite.org:9921/releases/2015/2015-12-25.zip " ,
" mailto:support@cuberite.org " ,
}
for _ , u in ipairs ( UrlsToTest ) do
LOG ( " URL: " .. u )
local scheme , username , password , host , port , path , query , fragment = cUrlParser : Parse ( u )
if not ( scheme ) then
LOG ( " Error: " .. ( username or " <nil> " ) )
else
LOG ( " Scheme = " .. scheme )
LOG ( " Username = " .. username )
LOG ( " Password = " .. password )
LOG ( " Host = " .. host )
LOG ( " Port = " .. port )
LOG ( " Path = " .. path )
LOG ( " Query = " .. query )
LOG ( " Fragment = " .. fragment )
end
end
LOG ( " cUrlParser test complete " )
return true
end
2015-12-28 05:08:46 -05:00
function HandleConsoleUuid ( a_Split , a_EntireCmd )
-- Check params:
local playerName = a_Split [ 2 ]
if not ( playerName ) then
return true , " Usage: uuid <PlayerName> "
end
-- Query with cache:
LOG ( " Player " .. playerName .. " : " )
local cachedUuid = cMojangAPI : GetUUIDFromPlayerName ( playerName , true )
if not ( cachedUuid ) then
LOG ( " - not in the UUID cache " )
else
LOG ( " - in the cache: \" " .. cachedUuid .. " \" " )
end
-- Query online:
local onlineUuid = cMojangAPI : GetUUIDFromPlayerName ( playerName , false )
if not ( onlineUuid ) then
LOG ( " - UUID not available online " )
else
LOG ( " - online: \" " .. onlineUuid .. " \" " )
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
1.9 / 1.9.2 / 1.9.3 / 1.9.4 protocol support (#3135)
* Semistable update to 15w31a
I'm going through snapshots in a sequential order since it should make things easier, and since protocol version history is written.
* Update to 15w34b protocol
Also, fix an issue with the Entity Equipment packet from the past version. Clients are able to connect and do stuff!
* Partially update to 15w35e
Chunk data doesn't work, but the client joins. I'm waiting to do chunk data because chunk data has an incomplete format until 15w36d.
* Add '/blk' debug command
This command lets one see what block they are looking at, and makes figuring out what's supposed to be where in a highly broken chunk possible.
* Fix CRLF normalization in CheckBasicStyle.lua
Normally, this doesn't cause an issue, but when running from cygwin, it detects the CR as whitespace and creates thousands of violations for every single line. Lua, when run on windows, will normalize automatically, but when run via cygwin, it won't.
The bug was simply that gsub was returning a replaced version, but not changing the parameter, so the replaced version was ignored.
* Update to 15w40b
This includes chunk serialization. Fully functional chunk serialization for 1.9.
I'm not completely happy with the chunk serialization as-is (correct use of palettes would be great), but cuberite also doesn't skip sending empty chunks so this performance optimization should probably come later. The creation of a full buffer is suboptimal, but it's the easiest way to implement this code.
* Write long-by-long rather than creating a buffer
This is a bit faster and should be equivalent. However, the code still doesn't look too good.
* Update to 15w41a protocol
This includes the new set passengers packet, which works off of the ridden entity, not the rider. That means, among other things, that information about the previously ridden vehicle is needed when detaching. So a new method with that info was added.
* Update to 15w45a
* 15w51b protocol
* Update to 1.9.0 protocol
Closes #3067. There are still a few things that need to be worked out (picking up items, effects, particles, and most importantly inventory), but in general this should work. I'll make a few more changes tomorrow to get the rest of the protocol set up, along with 1.9.1/1.9.2 (which did make a few changes). Chunks, however, _are_ working, along with most other parts of the game (placing/breaking blocks).
* Fix item pickup packet not working
That was a silly mistake, but at least it was an easy one.
* 1.9.2 protocol support
* Fix version info found in server list ping
Thus, the client reports that it can connect rather than saying that the server is out of date. This required creating separate classes for 1.9.1 and 1.9.2, unfortunately.
* Fix build errors generated by clang
These didn't happen in MSVC.
* Add protocol19x.cpp and protocol19x.h to CMakeLists
* Ignore warnings in protocol19x that are ignored in protocol18x
* Document BLOCK_FACE and DIG_STATUS constants
* Fix BLOCK_FACE links and add separate section for DIG_STATUS
* Fix bat animation and object spawning
The causes of both of these are explained in #3135, but the gist is that both were typos.
* Implement Use Item packet
This means that buckets, bows, fishing rods, and several other similar items now work when not looking at a block.
* Handle DIG_STATUS_SWAP_ITEM_IN_HAND
* Add support for spawn eggs and potions
The items are transformed from the 1.9 version to the 1.8 version when reading and transformed back when sending.
* Remove spammy potion debug logging
* Fix wolf collar color metadata
The wrong type was being used, causing several clientside issues (including the screen going black).
* Fix 1.9 chunk sending in the nether
The nether and the end don't send skylight.
* Fix clang build errors
* Fix water bottles becoming mundane potions
This happened because the can become splash potion bit got set incorrectly. Water bottles and mundane potions are only differentiated by the fact that water bottles have a metadata of 0, so setting that bit made it a mundane potion.
Also add missing break statements to the read item NBT switch, which would otherwise break items with custom names and also cause incorrect "Unimplemented NBT data when parsing!" logging.
* Copy Protocol18x as Protocol19x
Aditionally, method and class names have been swapped to clean up other diffs. This commit is only added to make the following diffs more readable; it doesn't make any other changes (beyond class names).
* Make thrown potions use the correct appearence
This was caused by potions now using metadata.
* Add missing api doc for cSplashPotionEntity::GetItem
* Fix compile error in SplashPotionEntity.cpp
* Fix fix of cSplashPotionEntity API doc
* Temporarilly disable fall damage particles
These were causing issues in 1.9 due to the changed effect ID.
* Properly send a kick packet when connecting with an invalid version
This means that the client no longer waits on the server screen with no indication whatsoever. However, right now the server list ping isn't implemented for unknown versions, so it'll only load "Old" on the ping.
I also added a GetVarIntSize method to cByteBuffer. This helps clean up part of the code here (and I think it could clean up other parts), but it may make sense for it to be moved elsewhere (or declared in a different way).
* Handle server list pings from unrecognized versions
This isn't the cleanest way of writing it (it feels odd to use ProtocolRecognizer to send packets, and the addition of m_InPingForUnrecognizedVersion feels like the wrong technique), but it works and I can't think of a better way (apart from creating a full separate protocol class to handle only the ping... which would be worse).
* Use cPacketizer for the disconnect packet
This also should fix clang build errors.
* Add 1.9.3 / 1.9.4 support
* Fix incorrect indentation in APIDesc
2016-05-14 15:12:42 -04:00
2016-08-23 07:20:43 -04:00
function HandleConsoleDownload ( a_Split )
-- Check params:
local url = a_Split [ 2 ]
local fnam = a_Split [ 3 ]
if ( not ( url ) or not ( fnam ) ) then
return true , " Missing parameters. Usage: download <url> <filename> "
end
local callbacks =
{
OnStatusLine = function ( self , a_HttpVersion , a_Status , a_Rest )
if ( a_Status ~= 200 ) then
LOG ( " Cannot download " .. url .. " , HTTP error code " .. a_Status )
return
end
local f , err = io.open ( fnam , " wb " )
if not ( f ) then
LOG ( " Cannot download " .. url .. " , error opening the file " .. fnam .. " : " .. ( err or " <no message> " ) )
return
end
self.m_File = f
end ,
OnBodyData = function ( self , a_Data )
if ( self.m_File ) then
self.m_File : write ( a_Data )
end
end ,
OnBodyFinished = function ( self )
if ( self.m_File ) then
self.m_File : close ( )
LOG ( " File " .. fnam .. " has been downloaded. " )
end
end ,
}
local isSuccess , msg = cUrlClient : Get ( url , callbacks )
if not ( isSuccess ) then
LOG ( " Cannot start an URL download: " .. ( msg or " <no message> " ) )
return true
end
return true
end
1.9 / 1.9.2 / 1.9.3 / 1.9.4 protocol support (#3135)
* Semistable update to 15w31a
I'm going through snapshots in a sequential order since it should make things easier, and since protocol version history is written.
* Update to 15w34b protocol
Also, fix an issue with the Entity Equipment packet from the past version. Clients are able to connect and do stuff!
* Partially update to 15w35e
Chunk data doesn't work, but the client joins. I'm waiting to do chunk data because chunk data has an incomplete format until 15w36d.
* Add '/blk' debug command
This command lets one see what block they are looking at, and makes figuring out what's supposed to be where in a highly broken chunk possible.
* Fix CRLF normalization in CheckBasicStyle.lua
Normally, this doesn't cause an issue, but when running from cygwin, it detects the CR as whitespace and creates thousands of violations for every single line. Lua, when run on windows, will normalize automatically, but when run via cygwin, it won't.
The bug was simply that gsub was returning a replaced version, but not changing the parameter, so the replaced version was ignored.
* Update to 15w40b
This includes chunk serialization. Fully functional chunk serialization for 1.9.
I'm not completely happy with the chunk serialization as-is (correct use of palettes would be great), but cuberite also doesn't skip sending empty chunks so this performance optimization should probably come later. The creation of a full buffer is suboptimal, but it's the easiest way to implement this code.
* Write long-by-long rather than creating a buffer
This is a bit faster and should be equivalent. However, the code still doesn't look too good.
* Update to 15w41a protocol
This includes the new set passengers packet, which works off of the ridden entity, not the rider. That means, among other things, that information about the previously ridden vehicle is needed when detaching. So a new method with that info was added.
* Update to 15w45a
* 15w51b protocol
* Update to 1.9.0 protocol
Closes #3067. There are still a few things that need to be worked out (picking up items, effects, particles, and most importantly inventory), but in general this should work. I'll make a few more changes tomorrow to get the rest of the protocol set up, along with 1.9.1/1.9.2 (which did make a few changes). Chunks, however, _are_ working, along with most other parts of the game (placing/breaking blocks).
* Fix item pickup packet not working
That was a silly mistake, but at least it was an easy one.
* 1.9.2 protocol support
* Fix version info found in server list ping
Thus, the client reports that it can connect rather than saying that the server is out of date. This required creating separate classes for 1.9.1 and 1.9.2, unfortunately.
* Fix build errors generated by clang
These didn't happen in MSVC.
* Add protocol19x.cpp and protocol19x.h to CMakeLists
* Ignore warnings in protocol19x that are ignored in protocol18x
* Document BLOCK_FACE and DIG_STATUS constants
* Fix BLOCK_FACE links and add separate section for DIG_STATUS
* Fix bat animation and object spawning
The causes of both of these are explained in #3135, but the gist is that both were typos.
* Implement Use Item packet
This means that buckets, bows, fishing rods, and several other similar items now work when not looking at a block.
* Handle DIG_STATUS_SWAP_ITEM_IN_HAND
* Add support for spawn eggs and potions
The items are transformed from the 1.9 version to the 1.8 version when reading and transformed back when sending.
* Remove spammy potion debug logging
* Fix wolf collar color metadata
The wrong type was being used, causing several clientside issues (including the screen going black).
* Fix 1.9 chunk sending in the nether
The nether and the end don't send skylight.
* Fix clang build errors
* Fix water bottles becoming mundane potions
This happened because the can become splash potion bit got set incorrectly. Water bottles and mundane potions are only differentiated by the fact that water bottles have a metadata of 0, so setting that bit made it a mundane potion.
Also add missing break statements to the read item NBT switch, which would otherwise break items with custom names and also cause incorrect "Unimplemented NBT data when parsing!" logging.
* Copy Protocol18x as Protocol19x
Aditionally, method and class names have been swapped to clean up other diffs. This commit is only added to make the following diffs more readable; it doesn't make any other changes (beyond class names).
* Make thrown potions use the correct appearence
This was caused by potions now using metadata.
* Add missing api doc for cSplashPotionEntity::GetItem
* Fix compile error in SplashPotionEntity.cpp
* Fix fix of cSplashPotionEntity API doc
* Temporarilly disable fall damage particles
These were causing issues in 1.9 due to the changed effect ID.
* Properly send a kick packet when connecting with an invalid version
This means that the client no longer waits on the server screen with no indication whatsoever. However, right now the server list ping isn't implemented for unknown versions, so it'll only load "Old" on the ping.
I also added a GetVarIntSize method to cByteBuffer. This helps clean up part of the code here (and I think it could clean up other parts), but it may make sense for it to be moved elsewhere (or declared in a different way).
* Handle server list pings from unrecognized versions
This isn't the cleanest way of writing it (it feels odd to use ProtocolRecognizer to send packets, and the addition of m_InPingForUnrecognizedVersion feels like the wrong technique), but it works and I can't think of a better way (apart from creating a full separate protocol class to handle only the ping... which would be worse).
* Use cPacketizer for the disconnect packet
This also should fix clang build errors.
* Add 1.9.3 / 1.9.4 support
* Fix incorrect indentation in APIDesc
2016-05-14 15:12:42 -04:00
function HandleBlkCmd ( a_Split , a_Player )
-- Gets info about the block the player is looking at.
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
a_Player : SendMessage ( " Block at " .. a_BlockX .. " , " .. a_BlockY .. " , " .. a_BlockZ .. " is " .. a_BlockType .. " : " .. a_BlockMeta )
return true ;
end
end
} ;
local EyePos = a_Player : GetEyePosition ( ) ;
local LookVector = a_Player : GetLookVector ( ) ;
LookVector : Normalize ( ) ;
local End = EyePos + LookVector * 50 ;
cLineBlockTracer.Trace ( World , Callbacks , EyePos.x , EyePos.y , EyePos.z , End.x , End.y , End.z ) ;
return true ;
end
2016-05-29 13:12:18 -04:00
function HandleTeamsCmd ( a_Split , a_Player )
local Scoreboard = a_Player : GetWorld ( ) : GetScoreBoard ( )
a_Player : SendMessage ( " Teams: " .. table.concat ( Scoreboard : GetTeamNames ( ) , " , " ) )
return true
end