[Plugin] Shoebill 1.1 - SA-MP Java Development Kit

@!damo!spiderman: They are hosted on dropbox and are working fine for me. Maybe Dropbox is restricted in your country?
Reply

I would like to discuss the PickupModel class...I see it is an enum, but ANY object can be used as a pickup, so why did you limit that enum to only some values?
Reply

@dusk: We are currently only including weapons and pickups in the enum (http://weedarr.wikidot.com/pickups).
If you want, you can always use your own modelId when using Pickup.create(). You cannot dynamically add items to an enum, that's why not every object is included in the list.
Reply

I have a suggestion:

I always code in english, so all my variables, methods and such are in english. It seems that sheobill generates command help messages from command method argument names and that is a problem for me as my server is deisgned for non english speaking users.

Would it be possible/practical to add some annotation before command parameters to rename them?

For example:

pawn Код:
@Command
public bool hello(Player p, Player @CommandParam("ąęųљįčę")p2)
{
    return true;
}
And when the user forgets to enter a player identifier it would say "Usage /hello [ąęųљįčę]".


And a question: If I return false in a command method, will the usage method be sent?
Reply

@dusk: It's currently not possible to set custom names for your parameters, but you can use the @CommandHelp Annotation as a workaround. After that, you can override the default UsageMessageSupplier to output your custom parameters:

PHP код:
@Command
@CommandHelp("[myCustomArgumentText] [mySecondCustomArgumentText]")
public 
boolean givecash(Player pPlayer targetint amount)
{
    ...

PHP код:
commandManager.setUsageMessageSupplier((issuercommandprefixargumentshelp) -> {
    if(
help != null//if @CommandHelp has been supplied
        
return "Correct usage: " prefix command " " help;
    else { 
//If not, use parameterNames from Java
        
String paramsString "";
        for(
int i 0arguments.lengthi++) {
            
paramsString += "[" arguments[i] + "]";
            if(
arguments.length)
            
paramsString += " ";
        }
        return 
"Correct usage: " prefix command " " paramsString;
    }
}); 
If you return false in a Command, the CommandManager will try to find another matching callback, but if it won't find any, the PlayerCommandEvent will not be interrupted. This way, you can make a "Command not found" message using events:

PHP код:
eventManagerNode.registerHandler(PlayerCommandEvent.class, HandlerPriority.BOTTOM, (e) ->
{
    
Player player e.getPlayer();
    
player.sendMessage(Color.RED"Unknown command. Type /help to see help.");
    
e.setProcessed();
}); 
(It's important to use HandlerPriority.BOTTOM, because otherwise it would not be called at the end)
Reply

Unfortunately the CI Server is down
Reply

@Piranha19: Yes, I know. We are trying to fix it.
Reply

So let's say I have VehicleData class.
PHP код:
public class VehicleData extends VehicleLifecycleObject 
    private 
String owner;
    public 
VehicleData(EventManager eventManagerVehicle vehicle) { 
        
super(eventManagervehicle); 
    } 
    @
Override 
    
public void onInit() { 
    } 
    @
Override 
    
public void onDestroy() { 
    } 
    public 
void setOwner(String owner) { 
        
this.owner owner;
    } 

And I register it.
PHP код:
vehicleLifecycleHolder = new VehicleLifecycleHolder(eventManager); 
vehicleLifecycleHolder.registerClass(VehicleData.class); 
So how do I set the owner value? Is this correct?
PHP код:
Vehicle vehicle Vehicle.create(some values here...);
VehicleData vehicleData vehicleLifecycleHolder.getObject(vehicleVehicleData.class);
vehicleData.setOwner(owner); 
Or can I use the VehicleData constructor some how?
Reply

@tampz: Yes, this is correct. The VehicleData class will be instantiated when the Vehicle is created automatically. You can use the onInit() function in your VehicleData class to initialize additional data and use the onDestroy() function to destroy data (will be called when the vehicle is destroyed using .destroy(), not when health = 0).
Reply

I have a few questions about the EventManager and its' subclasses. What the purpose of child event manager nodes? Are they there only for convenience or they have some underlying purpose? As much I understand the only reason to use them is the convenience of cancelling all events that it contains.
Reply

@Piranha19: You can temporarily change the address of the repo server to http://79.133.59.33:8081/content/groups/public. If you want to access the ci server, you can use http://79.133.59.33/. Don't forget to change the address in your resources.yml.

@dusk: The only purpose of it is to cancel all events and children handlers in that specific handler. It is useful when you need to setup a temporary handler that will be destroyed later.
Reply

Greetings. I've bumped into a problem. I'm trying to write my first gamemode in IntelliJ IDEA, following all steps on the video, everything is ok, but when I paste the code for pom.xml from the pastebin link there are errors "Multiple root tags" for tags <dependencies>, <properties> and <build>. How can I solve them?
Reply

Ok, my bad, I REPLACED the code from pastebin instead of just to ADD it.

Other questions (excuse me if there were ones. There are 40 pages to read...):
1) Is there any tutorial how to run already written PAWN gamemode with a gamemode written with the help of your plugin?
2) Can I use plugins like Incognito's streamer in a java gamemode?
3) Can I write my own plugin with the help of your plugin? Or how can I make methods in java gamemode accessible in pawn gamemode?
Reply

Quote:
Originally Posted by valych
Посмотреть сообщение
Ok, my bad, I REPLACED the code from pastebin instead of just to ADD it.

Other questions (excuse me if there were ones. There are 40 pages to read...):
1) Is there any tutorial how to run already written PAWN gamemode with a gamemode written with the help of your plugin?
2) Can I use plugins like Incognito's streamer in a java gamemode?
3) Can I write my own plugin with the help of your plugin? Or how can I make methods in java gamemode accessible in pawn gamemode?
2) You can use any functions from plugins. I think it's a good idea to create a wrapper class for those functions. For example, this is my DynamicObject class:
[spoiler]
Код:
package maze.plugin.streamer;

