BucketOfLua - Lua for Bukkit

Discussion in 'WIP and Development Status' started by apiocera, Jul 19, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    apiocera

    BucketOfLua
    Version: 0.0.1-alpha3

    Description: BucketOfLua is a plugin providing Lua scripting for Bukkit. The name is (probably) temporary, it seems a little stupid to me :] nevertheless, it is highly descriptive. You can make some small plugins with help of this tool and apply them without restarting the server. At least you will able to do this in betas - BOL uses native luajava library that sometimes refuses to do its job and throws access violations crashing JVM.

    Features: BucketOfLua automatically picks up all files (except BucketOfLua.yml) from its configuration directory and processes them as Lua files. BOL gives the scripts (snips in its terminology) a small API to subscribe to events and commands in a simple way.

    Installation: Installation is quite simple: drop BucketOfLua.jar into /plugins and run CraftBukkit. plugins/BucketOfLua folder will be created. Put luajava-1.1.dll in it along with some snips: examples are good enough. If you have Linux or Mac box, just make luajava from source - BucketOfLua will try to pick luajava-1.1.so from lib folders.

    Commands:
    • /lua stop stops all scripts.
    • /lua run reads the snip directory again and loads everything it could find.
    • /lua reset is an alias for consecutive /lua stop and /lua run, just faster to type.
    API:
    API description (open)
    Plugin provides two objects into snip's namespace: dispatcher and assistant. Dispatcher is used to subscribe to events and assistant has supplementary functions that are not available in Lua.
    Dispatcher has these methods:
    • boolean dispatcher:registerListener(event_type, callback) - event is an Event.Type constant transformed into Lua (!not Event.Type.PLAYER_MOVE, but MC_EVENT_PLAYER_MOVE, you should remember it!), callback is a Lua function. This method makes link between that event's arrival and call of a callback, something like PluginManager::registerEvent does. Callback for an event should take only one variable as a parameter. That variable will be Event-derived object when called. Please note: no type-checking is provided, so if you will try to demand getLigtning() from a PlayerEvent... well, I guess, there will be no segfault, but you'll get an exception.
    • boolean dispatcher:registerCommand(command, callback) - command is a string, callback is a Lua function. Lua callback will be called on "/<command>" from player. Callback for a command should take two commands: (sender, cmd). Sender is the player and cmd is a command string. Due to my laziness, in case you type in something like /lolcats add 9000, you'll get literally "/lolcats add 9000" as cmd.
    • Server dispatcher:getServer() - returns a server instance (this function actually returns plugin.getServer()). You can do anything you want to do with it from within Lua code.
    Assistant for now has only two methods:
    • boolean isPlayer(Object o). It returns (o instanceof Player).
    • Object arrayIndex(Object o, int index). It returns o[index].
    But that's just for now, I'll add something more in the future!


    Changelog:
    0.1-alpha3:
    • 32-bit Linux luajava library is now bundled.
    • Scheduled tasks. I should probably update the first post, but I am a bit lazy.
    0.1-alpha2:
    • luajava is now right in the BucketOfLua.jar, and binary library can be loaded from config folder.
    • it is possible now to register commands that work without /l prefix. awesome :3
    • also new release numeration system.
    • and assistant got another method: arrayIndex.
    r1-alpha1:
    • initial public release
    What I did not test, but it might work:
    • Permissions. The official ones, you know. They have an interface accessible through Player object, so there should be no problems in theory... but only in theory.
    • Most events. My progress in testing all that is about four events: PLAYER_JOIN, PLAYER_MOVE, PLAYER_INTERACT and BLOCK_BREAK. There were no problems with them, I suppose there would be no problems with other events, as long as you are addressing existing methods.
    What I did test and it did not work:
    • Equality operator in Lua. All Java objects, including of the same type, are treated as black boxes of `userdata`. Lua interpreter can only call their methods. There is no way to find out if those two black boxes situated at different addresses in fact have the same class or even equal. But! You can call obj1:equals(obj2) and it will return if obj1 is the same as obj2 in a Lua-compartible boolean type. Check out dont_mine_with_hands snip to understand.
    • Correct error handling. Sorry, but for now all you can see in case of an error is a Java stack trace or, in case you are lucky, fatal error message. The latter are rare now, but in case you encounter one please don't hesitate to copy it to pastebin and tell me how to reproduce it.
    • Lots of Exceptions. Sorry. Report them. Please.
    Download alpha3: Dropbox [i hate adfly]. Sources.

    P.S. Ideas are always welcome, of course :)
     
    repeat likes this.
  2. Offline

    repeat

    I began to unnecessary work))) http://forums.bukkit.org/threads/play-lua-i-only-try-make-this.27703/

    better if you include luajava-1.1.jar into your source

    change LuaState.java path to DLL.

    xAuth plugin have alias /l (/login) maybe remove this command and use Event.Type.PLAYER_COMMAND_PREPROCESS (if i right understand for this event noneed define command in plugin.yml)


    bugfix: load only lua files in plugin directory
    PHP:
        private List<FilegetSnipList() {
            List<
    Filesnips = new ArrayList<File>();
            
    File[] children mainDir.listFiles();
            if (
    children != null) {
                for (
    File f children) {
    //                if (!f.getName().equals(configurationFile) and ) snips.add(f);
                    
    if (f.getName().endsWith(".lua")) snips.add(f);
                    }
            }

            return 
    snips;
        }
    PHP:
    function moving(event)

    --
    userdate
      player
    :chat(type(event:getType()))
    --
    string :)
      
    player:chat(event:getType():toString())

    end
    http://www.dalarium-lejeu.com/server/luajava-1.1/doc/us/API/index-all.html

    what about extend plugin?

    make libs like this (because i don't know how send broadcast message using your plugin)
    i do this in my toy thus

    Show Spoiler
    messageTo function i do using lua scripts but broadcast don;t know how do it
    PHP:
    public class Libs {

              private final static 
    String LIB_NAME "plua";

              private static 
    void getLibTable(LuaState L)
              {
                
    L.getGlobal(LIB_NAME);
                if (
    L.isNil(-1))
                {
                  
    L.pop(1);
                  
    L.newTable();
                  
    L.pushValue(-1);
                  
    L.setGlobal(LIB_NAME);
                }
              }

              
    /**
               * Method called by loadLib
               */
              
    public static void open(LuaState Lthrows LuaException
              
    {
                    
    getLibTable(L);

                
    L.pushString("example");

                
    L.pushJavaFunction(new JavaFunction(L) {
                  
    /**
                   * Example for loadLib.
                   * Prints the time and the first parameter, if any.
                   */
                  
    public int execute() throws LuaException
                  
    {
                    
    System.out.println(new Date().toString());

                    if (
    L.getTop() > 1)
                    {
                      
    System.out.println(getParam(2));
                    }

                    return 
    0;
                  }
                });

                
    L.setTable(-3);

                
    L.pop(1);

                
    getLibTable(L);
                
    L.pushString("messageTo");

                
    L.pushJavaFunction(new JavaFunction(L) {
                  
    /**
                   * Example for loadLib.
                   * Prints the time and the first parameter, if any.
                   */
                    
    public int execute() throws LuaException
                    
    {
                        
    //от двух параметров начинать обработку
                        
    if (L.getTop() > 2)
                        {
                            
    Player[] onlinePlayers Bukkit.getServer().getOnlinePlayers();
                            
    Player targetPlayer null;
                            for (
    int i=0<= onlinePlayers.lengthi++)
                            {
                                
    String pl getParam(2).toString();
                                if (
    pl.equalsIgnoreCase(onlinePlayers[i].getName()))
                                {
                                    
    targetPlayer onlinePlayers[i];
                                    
    targetPlayer.sendMessage(getParam(3).toString());
                                    return 
    0;
                                }
                            }
                            if (
    targetPlayer == null)
                            {
                                
    Logger.getLogger("Minecraft").warning("Player "+getParam(2).toString()+" not found");
                            }
                        }
                      return 
    0;
                }

              });
                
    L.setTable(-3);
                
    L.pop(1);

                
    getLibTable(L);
                
    L.pushString("broadcast");

                
    L.pushJavaFunction(new JavaFunction(L) {
                  
    /**
                   * Example for loadLib.
                   * Prints the time and the first parameter, if any.
                   */
                    
    public int execute() throws LuaException
                    
    {
                        if (
    L.getTop() > 1)
                        {
                            
    System.out.println(getParam(2));
                            
    Bukkit.getServer().broadcastMessage(ChatColor.RED getParam(2).toString());
                        }
                      return 
    0;
                }

              });
                
    L.setTable(-3);
                
    L.pop(1);
              }

        }         


    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 17, 2016
  3. Offline

    apiocera

    Cool :) i've added some of your contributions into source, like PLAYER_COMMAND_PREPROCESS taking and loading only .lua files (what was I thinking?). Also, no luajava.jar needed now, and .dll (.so) is read from config folder now.

    AFAIR, there are some classes with unimplemented toString method, but implemented equals().
    I am trying to keep Java code to a minimum, leaving everything to Lua. I've implemented getServer() method in the dispatcher and arrayIndex in assistant, that is enough to broadcast a message :)
     
  4. Offline

    repeat

    get error
    Show Spoiler
    2011-07-24 23:17:28 [SEVERE] org.keplerproject.luajava.LuaException: Lua Error code 2. Invalid method call. No such method.
    2011-07-24 23:17:28 [SEVERE] at org.keplerproject.luajava.LuaObject.call(LuaObject.java:452)
    2011-07-24 23:17:28 [SEVERE] at org.keplerproject.luajava.LuaObject.call(LuaObject.java:483)
    2011-07-24 23:17:28 [SEVERE] at im.creep.bucketoflua.EventDispatcher.parseCommand(EventDispatcher.java:110)
    2011-07-24 23:17:28 [SEVERE] at im.creep.bucketoflua.CommandListener.onPlayerCommandPreprocess(CommandListener.java:14)
    2011-07-24 23:17:28 [SEVERE] at org.bukkit.plugin.java.JavaPluginLoader$5.execute(JavaPluginLoader.java:272)
    2011-07-24 23:17:28 [SEVERE] at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:58)
    2011-07-24 23:17:28 [SEVERE] at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:332)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.NetServerHandler.handleCommand(NetServerHandler.java:706)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.NetServerHandler.chat(NetServerHandler.java:677)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.NetServerHandler.a(NetServerHandler.java:670)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.Packet3Chat.a(Packet3Chat.java:33)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.NetworkManager.b(NetworkManager.java:226)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.NetServerHandler.a(NetServerHandler.java:85)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.NetworkListenThread.a(SourceFile:105)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.MinecraftServer.h(MinecraftServer.java:451)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:361)
    2011-07-24 23:17:28 [SEVERE] at net.minecraft.server.ThreadServerApplication.run(SourceFile:422)


    Code:
    function g(sender, cName, cmdargs)
        local player = sender
        local server = dispatcher:getServer() --problem line
        sender:sendMessage("I know about You ALLLLLllllll!")
        sender:sendMessage(type(server))
    end
    
    dispatcher:registerCommand("g", g)
    
     
  5. Offline

    apiocera

    Works for me. Update the JAR, there is new version.
     
  6. Offline

    repeat

    we can get any enum in lua script like this!

    local CreatureType = luajava.bindClass("org.bukkit.entity.CreatureType")
    local pig = CreatureType:fromName("Pig")

    example:
    Code:
    function createEntity(sender, cmdargs)
    	local loc = sender:getLocation()
    
    	cmdargs = tostring(cmdargs)
    
    	-- get second word and set 1st char to uppercase
    	local _, _, cr = cmdargs:find("%S*%s(%S*)")
    	cr = string.upper(cr:sub(1,1))..cr:sub(2)
    	if (cr == "Pigzombie") then cr = "PigZombie"; end
    	if (cr == "?") then
    
    		sender:sendMessage("(Chicken, Cow, Creeper, Ghast, Giant, Monster, Pig, PigZombie)")
    		sender:sendMessage("(Sheep, Skeleton, Slime, Spider, Squid, Zombie, Wolf)")
    		return true
    	end
    
    	local Creature = luajava.bindClass("org.bukkit.entity.CreatureType")
    	local Creature = Creature:fromName(cr)
    	if type(Creature) == "userdata" then
    --		dispatcher:getServer():getWorld("world"):spawnCreature(loc,Creature)
    		sender:getWorld():spawnCreature(loc,Creature)
    		sender:sendMessage("Create "..Creature:getName())
    	else
    		sender:sendMessage("Wrong entity use /create ? for get list")
    	end
    end
    
    function effect(sender, cmdargs)
        local loc = sender:getLocation()
    
        local cmdargs = "BOW_FIRE"
        -- data - a data bit needed for the RECORD_PLAY, SMOKE, and STEP effects
        local data = 0
        -- radius - the radius around the location
        local radius = 10
    
        local effect = luajava.bindClass("org.bukkit.Effect")
        local effect = effect:valueOf(cmdargs)
        if type(effect) == "userdata" then
    --        dispatcher:getServer():getWorld("world"):playEffect(pl,effect,data,radius)
            sender:getWorld():playEffect(loc,effect,data,radius)
            sender:sendMessage("Effect done")
        else
            sender:sendMessage("Wrong effect")
        end
    end
    
    dispatcher:registerCommand("create", createEntity)
    dispatcher:registerCommand("effect", effect)
    Note: If we want output some variable using sender:sendMessage then this variable need convert to string using function 'tostring' ( ex: variable = tostring(variable) )
     
  7. Offline

    nickguletskii

    A problem with that: almost nobody sane uses Windows in the server world, and luajava is a pain to compile due to a badly written manifest.
     
  8. Offline

    DalexL

    I have to say, this is the most amazing plug-in I've ever used... I'm using it to control everything on my server because I know Lua and it can be updated even while the server is running! Thanks so much!

    Now, there are a couple of things I'm confused about. Take a look at this questions I asked here:

    http://forums.bukkit.org/threads/display-a-chest-window.31202/

    It took some time to get used to (things such as ChatColor and CreatureType were annoying but I figure it all out).

    I would like to point out, however, that coroutines cause it to crash. They are kind of important and I didn't know if you would be willing to fix that. I also have a theory that 'pcall' isn't working quite right either.

    Cheers and Thanks again!
     
  9. Offline

    repeat

    author is gone. Me too is like this plugin. I make new build plugin with some own additions.

    + all libs (corouting, io and so on)
    + schedulers (name of method like as in http://jd.bukkit.org/doxygen/df/dfa/interfaceorg_1_1bukkit_1_1scheduler_1_1BukkitScheduler.html
    |- dispatcher:scheduleAsyncDelayedTask(func_name, delay_befor_start)
    |- dispatcher:scheduleAsyncRepeatingTask(func_name,delay_befor_start, delay_before_repeating)
    |- and like for Sync schedule.
    |- dispatcher:cancelTask(ID); ID - Object returned from any repeating task

    if i remember correctly delay = 20 is equal 1sec

    Code:
    function t1()
        dispatcher:getServer():broadcastMessage("В§E--T1");
    end
    function t2()
        dispatcher:getServer():broadcastMessage("В§E--T2");
    end
    function t3()
        dispatcher:getServer():broadcastMessage("В§E--T3");
    end
    function t4()
        dispatcher:getServer():broadcastMessage("В§E--T4");
    end
    
    task = {}
    
    -- IMPORTANT!!! for RepeatingTask need make variable for in future you can cancel it
    dispatcher:scheduleAsyncDelayedTask(t1,100)
    task.t2 = dispatcher:scheduleAsyncRepeatingTask(t12,120,40)
    dispatcher:scheduleSyncDelayedTask(t3,140)
    task.t4 = dispatcher:scheduleSyncRepeatingTask(t4,160,50)
    
    --for canceling tasks use something like that dispatcher:cancelTask(task.t4)
    giveme
    I don't know how i can use /give command for to give me for example RED WOOL, so i made this script
    command: /giveme <id> <amount> <data>
    <data> usefull for WOOL or other materials which have different types (17, 18, 31, 35, 43 and so on)

    [​IMG]

    for red wool need write /giveme 35 1 14
    /giveme 35 5 is like as /give 35 5 command
    /giveme 35 is like as /give 35 command
    script not support permissions. In one of line I inserted the verifying own name

    createWorld
    command: /createworld
    this is test of creating world :)
    in this script you can change "NORMAL" type of world on another type and change name of world is "sn" and seed

    loadWorld
    after when i created world me need load it. I put this script which not included into any functions and it run every time when BOL (BucketOfLua) runs after start server or reset (/lua reset).
    This line (local world = dispatcher:getServer():getWorld("world")) contain world is "world" because i don't know how get 'Environment without it. If you have other name of world (and not have world is "world"), change this name on yours
    In this example my new world have name is "test" (it name is not equals name in previous example)

    ttw (Teleport To World)
    command: /ttw <worldname>
    some simple exapmple
     

    Attached Files:

    Last edited by a moderator: May 18, 2016
  10. Offline

    DalexL

    Thanks for all of the help! You've been a life saver!
     
  11. Offline

    Jadedwolf

    Looks at my WoW addons I've made and realize my lua knowledge may come in handy once again :D
     
  12. Offline

    DalexL

  13. Offline

    Evangon

    I hope this thread isn't dead, but, could you make a wiki of this? Nothing too fancy. :)
     
  14. Offline

    DalexL

    Well, you can get a lot from the Bukkit API: http://jd.bukkit.org/apidocs/

    I'm not sure how to utilize things that are in CraftBukkit (I really want to be able to force players to open a chest no matter how far away they are from it). Everything non-Bukkit related is posted here.

    Over all, I use this for all my work, I've got only 1 other plugin, and that is for terrain generation.

    @repeat

    I was wondering, would it be possible for you to add support to determine the location of the plugin in the windows file system? I want to save data from my Lua scripts (persistence reasons, e.g. saved settings) but Lua doesn't have a native method for getting the current directory.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 17, 2016
  15. Offline

    repeat

    current directory is the directory where the server starts up

    i make ini.lua in directory of plugin BOL
    Show Spoiler

    Code:
    --http://jd.bukkit.org/doxygen/d4/da9/interfaceorg_1_1bukkit_1_1Server.html
    
    BOLdir = "plugins\\bucketoflua\\"
    logger = luajava.bindClass("java.util.logging.Logger")
    logger = logger:getLogger("Minecraft")
    
    require "plugins.bucketoflua.java.import"
    
    --require "plugins.bucketoflua.core.ginfo"
    --require "plugins.bucketoflua.scripts.starting"
    
    require "plugins.bucketoflua.scripts.createEntity"
    require "plugins.bucketoflua.scripts.effect"
    require "plugins.bucketoflua.scripts.explosion"
    require "plugins.bucketoflua.scripts.rules"
    
    --require "plugins.bucketoflua.scripts.ferma"
    
    --загружаем мир и включаем возможность перемещаться к нему
    require "plugins.bucketoflua.scripts.loadWorld"
    require "plugins.bucketoflua.scripts.ttw"
    
    -- расширенная версия /give только для себя, параметры <Тип> [<кол-во> <подвид>]
    require "plugins.bucketoflua.scripts.giveme"
    
    require "plugins.bucketoflua.scripts.test"
    
    --пускать на полный сервер VIP игроков
    --require "plugins.bucketoflua.scripts.vip_to_full"
    
    --require "plugins.bucketoflua.scripts.!ban"
    --require "plugins.bucketoflua.scripts.close_server"
    
    my dirs:
    core\ <DIR> 12.08.2011 16:39 ----
    java\ <DIR> 12.08.2011 16:39 ----
    off\ <DIR> 12.08.2011 16:39 ----
    scripts\ <DIR> 20.08.2011 01:58 ----
    init.lua 1 123 20.08.2011 02:07 -a--
    BucketOfLua.yml 3 20.08.2011 01:18 -a--
    luajava-1.1.dll 225 280 05.08.2011 10:31 -a--


    In this file I load what i want and doing initialize global variable
    If you want you can determine variable BOLdir (or else)
    BOLdir = "plugins\\bucketoflua\\" and use it future in your scripts
    Show Spoiler

    scripts\vip_to_full.lua
    Code:
    --[[
    
    Разрешает зайти на полный сервер VIP игроков
    Имена VIP игроков записаны в vip_to_full.allow файле
    
    Allows enter in a full server VIP players
    VIP players names are written in a file vip_to_full.allow
    
    ]]
    
    function VIPtoFull(event)
        if (event:getResult():toString() ~= "KICK_FULL") then return end
    
        local player = event:getPlayer()
        local file = io.open(BOLdir.."\\scripts\\vip_to_full.allow")
        for pname in file:lines() do
            if (player:getName() == pname) then
                event:allow()
                file:close()
                return
            end
        end
        file:close()
    end
    dispatcher:registerListener(MC_EVENT_PLAYER_LOGIN, VIPtoFull)
    logger:info("[BucketOfLua]: vip_to_full loaded")
    scripts\vip_to_full.allow
    Code:
    bulka
    repeat
    
     
  16. Offline

    DalexL

    Thanks once again ;)
     
  17. Offline

    repeat

    i found this. return current directory.
    Code:
    t =io.popen"cd":read'*l'
    print(t)
     
  18. Offline

    7eggert

  19. Offline

    repeat

    Are you using BOL on Unix? Post your luajava-1.1.so

    may be
    t =io.popen"ls":read'*l'
    print(t)
     
  20. Offline

    repeat

    access to permissions:

    Code:
    function test(sender, cmdargs)
        local player = sender
        local t = player:hasPermission("lwc.protect")
        sender:sendMessage(tostring(t))
    end
    dispatcher:registerCommand("test", test)
     
  21. Offline

    apiocera

    Hi, I'm back. Didn't do anything productive though :)

    @repeat suggested using luaj, so plugin will be liberated from platform-dependent luajava code. By the way, I have compiled libluajava-1.1.so for 32bit Linux and successfully tested it with some trivial code ('print("hi")' for those interested). There is also some scheduled tasks code, but I am not sure if it is mine or @repeats, since the API is suspiciously similar to the one posted before.

    So: libluajava-1.1.so, alpha3 BucketOfLua. Just put .so under /usr/lib, chmod it to 755 and enjoy.
     
  22. Offline

    VADemon

    @apiocera
    Aplugin that allows lua in Minecraft - sounds great. Could you make some examples of the code?
    The simple ones like "on leave all show a broadcast message". And one or two pairs of harder scripts.
    Thanks!
     
  23. Offline

    apiocera

  24. Offline

    VADemon

    apiocera
    Ehm, one question:
    will you continue working on this plugin? You know Minecraft 1.2, R5...
     
  25. Offline

    apiocera

    Do you need me to? What features do you need? Does it work with late bukkit builds? I haven't touched it for a while, I don't know much about it :)
     
  26. Offline

    VADemon

    apiocera
    Bukkit's API changes with R5. I think BucketOfLua won't work. Didn't tested it (just wanted to see whether you're here ^^).
     
  27. Offline

    Waterflames

    Combine this with a spoutcraft custom client gui = computercraft for spout/bukkit!

    I would do it, but I don't have the time ATM.
     
Thread Status:
Not open for further replies.

Share This Page