import maze.Util;
import net.gtaun.shoebill.amx.AmxCallable;
import net.gtaun.shoebill.amx.types.ReferenceFloat;
import net.gtaun.shoebill.constant.ObjectMaterialSize;
import net.gtaun.shoebill.constant.ObjectMaterialTextAlign;
import net.gtaun.shoebill.data.Color;
import net.gtaun.shoebill.data.Location;
import net.gtaun.shoebill.data.Vector3D;
import net.gtaun.shoebill.object.Player;
import net.gtaun.shoebill.object.Vehicle;

import java.util.ArrayList;

/**
 * Created by Bebras on 2015.06.07.
 */
public class DynamicSampObject {

    private static ArrayList<DynamicSampObject> objects;
    private int id, modelId;
    private Player attachedPlayer;
    private float speed, drawDistance;
    private DynamicSampObject attachedObject;
    private Vehicle attachedVehicle;

    public static DynamicSampObject create(int modelid, Location location, float rx, float ry, float rz) {
        return create(modelid, location, rx, ry, rz, 0.0f);
    }

    // CreateDynamicObject(modelid, Float:x, Float:y, Float:z, Float:rx, Float:ry, Float:rz, worldid = -1, interiorid = -1, playerid = -1, Float:streamdistance = 200.0, Float:drawdistance = 0.0);
    public static DynamicSampObject create(int modelid, Location location, float rx, float ry, float rz, float drawdistance) {
        if(objects == null) {
            objects = new ArrayList<>();
        }
        AmxCallable nativeMethod = Util.getNativeMethod("CreateDynamicObject");
        int id = 0;
        if(nativeMethod != null) {
            id = (Integer)nativeMethod.call(modelid, location.getX(), location.getY(), location.getZ(), rx, ry, rz, location.getWorldId(), location.getInteriorId(), drawdistance);
            DynamicSampObject object = new DynamicSampObject(id, modelid, null, 0.0f, drawdistance, null, null);
            objects.add(object);
        }
        return null;
    }

    private DynamicSampObject(int id, int modelId, Player attachedPlayer, float speed, float drawDistance, DynamicSampObject attachedObject, Vehicle attachedVehicle) {
        this.id = id;
        this.modelId = modelId;
        this.attachedPlayer = attachedPlayer;
        this.speed = speed;
        this.drawDistance = drawDistance;
        this.attachedObject = attachedObject;
        this.attachedVehicle = attachedVehicle;
    }

    public int getId() {
        return id;
    }

    public int getModelId() {
        return modelId;
    }

    public float getSpeed() {
        return speed;
    }

    public float getDrawDistance() {
        return drawDistance;
    }

    public Player getAttachedPlayer() {
        return attachedPlayer;
    }

    public DynamicSampObject getAttachedObject() {
        return attachedObject;
    }

    public Vehicle getAttachedVehicle() {
        return attachedVehicle;
    }

    // native GetDynamicObjectPos(objectid, &Float:x, &Float:y, &Float:z);
    public Location getLocation() {
        AmxCallable function = Util.getNativeMethod("GetDynamicObjectPos");
        if(function != null) {
            ReferenceFloat x = new ReferenceFloat(0.0f);
            ReferenceFloat y = new ReferenceFloat(0.0f);
            ReferenceFloat z = new ReferenceFloat(0.0f);
            function.call(getId(), x, y, z);
            return new Location(x.getValue(), y.getValue(), z.getValue());
        } else {
            return null;
        }
    }

    // native SetDynamicObjectPos(objectid, Float:x, Float:y, Float:z);
    public void setLocation(Vector3D location) {
        AmxCallable function = Util.getNativeMethod("SetDynamicObjectPos");
        if(function != null) {
            function.call(getId(), location.getX(), location.getY(), location.getZ());
        }
    }

    public void setLocation(Location location) {
        setLocation(new Vector3D(location.getX(), location.getY(), location.getZ()));
    }

    // native GetDynamicObjectRot(objectid, &Float:rx, &Float:ry, &Float:rz);
    public Vector3D getRotation() {
        AmxCallable function = Util.getNativeMethod("GetDynamicObjectRot");
        if(function != null) {
            ReferenceFloat x = new ReferenceFloat(0.0f);
            ReferenceFloat y = new ReferenceFloat(0.0f);
            ReferenceFloat z = new ReferenceFloat(0.0f);
            function.call(getId(), x, y, z);
            return new Location(x.getValue(), y.getValue(), z.getValue());
        } else {
            return null;
        }
    }

    // native SetDynamicObjectRot(objectid, Float:rx, Float:ry, Float:rz);
    public void setRotation(float rx, float ry, float rz) {
        AmxCallable function = Util.getNativeMethod("SetDynamicObjectRot");
        if(function != null) {
            function.call(getId(), rx, ry, rz);
        }
    }

    public void setRotation(Vector3D vector3D) {
        setRotation(vector3D.getX(), vector3D.getY(), vector3D.getZ());
    }

    // native IsDynamicObjectMoving(objectid);
    public boolean isMoving() {
        AmxCallable function = Util.getNativeMethod("IsDynamicObjectMoving");
        if(function != null) {
            return (Boolean) function.call(getId());
        } else {
            return false;
        }
    }

    //native MoveDynamicObject(objectid, Float:x, Float:y, Float:z, Float:speed, Float:rx = -1000.0, Float:ry = -1000.0, Float:rz = -1000.0);
    public int move(float x, float y, float z, float speed) {
        return move(x, y, z, speed, getRotation().getX(), getRotation().getY(), getRotation().getZ());
    }

    public int move(float x, float y, float z, float speed, float rx, float ry, float rz) {
        this.speed = speed;
        AmxCallable function = Util.getNativeMethod("MoveDynamicObject");
        if(function == null) {
            return 0;
        } else {
            return (Integer)function.call(getId(), x, y, z, speed, rx, ry, rz);
        }
    }

    public int move(Vector3D pos, float speed) {
        return move(pos.getX(), pos.getY(), pos.getZ(), speed, getRotation().getX(), getRotation().getY(), getRotation().getZ());
    }

    public int move(Vector3D pos, float speed, Vector3D rotation) {
        return move(pos.getX(), pos.getY(), pos.getZ(), speed, rotation.getX(), rotation.getY(), rotation.getZ());
    }

    public void stop() {
        AmxCallable function = Util.getNativeMethod("StopDynamicObject");
        if(function != null) {
            function.call(getId());
        }
    }

    // native AttachDynamicObjectToPlayer(objectid, playerid, Float:offsetx, Float:offsety, Float:offsetz, Float:rx, Float:ry, Float:rz);
    public void attach(Player player, float offx, float offy, float offz, float rx, float ry, float rz) {
        AmxCallable function = Util.getNativeMethod("AttachDynamicObjectToPlayer");
        if(function != null) {
            attachedPlayer = player;
            function.call(getId(), player.getId(), offx, offy, offz, rx, ry, rz);
        }
    }

    public void attach(Player player, Vector3D vector3D, Vector3D rotation) {
        attach(player, vector3D.getX(), vector3D.getY(), vector3D.getZ(), rotation.getX(), rotation.getY(), rotation.getZ());
    }

    // native AttachDynamicObjectToObject(objectid, attachtoid, Float:offsetx, Float:offsety, Float:offsetz, Float:rx, Float:ry, Float:rz, syncrotation = 1);
    public void attach(DynamicSampObject object, float offsetx, float offsety, float offsetz, float rx, float ry, float rz, boolean syncrotation) {
        AmxCallable function = Util.getNativeMethod("AttachDynamicObjectToObject");
        if(function != null) {
            attachedObject = object;
            function.call(getId(), object.getId(), offsetx, offsety, offsetz, rx, ry, rz, syncrotation);
        }
    }

    public void attach(DynamicSampObject object, Vector3D offset, Vector3D rotation, boolean sync) {
        attach(object, offset.getX(), offset.getY(), offset.getZ(), rotation.getX(), rotation.getY(), rotation.getZ(), sync);
    }

    // native AttachDynamicObjectToVehicle(objectid, vehicleid, Float:offsetx, Float:offsety, Float:offsetz, Float:rx, Float:ry, Float:rz);
    public void attach(Vehicle vehicle, float offsetx, float offsety, float offsetz, float rx, float ry, float rz) {
        AmxCallable function = Util.getNativeMethod("AttachDynamicObjectToObject");
        if(function != null) {
            attachedVehicle = vehicle;
            function.call(getId(), vehicle.getId(), offsetx, offsety, offsetz, rx, ry, rz);
        }
    }

    public void attach(Vehicle vehicle, Vector3D offsets, Vector3D location) {
        attach(vehicle, offsets.getX(), offsets.getY(), offsets.getZ(), location.getX(), location.getY(), location.getZ());
    }

    // native AttachCameraToDynamicObject(playerid, objectid);
    public void attachCamera(Player player) {
        AmxCallable function = Util.getNativeMethod("AttachCameraToDynamicObject");
        if(function != null) {
            function.call(player.getId(), getId());
        }
    }

    // native SetDynamicObjectMaterial(objectid, materialindex, modelid, const txdname[], const texturename[], materialcolor = 0);
    public void setMaterial(int materialindex, int modelid, String txdname, String texturename, Color color) {
        AmxCallable function = Util.getNativeMethod("SetDynamicObjectMaterial");
        if(function != null) {
            function.call(getId(), materialindex, modelid, txdname, texturename, color.getR() | color.getG() | color.getB() | color.getA());
        }
    }

    public void setMaterial(int materialindex, int modelid, String txdname, String texturename) {
        setMaterial(materialindex, modelid, txdname, texturename, new Color(0));
    }

    // native SetDynamicObjectMaterialText(objectid, materialindex, const text[], materialsize = OBJECT_MATERIAL_SIZE_256x128, const fontface[] = "Arial", fontsize = 24, bold = 1, fontcolor = 0xFFFFFFFF, backcolor = 0, textalignment = 0);
    public void setMaterialText(String text, int materialindex, ObjectMaterialSize materialsize, String font, int fontsize, boolean bold, Color fontcolor, Color backcolor, ObjectMaterialTextAlign objectMaterialTextAlign) {
        AmxCallable function = Util.getNativeMethod("SetDynamicObjectMaterialText");
        if(function != null) {
            function.call(getId(), materialindex, text, materialsize.getValue(), font, fontsize, bold, fontcolor.toArgbValue(), backcolor.toArgbValue(), objectMaterialTextAlign.getValue());
        }
    }

    public void setMaterialText(String text) {
        setMaterialText(text, 0, ObjectMaterialSize.SIZE_256x128, "Arial", 24, true, Color.WHITE, Color.BLACK, ObjectMaterialTextAlign.LEFT);
    }

    // native SetDynamicObjectNoCameraCol(objectid);
    public void setNoCameraCol() {
        AmxCallable function = Util.getNativeMethod("SetDynamicObjectNoCameraCol");
        if(function != null) {
            function.call(getId());
        }
    }

    // native DestroyDynamicObject(objectid);
    public void destroy() {
        objects.remove(this);
        AmxCallable function = Util.getNativeMethod("DestroyDynamicObject");
        if(function != null) {
            function.call(getId());
            id = 0;
        }
    }

    // native IsValidDynamicObject(objectid);
    public boolean isDestroyed() {
        AmxCallable function = Util.getNativeMethod("IsValidDynamicObject");
        if(function != null) {
            return !(Boolean)function.call(getId());
        } else {
            return false;
        }
    }

    public static DynamicSampObject findById(int id) {
        for(DynamicSampObject object : objects) {
            if(object.getId() == id) {
                return object;
            }
        }
        return null;
    }
}
[/spoiler]

You see the use of a method "Util.getNativeMethod", this is it:
Код:
public static AmxCallable getNativeMethod(String name) {
        AmxCallable nativeMethod = null;
        for(AmxInstance instance : Shoebill.get().getAmxInstanceManager().getAmxInstances()) {
            if((nativeMethod = instance.getNative(name)) != null)
                break;
        }
        return nativeMethod;
    }
Obviously, this is only an example. You can think of your own way of doing it. Here is the authors' post on calling native functions and handling events. Also, there is a streamer plugin written in Java.


3)Yes, you can write your own plugin by extending the Plugin class. And this is a post about calling Java methods from Pawn.





I also have a question of my own. Where is the class SampNativeFunction located? I can see it refferenced in shoebill-runtime but it's never defined.
Reply

@valych:

3) Yes, you can also access Java methods from Pawn. Please look at this post: http://forum.sa-mp.com/showpost.php?...&postcount=371

@dusk: The SampNativeFunction class is located in shoebill-launcher and should not be used. This is internal code only.
Reply

@dusk, thanks a lot
@mk124, thanks for reply. But what about the first question? The thing is, I have already written gamemode from scratch (by myself) in pawn and I dont have any desire to rewrite it again (also it will take a lot of time). I want to continue the development of the gamemode in java and sometimes will rewrite old pieces of code from pawn to java. How can I start the server with pawn and java gamemode in parallel?
Reply

Quote:
Originally Posted by mk124
Посмотреть сообщение
@valych:

@dusk: The SampNativeFunction class is located in shoebill-launcher and should not be used. This is internal code only.
I'm aware of it, just wanted to see it

1.Why doesn't the Player override Equals method? I don't even know what's the default comparator in Java. But I believe it is neccessary, I mean all collections use it, right?

2.What about timers. Are Java timers(util.timer) safe to use? AFAIK they run on their own thread. So if I start something from the main thread(I assume that its also Pawn thread) won't it interrupt it since it's not calling the runOnSampThread method?

I'll probably stick to Shoebill timers, I just find it interesting and would love to hear the answer.

3. What happens if I dispatch an event from a different thread?


Quote:
Originally Posted by valych
Посмотреть сообщение
@dusk, thanks a lot
@mk124, thanks for reply. But what about the first question? The thing is, I have already written gamemode from scratch (by myself) in pawn and I dont have any desire to rewrite it again (also it will take a lot of time). I want to continue the development of the gamemode in java and sometimes will rewrite old pieces of code from pawn to java. How can I start the server with pawn and java gamemode in parallel?
Your post actually gave me a great idea because that's exactly what I want myself. I did some testing and yes, they can exist with each other.

Instead of using the shoebill gamemode (bare.amx) use your own. Just add the shoebill filterscript(base.amx) to server.cfg. That way, the Java side of your gamemode will still work(I believe both the FS and GM do the same). Just be careful about callbacks such as OnDialogResponse in your Pawn code, if you return something other then 0 there, that means it won't be passed to other scripts, i.e. the shoebill FS and that means you won't be able to use it in you Java code.








In other news, I can't make the Pawn function calling from Java work.

My code:
Код:
AmxCallable getPos = getNativeMethod("AFunction");
int value  =(Integer)getPos.call();
Where getNativeMethod is a util method:
Код:
    public static AmxCallable getNativeMethod(String name) {
        AmxCallable nativeMethod = null;
        for(AmxInstance instance : Shoebill.get().getAmxInstanceManager().getAmxInstances()) {
            if((nativeMethod = instance.getNative(name)) != null)
                break;
        }
        return nativeMethod;
    }
The called function in pawn looks like this:

Код:
forward AFunction();
public AFunction()
{
	return 1;
}
The output? The server crashes, here's the dump: http://pastebin.com/gVp1SEig

Crash update: seems to be fixed by using AmxInstances getPublic instead of getNative method.
Reply

Great job!
Very useful since I am learning Java, I guess I will learn a lot with this plugin, although it seems I will be dealing with a lot of AMX crashes
Keep up the good work!
Reply

Another issue to report. CallShoebillFunction crashes when the Java implementation returns a boolean. I also registered an issue on github, let me know which do you prefer, bug reports here or on github.
Reply

@Su37Erich: Hi, thanks for you feedback. You will not have to deal with any AMX-Crashes when you use java only, but there are some problem when using AmxInterfaces. I will try to fix these as soon as possible.

@dusk:
1. If you want to compare Player, you should use ==. This will give you an unique result.
2. Yes, you should use Shoebill's timers, because they run on the samp thread. You can use Java's timers, but you will need to use Shoebill.get().runOnSampThread() or the server might crash.
3. Nothing special will happen, except that the Event will not be dispatched on the main thread which can be problematic (see 2.)

(If you want to find functions in Pawn, you'll need to use getPublic(), getNative() is for plugin and internal functions only )

I replied you on Github, because I like it way more.
Reply


Forum Jump:


Users browsing this thread: 30 Guest(s)